X6 MiniMap 插件实战指南:为图形画布添加可交互的小地图导航
2026/9/17 11:30:06 网站建设 项目流程

X6 MiniMap 插件实战指南:为图形画布添加可交互的小地图导航

【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6

MiniMap 是 X6 官方提供的一颗图编辑插件,它在主画布之外渲染一个缩略视图,让用户通过移动/缩放小地图视口即可快速定位与缩放主画布,非常适合大图、长流程编辑场景。阅读本文后,你将掌握 MiniMap 的接入方式、全部配置项含义,以及它在源码层的工作机制(内部 Graph 复用模型、视口比例换算、与 Scroller 的联动),并能直接复刻官方示例中的"简单视图/详细视图"切换能力。

MiniMap 是什么

MiniMap(小地图)插件为Graph实例提供一块独立的缩略图区域:它把当前画布中的所有节点、边渲染到一个更小的内部 Graph 中,并在其上叠加一个高亮"视口"(viewport)矩形,用于表示主画布当前可见区域。通过小地图,用户可以:

  • 拖动小地图视口来平移主画布;
  • 拖动视口右下角的缩放圆柄来缩放主画布(在scalable开启时)。

该功能在 X6 中由 src/plugin/minimap/index.ts 中的MiniMap类实现,类名name = 'minimap',实现了GraphPlugin接口,因此可以通过graph.use(...)安装,并由 src/plugin/index.ts 作为MiniMap统一导出。

基本使用

启用小地图非常简单:从@antv/x6导入MiniMap,实例化后通过graph.use()挂载到图上。官方教程给出的最小示例:

import { Graph, MiniMap } from '@antv/x6' const graph = new Graph({ background: { color: '#F2F7FA', }, }) graph.use( new MiniMap({ container: document.getElementById('minimap'), }), )

其中container是唯一的必选参数,用于指定小地图挂载的 DOM 容器。MiniMap 会在该容器内创建自己的 DOM 结构(容器根节点x6-widget-minimap、内部 Graph 区域与视口层),无需额外编写样式。

交互行为

官方演示(演示源码)展示了小地图的两类核心交互:

  • 移动小地图视口来移动画布:鼠标按下视口区域并拖拽时,主画布会同步平移(若安装了 Scroller 插件,则移动的是滚动容器的滚动位置)。
  • 缩放小地图视口来缩放画布:视口右下角有一个圆形缩放手柄,拖动它即可连续缩放主画布,缩放范围由minScalemaxScale约束。

在源码中,这两类交互由startActiondoActionstopAction这套事件流完成(见 src/plugin/minimap/index.ts):

  • mousedown/touchstart触发startAction,根据事件目标是视口还是缩放手柄,将action标记为panningzooming,并记录初始坐标、滚动位置、缩放值与视口几何信息;
  • 之后的mousemove/touchmove进入doActionpanning分支根据鼠标位移换算成主画布的平移量(或滚动容器的scrollLeft/scrollTop),zooming分支通过requestAnimationFrame按位移增量调用sourceGraph.zoom(delta, { absolute: true, minScale, maxScale })
  • mouseup/touchend触发stopAction,解绑文档级事件并结束本次操作。

另外,在小地图空白区域(内部 Graph 区域)直接点击,会触发scrollTo,将主画布中心移动到对应位置(src/plugin/minimap/index.ts)。

配置选项

MiniMap 支持以下配置项,下表完整来自官方文档,并已对照 src/plugin/minimap/type.ts 与 src/plugin/minimap/index.ts 中的DefaultOptions进行核实:

属性名类型默认值必选描述
containerHTMLElement-挂载小地图的容器
widthnumber300小地图的宽度
heightnumber200小地图的高度
paddingnumber10小地图容器的 padding 边距
scalablebooleantrue是否启用缩放(false 时隐藏缩放手柄)
minScalenumber0.01最小缩放比例
maxScalenumber16最大缩放比例
graphOptionsGraph.Options{}小地图内部 Graph 的选项
createGraph(options: Graph.Options) => Graphoptions => new Graph(options)自定义创建小地图内部 Graph 的方法

各选项的实际作用:

  • container:必填,小地图挂载的容器,源码中通过this.options.container.appendChild(this.container)插入;
  • width/height/padding:决定小地图根容器的尺寸与内边距,同时参与视口比例计算(maxWidth = width - 2 * paddingmaxHeight = height - 2 * padding,见updatePaper,src/plugin/minimap/index.ts);
  • scalable:为true时才会创建视口右下角的缩放圆柄(zoomHandle,src/plugin/minimap/index.ts);
  • minScale/maxScale:在doActionzooming分支中作为graph.zoom(..., { minScale, maxScale })的参数传入,用于限制缩放范围;
  • graphOptions:透传给内部小地图 Graph 的选项(见下文"内部实现");
  • createGraph:默认是options => new Graph(options),可替换为自定义 Graph 工厂方法(如接入自定义子类)。

内部实现:小地图如何工作

了解实现细节有助于在复杂场景下正确使用 MiniMap。以下是 src/plugin/minimap/index.ts 的核心机制:

内部 Graph 与模型共享

MiniMap在初始化时会创建一个内部目标 Graph(targetGraph),其关键点是与源 Graph 共享同一个 modelmodel: this.sourceGraph.model),因此主画布上的任何增删改都会自动反映在小地图中。同时,内部 Graph 会强制关闭大部分交互与辅助能力(见 src/plugin/minimap/index.ts):

const targetGraphOptions: Options = { ...this.options.graphOptions, container: graphContainer, model: this.sourceGraph.model, interacting: false, grid: false, background: false, embedding: false, panning: false, }

视口比例换算

updatePaper会根据源画布与容器的尺寸计算缩放比例:

ratio = Math.min(maxWidth / width, maxHeight / height)

即在小地图可用区域内等比放下整张画布,随后对内部 Graph 执行resizetranslatescale(有 Scroller 时直接scale(ratio, ratio),否则zoomToFit()),并在源画布resize时同步更新。这也解释了为什么小地图始终能看到整张图的缩略全貌。

事件联动

startListening(src/plugin/minimap/index.ts)按是否安装 Scroller 插件区分了监听策略:

  • 安装了 Scroller:直接监听滚动容器的scroll事件来更新视口位置;
  • 未安装 Scroller:监听源 Graph 的translatescale事件,以及model:updated事件(模型更新时对内部 Graph 执行zoomToFit)。

视口位置的更新(updateViewport)使用了FunctionExt.debounce(..., 0)防抖,避免高频事件导致布局抖动,并通过计算源画布可见区域在小地图坐标系中的几何信息(top/left/width/height)来设置视口 div 的 CSS。

视觉样式

小地图的外观由 src/plugin/minimap/index.less(运行时通过CssLoader.ensure('minimap', content)注入)控制,关键样式包括:

  • 根容器x6-widget-minimapoverflow: hidden、居中显示、白色背景;
  • 视口x6-widget-minimap-viewport2px solid #31d0c6青绿色边框、cursor: move,并通过margin: -2px 0 0 -2px抵消边框对外部布局的影响;
  • 缩放手柄x6-widget-minimap-viewport-zoom:12×12 的白色圆形圆柄,位于视口右下角,cursor: nwse-resize

进阶:自定义小地图视图(简单视图)

官方演示还展示了小地图的高级用法:通过graphOptions.createCellView自定义内部 Graph 对单元格的渲染方式,实现"简单视图 / 详细视图"切换(site/src/tutorial/plugins/minimap/index.tsx)。

this.graph.disposePlugins('minimap') // 先销毁已有小地图 this.graph.use( new MiniMap({ container: this.minimapContainer, width: 200, height: 160, padding: 10, graphOptions: { createCellView(cell) { // 返回三种类型数据: // 1. null: 不渲染该单元格 // 2. undefined: 使用 X6 默认渲染方式 // 3. CellView: 自定义渲染 if (cell.isEdge()) { return null // 不渲染边,小地图更干净 } if (cell.isNode()) { return SimpleNodeView // 自定义节点视图 } }, }, }), )

配套的SimpleNodeView(site/src/tutorial/plugins/minimap/simple-view.tsx)继承自NodeView,只渲染一个<rect>并以灰色填充,从而获得更"扁平"的缩略效果:

export class SimpleNodeView extends NodeView { protected renderMarkup() { return this.renderJSONMarkup({ tagName: 'rect', selector: 'body', }) } update() { super.update({ body: { refWidth: '100%', refHeight: '100%', fill: '#8f8f8f' }, }) } }

这种"简单视图"在节点数量极大的场景下能显著提升小地图的渲染与交互性能。

插件生命周期与 API

MiniMap 作为标准 Graph 插件,支持完整的生命周期管理(实现在 src/graph/graph.ts):

  • graph.use(new MiniMap({ ... })):安装插件,等价于调用plugin.init(graph, ...options)
  • graph.getPlugin('minimap'):获取 MiniMap 实例(注意插件名固定为'minimap');
  • graph.disposePlugins('minimap'):销毁插件,会调用MiniMap.dispose(),依次执行remove()(内部会stopListening()targetGraph.dispose(false))与CssLoader.clean('minimap')(src/plugin/minimap/index.ts)。

在官方演示中,"简单视图/详细视图"切换正是通过disposePlugins('minimap')后重新use(new MiniMap(...))实现的,这是动态调整小地图渲染策略的推荐做法。

测试与验证

仓库内置了 MiniMap 的完整测试(tests/plugin/minimap.spec.ts),覆盖三类典型场景:

  1. 基础场景:安装 MiniMap 后添加带端口的节点,验证实例类型与 DOM 快照(minimap-1.html/minimap-2.html),并验证addClass/removeClass方法;
  2. 配合 Scroller:同时安装TransformScroller,设置滚动位置、平移与缩放(graph.scale(0.1, 0.2))后校验快照(minimap-scroller.html);
  3. 仅配合 Transform:不安装 Scroller,仅做translatescale,校验快照(minimap-transform.html)。

这些测试印证了 MiniMap 在"有 Scroller / 无 Scroller"两种布局模式下都能正确同步画布状态,可以作为你集成小地图时的行为基准。

完整可运行示例

结合官方演示(site/src/tutorial/plugins/minimap/index.tsx),一个同时使用 Scroller 与 MiniMap 的完整示例:

import { Graph, MiniMap, Scroller } from '@antv/x6' const graph = new Graph({ container: document.getElementById('container'), width: 600, height: 320, panning: false, background: { color: '#F2F7FA' }, }) // 先启用滚动容器,让主画布可平移、可滚动 graph.use( new Scroller({ enabled: true, pageVisible: true, pageBreak: false, pannable: true, }), ) // 再挂载小地图 graph.use( new MiniMap({ container: document.getElementById('minimap'), width: 200, height: 160, padding: 10, }), ) // 添加节点与边 graph.addNode({ x: 200, y: 100, width: 100, height: 40, label: 'Rect', attrs: { body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 } }, }) const source = graph.addNode({ x: 32, y: 32, width: 100, height: 40, label: 'Hello', attrs: { body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 } }, }) const target = graph.addNode({ shape: 'circle', x: 160, y: 180, width: 60, height: 60, label: 'World', attrs: { body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff' } }, }) graph.addEdge({ source, target, attrs: { line: { stroke: '#8f8f8f', strokeWidth: 1 } }, })

运行后即可体验:拖动小地图视口平移画布,拖动视口右下角圆柄缩放画布,点击小地图任意位置将主画布中心定位到该处。建议在此基础上按需组合width/height/paddingminScale/maxScalegraphOptions.createCellView,为大图编辑场景打造流畅的导航体验。

【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询