AI智能
改变未来

d3js 各种力的仿真


d3js 各种力的仿真

d3js 是一种可以自由组合图形的库

我的理解,简单图形,比如柱形图,饼状图,条形图,要求不高可以直接用 echarts,自由度需要高些的使用 d3js,需要频繁交互的使用 pixi

偶然发现 d3js 可以进行各种力的模拟,学习后进行分析,希望大家能点个赞,让我有更多动力。点赞过十,下星期分享一篇 d3js 入门教程

效果如下

1. 创建数据

首先创建两组 JSON 数据,这里我使用的是网上的数据。一组数据记录了每个点的信息,另一组数据记录了连接信息。

// 结点信息的文件 node_data.json[{"x": 30.5, "y": 100.7, "r": 4},...]// 连接信息的文件 edge_data.json[{"source": 0, "target": 98},...]

2. 代码结构

力的模拟分为四个步骤

  1. 加载数据
  2. 创建仿真变量
  3. 绑定数据并画出图形
  4. 在帧回调函数中更新图形
// step 1const nodes = await d3.json("/force/node_data.json");// step 2const sim = d3.forceSimulation(nodes).force(\'力的名字\', ...).on(\'tick\', tick_function);// step3const node = svg.selectAll("circle").data(nodes).enter().append(\'circle\');// step4tick_function(){// 更新图形}

3. 位置x和y的力

特定位置x,y有中拉力,将其他图形拉向自己,比如让特定位置为画框中心

const sim = d3.forceSimulation(nodes)// 指定位置的拉力.force(\'x\', d3.forceX(width/2)).force(\'y\', d3.forceY(height/2));

可以设置力的大小,力如果越大,那么拉力越强,越小越弱

const sim = d3.forceSimulation(nodes)// 指定位置的拉力.force(\'x\', d3.forceX(width/2).strength(0.06)).force(\'y\', d3.forceY(height/2).strength(0.06))

4. 碰撞力

如果不想让所有图形重合,那么就需要使用碰撞力,碰撞力以坐标为圆心,设置碰撞半径,碰撞半径内不会发生重合。比如让位置力和碰撞力结合

const sim = d3.forceSimulation(nodes)// 指定位置的拉力.force(\'x\', d3.forceX(width/2).strength(0.06)).force(\'y\', d3.forceY(height/2).strength(0.06))// 碰撞力, radius(碰撞半径).force(\'collide\', d3.forceCollide().radius(10))

我们会发现有一些图形会有重合部分,这是因为我们设置了固定半径,有些图形的半径要大于固定半径,因此我们需要动态设置半径

const sim = d3.forceSimulation(nodes)// 指定位置的拉力.force(\'x\', d3.forceX(width/2).strength(0.06)).force(\'y\', d3.forceY(height/2).strength(0.06))// 碰撞力, radius(碰撞半径).force(\'collide\', d3.forceCollide().radius(d=>d.r+1))

5. 原子力

这里是我自己的叫法,实际上这个力叫 many-body force,表示每个图形自身存在对其他图形的力,这个力如果是正数,代表了吸引力,如果这个力是负数,代表了排斥力

const sim = d3.forceSimulation(nodes)// 原子力,正数吸引力.force(\'charge\', d3.forceManyBody().strength(7))

看下排除力

const sim = d3.forceSimulation(nodes)// 原子力,正数吸引力.force(\'charge\', d3.forceManyBody().strength(-7))

当原子力为吸引力时,所有图形聚合起来了,不好看,加上碰撞力再看看效果

const sim = d3.forceSimulation(nodes)// 碰撞力, radius(碰撞半径).force(\'collide\', d3.forceCollide().radius(d=>d.r+1))// 原子力,正数吸引力.force(\'charge\', d3.forceManyBody().strength(7))

看起来效果和位置力类似,但是核心内容完全不同,原子力代表每个图形自身都有对其他图形的力,而位置力是一个点对其他图形的吸引力

6. 链接力

表示连接线产生的力,类似于弹簧,远了就拉回来,近了就推出去

const sim = d3.forceSimulation(nodes)// 链接力.force(\'link\', d3.forceLink(edges))

还是给链接力加个碰撞力,再加上刚刚了解的原子力看看效果

const sim = d3.forceSimulation(nodes).force(\'collide\', d3.forceCollide().radius(d=>d.r+1))// 原子力.force(\'charge\', d3.forceManyBody().strength(-7))// 链接力.force(\'link\', d3.forceLink(edges))

有点鲜花盛开的感觉是不

7. 径向力

我们可以定义一个圆,这个圆上的每一点都会有个从圆心指向该点的力,会让所有图形集中再圆上

const sim = d3.forceSimulation(nodes)// 径向力.force(\'radial\', d3.forceRadial(240, width/2, height/2))

径向力让所有图形在一个圆上并且图形直接由重合,不太美观,加其他力试试

const sim = d3.forceSimulation(nodes)// 碰撞力.force(\'collide\', d3.forceCollide().radius(d=>d.r+1))// 原子力.force(\'charge\', d3.forceManyBody().strength(-7))// 径向力// .force(\'radial\', d3.forceRadial(240, width/2, height/2)).force(\'radial\', d3.forceRadial(d => d.r+200, width/2, height/2))

将径向力半径改为动态生成,加入碰撞力和原子力后是不是就好看多了

8. 中心力

中心力也是定义了一个圆,这个力有让图形移动到该圆内的趋势,这个力要与其他力配合使用

const sim = d3.forceSimulation(nodes)// 碰撞力.force(\'collide\', d3.forceCollide().radius(d=>d.r+1))// 原子力.force(\'charge\', d3.forceManyBody().strength(7))// 中心力,规划.force(\'center\', d3.forceCenter(centerForce.x, centerForce.y))

9. 代码解析

<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><title>Title</title></head><body><script src="/lib/d3.v7.min.js"></script><script type="module">const width=1000, height = 500;const svg = d3.select("body").append("svg").attr(\'width\', width).attr(\'height\', height);// 选取颜色色系const color = d3.scaleOrdinal(d3.schemeTableau10)async function load(){// 加载数据const nodes = await d3.json("/force/node_data.json");const edges = await d3.json("/force/edge_data.json");// 绑定数据并形成圆const node = svg.selectAll("circle").data(nodes).enter().append(\'circle\').attr("cx", d=>d.x).attr("cy", d=>d.y).attr("r", d=>d.r).attr("fill", (d, i)=>color(i));// 绑定数据并形成线const lines = svg.selectAll(\'lines\').data(edges).enter().append(\'line\').attr(\'x1\',d=>nodes[d.source].x).attr(\'x2\',d=>nodes[d.target].x).attr(\'y1\',d=>nodes[d.source].y).attr(\'y2\',d=>nodes[d.target].y).attr(\'stroke\', "#5d5d66").attr(\'stroke-width\', 2)const centerForce = {x: 700,y: 250}// 创建中心力的圆svg.append(\'circle\').attr(\'cx\', centerForce.x).attr(\'cy\', centerForce.y).attr(\'r\', 100).attr(\'stroke\', \'#6b6f80\').attr(\'stroke-width\', 3).attr(\'fill\', "#00000000")// 力的模拟const sim = d3.forceSimulation(nodes)// 指定位置的拉力// .force(\'x\', d3.forceX(width/2))// .force(\'y\', d3.forceY(height/2))// .force(\'x\', d3.forceX(width/2).strength(0.06))// .force(\'y\', d3.forceY(height/2).strength(0.06))// 碰撞力// .force(\'collide\', d3.forceCollide().radius(10)).force(\'collide\', d3.forceCollide().radius(d=>d.r+1))// 原子力.force(\'charge\', d3.forceManyBody().strength(7))// 链接力// .force(\'link\', d3.forceLink(edges))// 径向力// .force(\'radial\', d3.forceRadial(240, width/2, height/2))// .force(\'radial\', d3.forceRadial(d => d.r+200, width/2, height/2))// 中心力,规划.force(\'center\', d3.forceCenter(centerForce.x, centerForce.y))// .stop()//  .tick(100);sim.on("tick", () => {// sim 直接改变了node.attr("cx", function(d){ return d.x;}).attr("cy", function(d){ return d.y;});lines.attr(\'x1\',d=>d.source.x).attr(\'y1\',d=>d.source.y).attr(\'x2\',d=>d.target.x).attr(\'y2\',d=>d.target.y);})}load()</script></body></html>

如果觉得不错的话,给个赞,有赞才能有动力呀,过十赞下星期写 d3js 入门教程

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » d3js 各种力的仿真