- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
unlock()是 cytoscape.js 集合 API 中用于解除元素锁定的核心方法,调用后节点/边恢复可移动状态,可被拖拽、由布局重新定位。本文围绕 documentation/md/collection/unlock.md 给出的用法示例,结合源码 switch-functions.mjs、position.mjs 与相关测试,系统讲解unlock()的调用方式、底层实现、事件行为及其与lock()、locked()、:unlocked选择器、autolock全局配置的协作关系,读完即可在项目中熟练运用元素锁定/解锁机制。
一、unlock() 快速上手
原文档 unlock.md 给出的用法非常直观:
cy.$('#j').unlock();cy.$('#j')通过 ID 选择器#j获取单个元素的集合(collection),对其调用unlock()即可解除该元素的锁定状态。unlock()是集合级方法,作用于集合中的每一个元素,因此也支持批量解锁:
// 解锁所有节点 cy.nodes().unlock(); // 解锁满足自定义条件的元素 cy.$('node[weight > 10]').unlock(); // 解锁同时保留链式调用 cy.$('#j').unlock().addClass('moved');unlock()会返回调用它的集合本身(即返回this),因此可以继续链式调用其他集合方法。同时,它也会对集合中的元素批量生效,这与select()、grabify()等开关型方法的行为一致。
二、锁定三兄弟:lock() / unlock() / locked()
在 cytoscape.js 中,锁定状态是每个元素的内部布尔属性,由三组方法共同管理:
| 方法 | 作用 | 返回 |
|---|---|---|
ele.lock() | 锁定元素,使其无法被移动 | 集合自身(链式) |
ele.unlock() | 解锁元素,使其恢复可移动 | 集合自身(链式) |
ele.locked() | 查询元素当前是否处于锁定状态 | 布尔值 |
这三个方法并非各写各的实现,而是由 src/collection/switch-functions.mjs 中的工厂函数defineSwitchSet统一生成:
defineSwitchSet( { field: 'locked', overrideField: function( ele ){ return ele.cy().autolock() ? true : undefined; }, on: 'lock', off: 'unlock' } );从源码结构看,defineSwitchSet会做三件事:
- 生成读取方法
locked(),返回ele._private.locked; - 生成开方法
lock(),把_private.locked置为true; - 生成关方法
unlock(),把_private.locked置为false。
值得注意的是overrideField:如果图实例开启了autolock()(自动锁定),locked()会直接返回true,此时即使某个元素内部状态是解锁的,查询结果也表现为锁定——这是全局配置对个体状态的覆盖,详见下文第七节。
同一套defineSwitchSet机制还派生了grabify/ungrabify(grabbable)、select/unselect(selected)、selectify/unselectify(selectable)、activate/unactivate(active)、panify/unpanify(pannable)等成对方法,unlock()与它们共享同一套状态切换框架。
三、unlock() 的底层实现原理
unlock()实际执行的是defineSwitchFunction生成的函数,核心逻辑位于 src/collection/switch-functions.mjs。关键流程如下:
- 遍历集合:遍历集合中的每个元素;
- 能力检查:判断元素是否可执行该操作(对于
unlock,没有ableField限制,默认均可执行); - 状态变更检测:比较
ele._private.locked与目标值false,只记录真正发生变化的元素到changedEles; - 样式刷新:对状态发生变化的元素调用
changedColl.updateStyle(),因为锁定状态的改变可能影响样式计算; - 事件广播:通过
changedColl.emit('unlock')触发unlock事件,并且支持额外事件参数。
因此unlock()对已经处于解锁状态的元素是幂等的:状态没有变化,就不会触发unlock事件,也不会做无谓的样式刷新,这为高频调用提供了性能保障。
解锁与位置写入的关系
锁定状态最直接的影响体现在位置写入。在 src/collection/dimensions/position.mjs 中,position的定义显式声明了canSet校验:
canSet: function( ele ){ return !ele.locked(); }也就是说,被锁定的元素无法通过position()改变位置;unlock()之后,position()、silentPosition()以及拖动交互才对其生效。此外,beforePositionSet(position.mjs)在写入位置时还会同步位移复合节点(parent)的子节点,解锁父节点后移动它会带动整棵子树,这是布局交互中容易忽略但很实用的行为。
四、解锁后元素恢复了哪些能力
unlock()解除锁定后,元素在以下场景中恢复"可移动"权限:
- 程序化定位:
ele.position()、ele.silentPosition()可以正常写入新坐标; - 用户拖拽:渲染层的交互监听在判定是否启动拖拽时会检查
!ele.locked()(见 src/extensions/renderer/base/load-listeners.mjs),解锁后的节点可被鼠标/触控拖拽,且拖拽过程中lock状态会参与grabbed状态判定(load-listeners.mjs); - 布局算法重排:多个内置布局会跳过锁定节点。例如网格布局 grid.mjs、预设布局 preset.mjs 都会判断
element.locked();CoSE 力导向布局也会在初始化时记录isLocked(cose.mjs),解锁后这些布局才会重新计算节点位置; - 动画位移:核心动画步进在
endPos && isEles && !self.locked()条件下才会应用位置插值(src/core/animation/step.mjs),测试 collection-style.mjs 也验证了"动画不会移动锁定节点",解锁后节点方可随动画移动。
五、监听 unlock 事件
unlock()每次让元素从锁定变为解锁状态时,都会在该元素(集合)上触发unlock事件。可以这样监听:
cy.$('#j').on('unlock', function( evt ){ console.log('元素 #j 已解锁', evt.target); }); cy.$('#j').unlock();事件触发与集合上下文相关:在 defineSwitchFunction 中,事件是在"状态发生变化的元素子集"(changedColl)上广播的,因此通过evt.target可以拿到实际解锁的元素。这一行为有测试兜底,见 test/events.mjs:
it('`unlock`', function(){ n1.on('unlock', handler); n1.lock(); // make sure it's already locked n1.unlock(); });测试先确保元素处于锁定状态,再调用unlock()验证事件被触发,与源码"仅对 changed 元素发事件"的逻辑一致。
六、通过 json() 读写锁定状态
unlock()并不是操作锁定状态的唯一入口。在 src/collection/index.mjs 的ele.json()写入逻辑中,存在开关映射:
checkSwitch( 'locked', 'lock', 'unlock' );这意味着传入json({ locked: false })等价于调用unlock(),传入json({ locked: true })等价于lock()。反过来,不带参数调用json()时,输出的 JSON 中会包含locked字段(index.mjs),便于序列化与状态恢复。
相关测试见 test/collection-data.mjs,分别验证了json({ locked: true })触发lock事件、json({ locked: false })触发unlock事件。
七、与 autolock 全局配置的交互
在defineSwitchSet的overrideField中,locked()的读取会被全局开关覆盖:
overrideField: function( ele ){ return ele.cy().autolock() ? true : undefined; }即:当图实例启用了autolock(初始化选项autolock: true或运行时调用cy.autolock(true),别名cy.autolockNodes(true),见 src/core/viewport.mjs 与 viewport.mjs)时,所有节点都表现为锁定状态,即使对单个元素调用unlock()也无法恢复其可移动性。这与autoungrabify、autounselectify的机制一脉相承。
因此在实际项目中要注意作用域:
- 想锁定个别元素 → 用
ele.lock()/ele.unlock(); - 想全局锁定所有节点 → 用
autolock; - 二者同时存在时,全局
autolock优先,unlock()不生效(查询层面始终返回true)。
八、结合选择器筛选解锁元素
锁定状态可以直接作为状态选择器使用(见 src/selector/state.mjs):
:locked— 匹配当前处于锁定状态的元素;:unlocked— 匹配当前未锁定的元素。
// 解锁所有当前被锁定的节点 cy.nodes(':locked').unlock(); // 统计当前可自由移动的节点数 cy.nodes(':unlocked').length;选择器测试见 test/selectors.mjs,它先对nparent调用lock(),再断言:locked与:unlocked的筛选结果,验证了状态选择器与lock/unlock的联动。
九、综合实战示例
结合以上所有知识点,一个完整的"条件解锁 + 事件响应"场景如下:
const cy = cytoscape({ container: document.getElementById('cy'), elements: [ { data: { id: 'a' } }, { data: { id: 'b' } }, { data: { id: 'c' } } ], layout: { name: 'grid' } }); // 初始全部锁定 cy.nodes().lock(); // 解锁 id 为 a 和 c 的节点,并监听事件 cy.$('#a, #c').on('unlock', function( evt ){ console.log('已解锁:', evt.target.id()); }).unlock(); // 锁定状态下 position() 写入无效 cy.$('#a').position({ x: 100, y: 100 }); // 无效(仍锁定前的旧位置) // 显式解锁后 position() 生效 cy.$('#a').unlock().position({ x: 100, y: 100 }); // 查询状态 console.log(cy.$('#b').locked()); // true console.log(cy.nodes(':unlocked').length); // 2注意:lock()的overrideField与unlock()相同,只要cy.autolock()为真,上面的unlock()在查询层面都不会改变locked()的结果——这是第七节强调的优先级问题,排查"为什么解锁无效"时应首先检查cy.autolock()与初始化配置。
小结
unlock()虽是一行 API,背后却串联了 cytoscape.js 的状态开关框架、位置写入校验、交互拖拽、布局算法、事件系统、JSON 序列化与状态选择器等多个模块。掌握它与lock()、locked()、autolock、:unlocked的关系,就能在"允许用户拖动哪些节点""哪些节点参与布局重排""状态如何序列化保存"等场景中精准控制图形的可交互性。
参考阅读
- API 文档入口:documentation/md/collection/unlock.md(配套 lock.md)
- 核心实现:src/collection/switch-functions.mjs
- 位置校验:src/collection/dimensions/position.mjs
- JSON 读写:src/collection/index.mjs
- 全局配置:src/core/viewport.mjs
- 测试用例:test/collection-position-and-dimensions.mjs、test/events.mjs、test/selectors.mjs、test/collection-data.mjs
- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
相关推荐
Cytoscape.js 元素数据管理完全指南:ele.data()、removeData() 与 scratch() 深度解析
Cytoscape.js 元素数据管理完全指南:ele.data 、removeData 与 scratch 深度解析 导读 在 Cytoscape.js 中,
数据可视化Cytoscape.js 元素查询与检索:深入解析 cy.$()、cy.filter() 与 cy.elements()
Cytoscape.js 元素查询与检索:深入解析 cy.$ 、cy.filter 与 cy.elements 导读 本文聚焦 Cytoscape.js 图库中
数据可视化Cytoscape.js 元素数值样式读取指南:深入理解 ele.numericStyle() 与 ele.numericStyleUnits()
Cytoscape.js 元素数值样式读取指南:深入理解 ele.numericStyle 与 ele.numericStyleUnits 导读 在 Cytos
数据可视化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考