- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
导读
unpanify()是 cytoscape.js 集合(collection)API 中用于关闭元素平移穿透(passthrough panning)能力的方法。将某个元素设为不可平移(unpannable)后,用户在该元素上按下并拖拽时不再平移整个画布,而是可以执行框选(box selection)、拖拽等其他交互。本文以 unpanify.md 为核心,结合 switch-functions.mjs、load-listeners.mjs 与 collection-data.mjs 等源码与测试,完整讲解unpanify()的用法、底层实现、配套事件与实战场景。读完本文,你将能够熟练通过unpanify()/panify()精确控制图中任意元素的平移行为,并理解该状态与抓取、复合节点等交互的联动关系。
一、方法速览:语法、返回值与配套方法
1.1 基本语法
unpanify()作用于集合中的所有元素,无参数:
cy.$('#j').unpanify();- 作用对象:调用该方法的集合中的每个节点与边(collection 文档 说明集合 API 的调用会应用到集合内全部元素)。
- 返回值:返回调用自身的集合(
this),支持链式调用,例如cy.$('#j').unpanify().addClass('fixed')。 - 幂等性:若元素已经是不可平移状态,重复调用不会重复触发事件(见下文“事件”小节)。
1.2 与 panify() 的对照
unpanify()是panify()的逆操作。两者构成一组“开关”型 API,与lock()/unlock()、select()/unselect()、grabify()/ungrabify()、selectify()/unselectify()等并列,全部由 switch-functions.mjs 中同一套defineSwitchSet()工厂统一生成:
defineSwitchSet( { field: 'pannable', on: 'panify', off: 'unpanify' } );从源码结构看,panify/unpanify对应元素私有状态字段pannable(true/false)。配套的读取方法为ele.pannable(),返回集合首个元素的布尔状态。
二、什么是“pannable”:平移穿透机制
在深入unpanify()之前,需要先理解它控制的交互行为。文档 pannable.md 给出了权威定义:
A pannable element allows passthrough panning: The user can pan the graph when dragging on the element. Thus, a pannable element is necessarily ungrabbable.
- 平移穿透(passthrough panning):当用户在元素上按下并拖拽时,事件“穿透”该元素,直接平移整个图(viewport panning)。
- 不可抓取(ungrabbable):可平移元素必然是“不可抓取”的,否则拖拽语义会冲突。这一点在 switch-functions.mjs 中体现:
grabbable的读取被强制受ele.pannable()影响——一个元素只要pannable()为真,grabbable()就恒为false。
2.1 默认值
按 pannable.md 所述,cytoscape.js 的默认规则是:
- 边(edge)默认可平移(pannable);
- 节点(node)默认不可平移(unpannable)。
因此对大多数节点而言,unpanify()的效果是“保持默认状态”;而对边,unpanify()会将其从默认的可平移状态切换为不可平移,这是该 API 最常见的用途之一。
三、底层实现:defineSwitchFunction 的完整行为
unpanify()并非独立编写的函数,而是由 switch-functions.mjs 的defineSwitchFunction()工厂生成。理解工厂逻辑即可完整掌握unpanify()的语义:
- 批量遍历:遍历集合中每个元素(
this.length次循环),对每个元素判断状态是否发生变化。 - 状态变更检测:仅当
ele._private.pannable与目标值(false)不同时,才将该元素记入changedEles。 - 样式刷新:对发生变化的元素集合调用
changedColl.updateStyle()——状态变化可能导致依赖该状态的样式(如overlay、透明度等)需要重绘。 - 事件派发:对变化后的元素发出
unpanify事件。 - 链式返回:始终返回
this。
工厂函数同时支持两种事件注册快捷调用形式(即把unpanify()当作on('unpanify', ...)的简写):
// 形式一:事件 + 数据 + 处理器 eles.unpanify( data, handler ); // 形式二:仅事件处理器 eles.unpanify( handler );3.1 json() 中的联动
元素的状态还可通过ele.json()统一读写。在 src/collection/index.mjs 的checkSwitch机制中,pannable被列入开关字段清单:
checkSwitch( 'pannable', 'panify', 'unpanify' );这意味着:
ele.json({ pannable: true })等价于调用ele.panify();ele.json({ pannable: false })等价于调用ele.unpanify();- 读取
ele.json()时返回的 JSON 对象中包含pannable字段(见 src/collection/index.mjs)。
该联动行为有测试直接佐证:见 collection-data.mjs 中的sets pannable与sets unpannable两个用例,它们分别通过json({ pannable: true })/json({ pannable: false })切换状态,并断言触发了一次panify/unpanify事件。
四、事件:监听状态切换
调用unpanify()后,状态发生变化的元素会触发unpanify事件,可用来驱动业务逻辑(例如同步更新 UI 提示“该节点已锁定平移”):
cy.$('#j').on('unpanify', function(evt){ console.log('元素 #j 已切换为不可平移', evt.target.id()); }); cy.$('#j').unpanify();对应地,panify()触发panify事件。测试 collection-data.mjs 证实了事件只会在状态实际变化时触发一次(evts计数为 1),重复设置相同状态不会重复触发。
五、交互层面的实际效果
unpanify()的价值最终体现在渲染层交互中。核心证据在 load-listeners.mjs:
5.1 鼠标拖拽分支(第 700~739 行)
在mousemove处理器中,决定“是平移还是框选/拖拽”的关键条件是:
} else if( select[4] == 1 && (down == null || down.pannable()) ){即:只有按下的元素为 null(点在空白处)或pannable()为真时,才进入平移/框选判定分支。随后调用allowPanningPassthrough(down, downs)决定是否真正允许穿透平移(load-listeners.mjs):
- 若图含复合节点(compound nodes),且按下的元素可平移,则检查事件层级中的所有元素:只要其中存在“是父节点且不可平移”的元素,就拒绝穿透平移(
allowPassthrough = false); - 否则允许穿透。
5.2 复合节点下的特殊规则
allowPanningPassthrough的逻辑意味着一个重要的组合行为:不可平移的父节点会“挡住”穿透平移。即使你unpanify()了一个子节点,若其父节点仍为 pannable,行为可能仍受父节点约束;反之,对父节点执行unpanify()可有效阻止在其区域内开始的拖拽平移画布。这与pannable字段的“必然是 ungrabbable”约束(switch-functions.mjs)共同构成一套一致的交互优先级体系。
5.3 触屏分支
同一套判断也用于触屏事件(load-listeners.mjs),因此unpanify()在触屏与鼠标输入下行为一致。
六、实战示例
6.1 让边不再拖动画布
默认边可平移,若希望用户拖拽某条边时进行框选而非平移画布:
cy.$('#edge-ab').unpanify();6.2 锁定节点后同时禁止其平移穿透
结合lock()使用,彻底固定一个节点的交互:
cy.$('#j') .lock() // 禁止移动位置 .unpanify(); // 禁止在它上面拖拽平移画布6.3 条件化切换与事件联动
// 根据数据字段批量控制 cy.nodes('[type="background"]').unpanify(); cy.nodes('[type="decor"]').panify(); // 监听并反馈 cy.on('unpanify', 'node', function(evt){ console.log('节点被锁定平移:', evt.target.id()); });6.4 通过 json() 等价操作
cy.$('#j').json({ pannable: false }); // 等价于 unpanify()七、注意事项与最佳实践
- 默认值意识:节点默认不可平移、边默认可平移。若业务依赖“边可拖动平移画布”,不要对全部边调用
unpanify()而不做区分。 - 与 grabbable 的冲突:pannable 元素必然 ungrabbable;若后续调用
grabify()恢复抓取,需先unpanify(),否则grabbable()仍返回false(由 switch-functions.mjs 的 override 逻辑保证)。 - 复合节点层级:父节点的 pannable 状态会影响子区域内的穿透平移判定,设计交互时需整体考虑父—子层级,参考 load-listeners.mjs。
- 状态持久化:
unpanify()只修改运行时状态,不会写入元素data()。需要持久化时应结合json()导出(ele.json()会包含pannable字段),或自行在业务数据中记录。 - 链式与批量:方法返回集合本身,可安全链式调用;对大型集合批量调用时,只会在状态实际变化的元素上触发事件与样式更新,开销可控。
八、相关资源
- 方法文档:unpanify.md、panify.md
- 状态定义文档:pannable.md
- 集合 API 说明:collection.md
- 开关函数工厂实现:switch-functions.mjs
- json() 中 pannable 联动:collection/index.mjs
- 渲染层交互判定:load-listeners.mjs
- 单元测试:collection-data.mjs
- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
相关推荐
Cytoscape.js 元素移动指南:使用 eles.move() 在不动图的前提下重连边与重设父节点
Cytoscape.js 元素移动指南:使用 eles.move 在不动图的前提下重连边与重设父节点 导读 本文系统讲解 Cytoscape.js 集合方法 e
数据可视化Cytoscape.js 元素状态控制:`unselectify()` 让元素不可选的原理与实战指南
Cytoscape.js 元素状态控制: unselectify 让元素不可选的原理与实战指南 导读 在 Cytoscape.js 中,元素的选中(select
数据可视化cytoscape.js 元素 pannable 状态详解:平移透传(Passthrough Panning)的配置、切换与渲染器实现
cytoscape.js 元素 pannable 状态详解:平移透传(Passthrough Panning)的配置、切换与渲染器实现 pannable(可平移
数据可视化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考