
本文旨在解决D3.js力导向图中无法拖拽整个图的问题。通过将拖拽功能替换为缩放功能,并禁用鼠标滚轮缩放,实现了对整个图的平移操作,同时保留了节点拖拽的功能。本文将提供详细的代码示例和实现步骤,帮助开发者在D3.js力导向图中实现类似效果。
问题分析
在使用D3.js构建力导向图时,经常需要实现缩放和平移功能。D3.js提供了d3.zoom()来实现缩放功能,但如果直接将拖拽功能应用于包含所有节点和连接线的元素,可能无法达到预期效果,即无法拖动整个图。
解决方案:利用缩放功能实现平移
D3.js的缩放功能实际上是通过改变元素的transform属性来实现的,这为我们提供了一个思路:可以使用缩放功能来实现平移效果。具体步骤如下:
创建新的缩放函数: 创建一个新的d3.zoom()函数,并将其绑定到SVG元素上。在这个缩放函数中,我们将修改包含所有节点和连接线的元素的transform属性。
const zoomSvg = d3.zoom().on('zoom', (event) => { group.attr('transform', event.transform).on('wheel.zoom', null);});
这里,group是包含所有节点和连接线的元素。event.transform包含了缩放和平移的信息。.on(‘wheel.zoom’, null)用于禁用鼠标滚轮缩放,防止与平移操作冲突。
ViiTor实时翻译
AI实时多语言翻译专家!强大的语音识别、AR翻译功能。
116 查看详情
将缩放函数绑定到SVG元素: 将新创建的缩放函数绑定到SVG元素上。
const svg = d3 .select(container) .append('svg') .attr('viewBox', [-width / 2, -height / 2, width, height]) .call(zoomSvg as any);
container是包含SVG元素的容器。.call(zoomSvg as any)将缩放函数绑定到SVG元素上。
完整代码示例
下面是完整的代码示例,展示了如何使用缩放功能实现力导向图的整体拖拽功能:
const data = jsonFyStory(selectedVariable, stories) const links = data.links.map((d) => d) const nodes = data.nodes.map((d: any) => d) const containerRect = container.getBoundingClientRect() const height = containerRect.height const width = containerRect.width function dragstarted() { // @ts-ignore d3.select(this).classed('fixing', true) setDisplayCta(false) setDisplayNodeDescription(false) setNodeData({}) } function dragged(event: DragEvent, d: any) { d.fx = event.x d.fy = event.y simulation.alpha(1).restart() setDisplayNodeDescription(true) d.class === 'story-node' && setDisplayCta(true) setNodeData({ name: d.name as string, class: d.class as string, definition: d.definition as string, summary: d.summary as string, }) } // dragended function in case we move away from sticky dragging! function dragended(event: DragEvent, d: DNode) { // @ts-ignore d3.select(this).classed('fixed', true) console.log(d) } function click(event: TouchEvent, d: DNode) { delete d.fx delete d.fy console.log(d) // @ts-ignore d3.select(this).classed('fixed', false) // @ts-ignore d3.select(this).classed('fixing', false) simulation.alpha(1).restart() } const simulation = d3 .forceSimulation(nodes as any[]) .force( 'link', d3.forceLink(links).id((d: any) => d.id) ) .force('charge', d3.forceManyBody().strength(isMobile ? -600 : -1300)) .force('collision', d3.forceCollide().radius(isMobile ? 5 : 20)) .force('x', d3.forceX()) .force('y', d3.forceY()) if (container.children) { d3.select(container).selectAll('*').remove() } const zoom = d3 .zoom() .on('zoom', (event) => { group.attr('transform', event.transform) }) .scaleExtent([0.2, 100]) const zoomSvg = d3.zoom().on('zoom', (event) => { group.attr('transform', event.transform).on('wheel.zoom', null) }) const svg = d3 .select(container) .append('svg') .attr('viewBox', [-width / 2, -height / 2, width, height]) .call(zoomSvg as any) const group = svg .append('g') .attr('width', '100%') .attr('height', '100%') .call( d3 .drag() .on('start', dragstarted) .on('drag', dragged as any) .on('end', dragended as any) as any ) // .call(zoom as any) const link = group .append('g') .attr('stroke', '#1e1e1e') .attr('stroke-opacity', 0.2) .selectAll('line') .data(links) .join('line') const node = group .append('g') .selectAll('g') .data(nodes) .join('g') .classed('node', true) .classed('fixed', (d: any) => d.fx !== undefined) .attr('class', (d: any) => d.class as string) .call( d3 .drag() .on('start', dragstarted) .on('drag', dragged as any) .on('end', dragended as any) as any ) .on('click', click as any) d3.selectAll('.category-node') .append('circle') .attr('fill', '#0083C5') .attr('r', isMobile ? 4 : 7) d3.selectAll('.tag-node') .append('circle') .attr('fill', '#FFC434') .attr('r', isMobile ? 4 : 7) d3.selectAll('.story-node') .append('foreignObject') .attr('height', isMobile ? 18 : 35) .attr('width', isMobile ? 18 : 35) .attr('x', isMobile ? -9 : -17) .attr('y', isMobile ? -18 : -30) .attr('r', isMobile ? 16 : 30) .append('xhtml:div') .attr('class', 'node-image') .append('xhtml:img') .attr('src', (d: any) => d.image) .attr('transform-origin', 'center') .attr('height', isMobile ? 18 : 35) .attr('width', isMobile ? 18 : 35) d3.selectAll('.main-story-node') .append('foreignObject') .attr('height', isMobile ? 50 : 100) .attr('width', isMobile ? 50 : 100) .attr('x', isMobile ? -25 : -50) .attr('y', isMobile ? -25 : -50) .attr('r', isMobile ? 50 : 100) .append('xhtml:div') .attr('class', 'node-image') .append('xhtml:img') .attr('src', (d: any) => d.image) .attr('transform-origin', 'center') .attr('height', isMobile ? 50 : 100) .attr('width', isMobile ? 50 : 100) node .append('foreignObject') .attr('height', (d: any) => (d.class === 'main-story-node' ? 65 : 55)) .attr('width', (d: any) => isMobile ? d.class === 'main-story-node' ? 80 : 50 : d.class === 'main-story-node' ? 120 : 70 ) .attr('x', (d: any) => isMobile ? d.class === 'main-story-node' ? -40 : -25 : d.class === 'main-story-node' ? -60 : -35 ) .attr('y', (d: any) => isMobile ? d.class === 'main-story-node' ? 32 : 7 : d.class === 'main-story-node' ? 60 : 12 ) .append('xhtml:p') .attr('class', (d: any) => d.class) .text((d: any) => d.name) simulation.on('tick', () => { link .attr('x1', (d: any) => d.source.x) .attr('y1', (d: any) => d.source.y) .attr('x2', (d: any) => d.target.x) .attr('y2', (d: any) => d.target.y) node .attr('cx', (d: any) => d.x as number) .attr('cy', (d: any) => d.y as number) .attr('transform', (d: any) => { return `translate(${d.x},${d.y})` }) }) function transition(zoomLevel: number) { group .transition() .delay(100) .duration(500) .call(zoom.scaleBy as any, zoomLevel) } transition(0.7) d3.selectAll('.zoom-button').on('click', function () { // @ts-ignore if (this && this.id === 'zoom-in') { transition(1.2) // increase on 0.2 each time } // @ts-ignore if (this.id === 'zoom-out') { transition(0.8) // deacrease on 0.2 each time } // @ts-ignore if (this.id === 'zoom-init') { group .transition() .delay(100) .duration(500) .call(zoom.scaleTo as any, 0.7) // return to initial state } })
注意事项
禁用鼠标滚轮缩放: 为了避免鼠标滚轮缩放与平移操作冲突,建议禁用鼠标滚轮缩放功能。性能优化: 对于大型力导向图,频繁的transform属性更新可能会影响性能。可以考虑使用requestAnimationFrame来优化性能。兼容性: 该解决方案基于D3.js的缩放功能,请确保你的D3.js版本支持该功能。
总结
通过利用D3.js的缩放功能,我们可以轻松实现力导向图的整体拖拽功能,同时保留节点拖拽的功能。这种方法简单有效,可以满足大多数应用场景的需求。在实际应用中,可以根据具体需求进行调整和优化,例如添加过渡效果、调整缩放比例等。
以上就是D3.js Force Directed Graph:实现整体拖拽功能的解决方案的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/211892.html
微信扫一扫
支付宝扫一扫