d3 forceLink 链接力详解:用弹簧模型稳定力导向图布局(d3 v7) d3 forceLink 链接力详解用弹簧模型稳定力导向图布局d3 v7【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3本文围绕 d3 官方文档 link.md 讲解 d3-force 模块中最核心的布局力——链接力link force它如何用弹簧模型把相连节点拉向目标距离以及forceLink的每个配置方法links、id、distance、strength、iterations的完整参数语义、默认值与可变性mutability陷阱。读完本文你将能在 d3v7.9.0依赖d3-force ^3.0.0中正确地为网络图、层级图与晶格结构配置链接力并理解参数何时重新计算、何时不重新计算。链接力是什么一条有目标长度的弹簧d3-force 模块基于 velocity Verlet 数值积分器模拟作用在节点粒子上的物理力可用于可视化网络、层级结构以及解决碰撞气泡图等场景模块总览见 d3-force.md。链接力link force是该模块中负责“结构”的力链接力按期望的链接距离link distance把相连节点拉近或推远力的大小与两节点当前距离和目标距离的差值成正比类似弹簧力。也就是说每条 link 都等价于一根弹簧节点间距偏离distance时产生恢复力偏离越大力越大。这决定了链接力本身不是“把图撑开”的力——撑开由 many-body 斥力 完成链接力负责让“该连着”的节点维持在一个合理间距上。两者配合才能形成稳定的力导向布局。forceLink(links)创建链接力创建方式默认参数const link d3.forceLink(links).id((d) d.id);forceLink(links)用指定的links数组和默认参数创建一个新链接力若不提供links默认为空数组[]注意该函数是“不纯”的impure它会修改mutate传入的links数组详见下文link.links一节。链接力最终通过simulation.force(name, force)挂到仿真器上仿真器每步 tick 时以新的alpha调用它完整机制见 simulation.forceconst simulation d3.forceSimulation(nodes) .force(charge, d3.forceManyBody()) .force(link, d3.forceLink(links)) .force(center, d3.forceCenter());link.links(links)设置并解析 link 数组如果提供了links则设置该力关联的链接数组并为每条 link重新计算distance和strength参数返回该力如果不提供返回当前的 links 数组默认空数组。每条 link 是如下形状的对象source— link 的源节点见 simulation.nodestarget— link 的目标节点index— 在links数组中的从零起始下标由该方法自动赋值为了方便link 的source/target可以用数字或字符串标识符初始化而不是对象引用配合link.id使用。可变性警告原文档明确标注该函数是不纯的。链接力被初始化或重新初始化例如节点或链接发生变化时时它可能修改传入的links。任何不是对象的link.source或link.target都会被替换为对应node的对象引用。这意味着初始化后你的原始 JSON 里的source: Alice会变成指向节点对象本身——这也是为什么很多代码在tick回调里可以直接写d.source.x、d.target.y。另外一个重要约定如果links数组本身被修改了向仿真中增删链接必须再次调用link.links(新数组)通知该力力不会为指定数组做防御性拷贝。nodes的修改同理见 simulation.nodes。link.id(id)节点标识符访问器如果提供了id把节点 id 访问器设置为指定函数并返回该力否则返回当前的 id 访问器默认为返回数字node.indexfunction id(d) { return d.index; }默认的 id 访问器允许每条 link 的source和target用nodes 数组的从零起始下标表示。例如const nodes [ {id: Alice}, {id: Bob}, {id: Carol} ]; const links [ {source: 0, target: 1}, // Alice → Bob {source: 1, target: 2} // Bob → Carol ];再换一个返回字符串的 id 访问器function id(d) { return d.id; }此时就可以用命名的 source 和 targetconst nodes [ {id: Alice}, {id: Bob}, {id: Carol} ]; const links [ {source: Alice, target: Bob}, {source: Bob, target: Carol} ];这一点在用 JSON 表示图时尤其有用因为 JSON 不允许对象引用这也是为什么网络图数据通常直接以这种nodeslinks的扁平结构存储。id 访问器在力初始化时例如 nodes 或 links 变化时对每个节点被调用一次参数为节点对象及其从零起始下标。link.distance(distance)目标距离访问器如果提供了distance把距离访问器设置为指定数字或函数对每条 link 重新求值距离访问器并返回该力否则返回当前距离访问器默认为function distance() { return 30; }距离访问器对每条 link 被调用参数为link及其从零起始index。求出的数字会被保存在内部——因此每条 link 的距离只在力被初始化、或调用该方法传入新distance时重新计算而不是在每次施加力时重新计算。这是一个值得记住的性能与语义细节写distance(30)之后tick 过程中不会重复求值。link.strength(strength)弹簧强度的默认公式如果提供了strength把强度访问器设置为指定数字或函数对每条 link 重新求值并返回该力否则返回当前强度访问器默认为function strength(link) { return 1 / Math.min(count(link.source), count(link.target)); }其中count(node)返回以该节点作为 source 或 target 的链接数。这个默认公式的动机是自动降低连到高度数节点hub的链接强度从而提升整体稳定性——枢纽节点连接的每条边都弱一些避免它被周围大量弱约束拉扯得剧烈抖动。强度访问器同样对每条 link 被调用一次参数为link与index结果保存在内部只在力初始化或传入新strength时重新计算不会在每次施加力时重复求值。link.iterations(iterations)迭代次数与结构刚性如果提供了iterations把每次施加时的迭代次数设置为指定数字并返回该力否则返回当前迭代次数默认为1。增加迭代次数会大幅提高约束的刚性rigidity对晶格lattice等复杂结构非常有用但同时也成倍增加每次施加力时的求值成本。这个取舍在文档仓库自带的可交互示例 ExampleLinkForce.vue 中体现得很直接——它构造了一个 20×20 的节点晶格横向、纵向相邻节点之间共生成 760 条链接然后配置simulation d3.forceSimulation(nodes) .force(charge, d3.forceManyBody().strength(-40)) .force(link, d3.forceLink(links).strength(1).distance(10).iterations(10)) .force(x, d3.forceX()) .force(y, d3.forceY()) .on(tick, ticked);可以看到这个示例同时动用了三个链接力参数strength(1)覆盖默认的按度数倒数公式、distance(10)把目标边长压到 10px、iterations(10)把单次施加的迭代提到 10 倍以“锁死”网格结构。这正是文档所说“迭代数提高刚性”的典型用法默认 1 次迭代的链接力更像“软弹簧”而 10 次迭代会让相邻节点距离非常接近设定值即使把某个节点拖到很远的地方再松手网格也能迅速弹回整齐的形态示例中还配合了d3.drag()simulation.find()实现拖拽拖拽期间用alphaTarget(0.3).restart()重新加热仿真。与仿真器的协作方式链接力不是独立运行的它遵循 d3-force 的统一力协议见 Custom forces 一节通过 simulation.force 绑定或 simulation.nodes 改变节点时力的initialize(nodes)被调用——id 访问器在此对每个节点求值source/target 的字符串/数字标识符在此被替换为节点对象引用distance与strength的缓存值也在此计算仿真器每步 tick 时以当前alpha调用该力链接力读取每条 link 两端节点的当前位置按“距离偏差 × 强度 × alpha”的比例修改两端节点的速度。因为alpha会随仿真冷却逐渐衰减见 simulation.alpha链接力施加的力也随之减弱布局在大约 300 步默认alphaDecay下后趋于静止。这也是为什么交互拖拽时要重新设置alphaTarget来“加热”仿真。实践要点小结结合 link.md 与仓库示例可以归纳出以下要点数据是扁平 JSON 结构nodes为对象数组links中source/target用 id 或下标通过.id(d d.id)解析——这是从数据到图布局的标准路径两个缓存distance和strength求值结果都缓存在力内部只在初始化或重新调用访问器方法时刷新调整布局参数后必要时需重新调用对应方法或重新links()/nodes()触发 re-initialize不纯函数forceLink(links)和link.links(...)都会原地修改你传入的 links 数组source/target 被替换为对象引用、写入 index不要假设原始数据保持不变默认 strength 自带防抖1 / Math.min(deg(source), deg(target))自动削弱 hub 节点上的边普通网络图通常无需自定义iterations 是刚性旋钮默认 1需要晶格、分子结构等强几何约束时提高到 5~10代价是求值成本线性增长参见 ExampleLinkForce.vue 中iterations(10)的用法动态增删节点/链接修改数组后必须重新调用 simulation.nodes /link.links()力不做防御性拷贝。在 d3 v7 中d3.forceLink由d3-force ^3.0.0提供并通过 src/index.js 的export * from d3-force统一导出安装d3包后即可直接使用d3.forceLink(...)本仓库中所有 d3-force 相关文档页面simulation、center、collide、many-body、position均可作为配套参考围绕链接力构建完整的力导向布局。【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考