Vega力导向图添加Pan & Zoom功能遇阻,求实现示例
在Vega力导向图中实现平移缩放(Pan & Zoom)功能
下面是添加了平移缩放功能的完整Vega配置代码,同时保留了原有的节点拖拽、力导向参数调节功能:
{ "$schema": "https://vega.github.io/schema/vega/v5.json", "description": "A node-link diagram with force-directed layout, depicting character co-occurrence in the novel Les Misérables, with pan & zoom support.", "width": 700, "height": 500, "padding": 0, "autosize": "none", "signals": [ { "name": "cx", "update": "width / 2" }, { "name": "cy", "update": "height / 2" }, { "name": "nodeRadius", "value": 8, "bind": {"input": "range", "min": 1, "max": 50, "step": 1} }, { "name": "nodeCharge", "value": -30, "bind": {"input": "range", "min":-100, "max": 10, "step": 1} }, { "name": "linkDistance", "value": 30, "bind": {"input": "range", "min": 5, "max": 100, "step": 1} }, { "name": "static", "value": true, "bind": {"input": "checkbox"} }, { "description": "State variable for active node fix status.", "name": "fix", "value": false, "on": [ { "events": "symbol:pointerout[!event.buttons], window:pointerup", "update": "false" }, { "events": "symbol:pointerover", "update": "fix || true" }, { "events": "[symbol:pointerdown, window:pointerup] > window:pointermove!", "update": "[(event.x - tx)/zoom - width/2 + cx, (event.y - ty)/zoom - height/2 + cy]", "force": true } ] }, { "description": "Graph node most recently interacted with.", "name": "node", "value": null, "on": [ { "events": "symbol:pointerover", "update": "fix === true ? item() : node" } ] }, { "description": "Flag to restart Force simulation upon data changes.", "name": "restart", "value": false, "on": [ {"events": {"signal": "fix"}, "update": "fix && fix.length"} ] }, // 新增平移缩放相关信号 { "name": "zoom", "value": 1, "on": [ {"events": {"type": "wheel", "consume": true}, "update": "clamp(zoom * pow(1.001, event.deltaY), 0.1, 5)"} ] }, { "name": "tx", "value": 0, "on": [ {"events": "mousedown[event.button === 1] > mousemove!", "update": "tx + event.deltaX"}, {"events": "drag", "update": "tx + event.deltaX / zoom"} ] }, { "name": "ty", "value": 0, "on": [ {"events": "mousedown[event.button === 1] > mousemove!", "update": "ty + event.deltaY"}, {"events": "drag", "update": "ty + event.deltaY / zoom"} ] }, { "name": "active", "value": false, "on": [ {"events": "mousedown[event.button === 1]", "update": "true"}, {"events": "mouseup", "update": "false"} ] }, { "name": "drag", "value": null, "on": [ {"events": "mousedown[!event.buttons || event.button !== 1]", "update": "null"}, {"events": "active!==false > mousemove", "update": "event"} ] } ], "data": [ { "name": "node-data", "url": "data/miserables.json", "format": {"type": "json", "property": "nodes"} }, { "name": "link-data", "url": "data/miserables.json", "format": {"type": "json", "property": "links"} } ], "scales": [ { "name": "color", "type": "ordinal", "domain": {"data": "node-data", "field": "group"}, "range": {"scheme": "category20c"} } ], "marks": [ { "name": "nodes", "type": "symbol", "zindex": 1, "from": {"data": "node-data"}, "on": [ { "trigger": "fix", "modify": "node", "values": "fix === true ? {fx: node.x, fy: node.y} : {fx: fix[0], fy: fix[1]}" }, { "trigger": "!fix", "modify": "node", "values": "{fx: null, fy: null}" } ], "encode": { "enter": { "fill": {"scale": "color", "field": "group"}, "stroke": {"value": "white"} }, "update": { "size": {"signal": "2 * nodeRadius * nodeRadius"}, "cursor": {"value": "pointer"} } }, "transform": [ { "type": "force", "iterations": 300, "restart": {"signal": "restart"}, "static": {"signal": "static"}, "signal": "force", "forces": [ {"force": "center", "x": {"signal": "cx"}, "y": {"signal": "cy"}}, {"force": "collide", "radius": {"signal": "nodeRadius"}}, {"force": "nbody", "strength": {"signal": "nodeCharge"}}, {"force": "link", "links": "link-data", "distance": {"signal": "linkDistance"}} ] }, // 新增:将力导向计算的坐标应用缩放和平移 { "type": "scale", "x": {"signal": "zoom"}, "y": {"signal": "zoom"}, "originX": {"signal": "cx"}, "originY": {"signal": "cy"} }, { "type": "translate", "x": {"signal": "tx"}, "y": {"signal": "ty"} } ] }, { "type": "path", "from": {"data": "link-data"}, "interactive": false, "encode": { "update": { "stroke": {"value": "#ccc"}, "strokeWidth": {"value": 0.5} } }, "transform": [ { "type": "linkpath", "require": {"signal": "force"}, "shape": "line", "sourceX": "datum.source.x", "sourceY": "datum.source.y", "targetX": "datum.target.x", "targetY": "datum.target.y" }, // 新增:连线同步应用缩放和平移 { "type": "scale", "x": {"signal": "zoom"}, "y": {"signal": "zoom"}, "originX": {"signal": "cx"}, "originY": {"signal": "cy"} }, { "type": "translate", "x": {"signal": "tx"}, "y": {"signal": "ty"} } ] } ] }
核心修改说明:
- 平移缩放信号:新增
zoom(滚轮控制缩放,范围0.1-5)、tx/ty(鼠标中键拖拽控制平移),以及配套的交互状态信号 - 坐标变换:在节点和连线的transform链末尾添加缩放+平移变换,让力导向计算的原始坐标映射到画布的实际显示位置
- 拖拽坐标适配:修改节点拖拽时的坐标计算,将鼠标的画布坐标转换为缩放平移后的原始坐标系坐标,确保拖拽位置和视觉一致
- 交互隔离:滚轮事件设置
consume: true避免页面滚动冲突,鼠标中键专门用于平移,不干扰节点拖拽
内容的提问来源于stack exchange,提问作者Always Confused
相关产品推荐
相关产品推荐

