D3v3气泡图升级至D3v4:力导向与加载动画修复需求
搞定D3v3到D3v4气泡图的力导向修复+点击涟漪效果
嘿,我帮你梳理下怎么把v3的气泡图升级到v4,同时解决力导向失效的问题,还要加上点击的涟漪效果。D3v4把力布局完全重写了,API变化特别大,这就是为啥原来的力导向不好使的原因,咱们一步步来:
一、先把力导向布局给救回来
D3v3的d3.layout.force()在v4里变成了d3.forceSimulation(),而且力的配置方式完全不一样了,咱们替换一下:
初始化力模拟
把原来v3的force初始化换成v4的simulation,还要把节点数据传进去,同时添加必要的力(电荷力、中心力、碰撞力,气泡图必须加碰撞力防止重叠):// 保存原始电荷强度,后面点击效果要用 const originalCharge = -50; // 创建力模拟 const simulation = d3.forceSimulation(nodes) .force("charge", d3.forceManyBody().strength(originalCharge)) // 电荷力,控制节点间的吸引/排斥 .force("center", d3.forceCenter(width/2, height/2)) // 让节点保持在画布中心 .force("collide", d3.forceCollide(d => d.radius * scale + 5)); // 碰撞力,防止气泡重叠监听tick事件更新节点位置
v3的force会自动更新节点位置,但v4需要你手动在tick事件里更新circle的cx和cy:simulation.on("tick", () => { nodes.attr("cx", d => d.x) .attr("cy", d => d.y); });替换拖拽功能
v3的.call(force.drag())在v4里换成d3.drag(),还要写三个拖拽事件函数来控制simulation的状态:function dragstarted(event, d) { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragended(event, d) { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; }然后在enter的circle上绑定拖拽:
.call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended))
二、保留初始加载的缩放动画
原来的enter/update/exit逻辑在v4里基本能用,稍微调整一下,确保动画和力模拟不冲突:
// Enter const nodesEnter = nodes.enter() .append("circle") .attr("class", "node") .attr("cx", d => d.x) .attr("cy", d => d.y) .attr("r", 10) // 初始小尺寸 .style("fill", (d,i) => color(i)) .call(d3.drag() // 绑定拖拽 .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)) .on("click", handleBubbleClick); // 绑定点击事件,后面讲 // Update nodesEnter.merge(nodes) .transition() .delay(300) .duration(1000) .attr("r", d => d.radius * scale); // Exit nodes.exit() .transition() .duration(250) .attr("r", 1) .remove();
三、添加点击气泡的涟漪式互斥/吸引效果
咱们实现点击某个气泡时,临时修改电荷力的强度,让所有节点产生涟漪式的排斥,然后再恢复原状:
- 定义点击事件处理函数
你可以调整function handleBubbleClick() { // 临时把电荷改成强排斥(正数是排斥,负数是吸引) simulation.force("charge").strength(100); // 重启simulation,让新的力生效 simulation.alpha(0.3).restart(); // 1秒后恢复原始电荷强度 setTimeout(() => { simulation.force("charge").strength(originalCharge); simulation.alpha(0.3).restart(); }, 1000); }100和1000这两个数值,来控制排斥强度和恢复时间,要是想要吸引效果,就把100改成负数比如-200。
最后整合起来的关键点
- D3v4的力模拟是声明式的,需要手动添加各种力,并且通过
tick事件更新节点位置 - 拖拽功能需要手动控制simulation的
alpha(模拟的活跃度),不然拖拽后节点不会回到自然状态 - 点击效果通过动态修改电荷力的强度,结合重启simulation来触发涟漪效果
内容的提问来源于stack exchange,提问作者The Old County
相关产品推荐
相关产品推荐

