ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

X6 框选(Selection)插件完全指南:启用、配置、事件与 API 详解

2026/9/17 12:42:01 拓冰建站 浏览量
X6 框选(Selection)插件完全指南:启用、配置、事件与 API 详解 X6 框选Selection插件完全指南启用、配置、事件与 API 详解【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6导读框选是图编辑类应用中最基础也最关键的交互能力之一用户需要点击选中节点、按住修饰键多选、在画布空白处拖出选框批量圈选并整体移动被选中的图形。本文以 AntV X6 的Selection插件为主线结合本仓库 Selection 插件源码 与 单元测试系统讲解如何启用选择与框选、如何配置多选与严格框选strict、如何设置修饰键与触发事件eventTypes、如何借助过滤器和附加内容增强选区以及完整的 Graph API 与事件体系。读完本文你将能独立配置出符合业务需求的框选交互并理解其底层实现原理。使用快速启用 Selection 插件Selection是 X6 的一个插件Plugin通过graph.use()挂载到图实例上。最简用法如下import { Graph, Selection } from antv/x6 const graph new Graph({ background: { color: #F2F7FA, }, }) graph.use( new Selection({ enabled: true, }), )从源码看插件在init()阶段创建内部实现SelectionImpl并在 index.ts 中完成事件监听与「框选 vs 画布平移」冲突的解析随后把所有事件转发到graph实例上setup()中通过this.trigger(name, args); this.graph.trigger(name, args)双重派发因此你既可以在graph上、也可以在插件实例上监听事件。仓库中的演示示例 site/src/tutorial/plugins/selection/index.tsx 给出了一套更完整的初始化配置this.graph.use( new Selection({ enabled: true, multiple: true, // 启用点击多选 rubberband: true, // 启用框选 movable: true, // 选中后可拖动 showNodeSelectionBox: true, // 显示节点选择框 }), )挂载后即可获得以下交互能力点击选中节点启用多选后按住Ctrl/Command再点击节点实现多选拖动选框selection box移动被选中的节点在画布空白位置按下鼠标左键拖动出选框批量框选节点开启严格框选strict模式后只有被选框完全包围的节点才会被选中。配置项详解Selection的配置项都定义在 SelectionImplCommonOptions 中默认值集中在 DefaultOptions。下表整理了文档中给出的全部配置项属性名类型默认值描述enabledbooleantrue是否启用 Selection 插件classNamestring-附加样式名用于定制样式multiplebooleantrue是否启用点击多选启用后按住ctrl或command键点击节点实现多选multipleSelectionModifiersModifierKey[ctrl, meta]设置点击多选配套的修饰键rubberbandbooleanfalse是否启用框选节点功能rubberNodebooleantrue框选时是否将节点纳入框选范围计算rubberEdgebooleanfalse框选时是否将边纳入框选范围计算modifiersModifierKey-设置框选配套的修饰键strictbooleanfalse选框是否需要完全包围节点时才选中节点movablebooleantrue拖动选框时框选的节点是否一起移动contentstring-设置附加显示的内容filterFilter-节点过滤器showNodeSelectionBoxbooleanfalse是否显示节点的选择框showEdgeSelectionBoxbooleanfalse是否显示边的选择框pointerEventsnone \| autoauto打开showNodeSelectionBox时选择框会盖在节点上方导致节点事件无法响应可配置为none解决eventTypesSelectionEventType[][leftMouseDown, mouseWheelDown]设置框选的触发事件类型movingRouterFallbackstring-批量拖拽时临时将与选中节点相连边的路由降级为指定路由拖拽停止空闲后自动恢复注意默认值有两处与直觉略有出入需要特别说明rubberband默认是false即默认只支持点击选择框选需显式开启rubberEdge默认是false源码中注释“next version will set to true”index.ts即默认框选不圈边需要同时选中边时请显式开启。Filter节点过滤器Filter的类型定义如下type Filter string[] | { id: string }[] | (this: Graph, cell: Cell) boolean三种形式分别对应string[]节点 shape 数组只有指定 shape 的节点/边才能被选中({ id: string })[]节点 ID 数组只有指定的节点/边才能被选中(this: Graph, cell: Cell) boolean函数过滤器返回true的节点/边才能被选中。过滤逻辑在 selection.ts 的filter()方法中实现数组形式按shape或id匹配函数形式通过FunctionExt.call(filter, this.graph, cell)以 graph 为this调用。测试 selection.spec.ts 覆盖了函数过滤、shape 数组过滤、ID 数组过滤与null过滤不过滤四种场景。一个典型场景是「排除 circle 节点」在演示示例中设置过滤器后圆形节点不会被选中。ModifierKey修饰键体系X6 中修饰键包括alt、ctrl、meta、shift、space五个类型定义如下type ModifierKey string | (alt | ctrl | meta | shift | space)[] | null设置修饰键后点击鼠标并同时按下修饰键即可触发相应的行为。支持单个如alt或多个如[alt, ctrl]数组形式的多个修饰键是「或」关系。如果需要更灵活的配置可以使用如下形式alt表示按下alt[alt, ctrl]表示按下alt或ctrlalt|ctrl表示按下alt或ctrlaltctrl表示同时按下alt和ctrlalt|ctrlshift表示同时按下alt和shift或者同时按下ctrl和shift。框选与画布拖拽平移的优先级这是一个高频踩坑点如果框选和画布拖拽平移的触发条件完全相同时——即相同事件类型eventTypes和相同修饰键modifiers——框选的优先级更高会禁用默认的画布拖拽平移如果触发条件不同则互不影响。该逻辑在 index.ts 的resolvePanningSelectionConflict()中实现插件初始化时比较 selection 与 panning 的eventTypes是否有交集、modifiers是否相等若同时满足则调用graph.panning.disablePanning()关闭平移。因此当需要「框选和拖拽画布同时开启」时修饰键就非常有用例如两者触发时机都是鼠标左键在画布空白位置按下leftMouseDown可以为框选设置modifiers: alt为画布平移设置不同的修饰键实现互不冲突。演示示例即演示了按住alt键在画布空白处按下鼠标左键并拖动选框来框选节点。eventTypes框选触发事件SelectionEventType的类型定义如下type SelectionEventType leftMouseDown | mouseWheelDown支持两种形式或它们的组合leftMouseDown按下鼠标左键移动进行拖拽mouseWheelDown按下鼠标滚轮中键进行拖拽。在源码allowBlankMouseDown()index.ts中左键对应e.button 0滚轮中键对应e.button 1触摸事件touchstart或pointerType touch则只响应leftMouseDown。演示示例把配置组合起来仓库中的完整演示位于 site/src/tutorial/plugins/selection/index.tsx它通过右侧设置面板动态切换各类配置onSettingChanged (options: State) { this.graph.toggleMultipleSelection(options.multiple) this.graph.toggleSelectionMovable(options.movable) this.graph.toggleRubberband(options.rubberband) this.graph.toggleStrictRubberband(options.strict) this.graph.setSelectionFilter(options.filter) this.graph.setRubberbandModifiers(options.modifiers as any) this.graph.setSelectionDisplayContent( options.content ? (selection) ${selection.length} node${selection.length 1 ? s : } selected. : null, ) }演示覆盖的可观察行为包括点击选中节点按住Ctrl/Command点击节点多选拖动选框移动节点在画布空白处按下鼠标左键拖动框选节点开启 strict 严格框选模式观察「部分相交不选中、完全包围才选中」的差异设置修饰键alt后按住alt在画布空白处按下左键拖动框选应用过滤器排除 circle 节点圆形节点无法被选中应用附加内容函数选中两个及以上节点时显示选中数量。其中「附加内容」通过content配置或setSelectionDisplayContentAPI实现。从源码updateContainer()selection.ts可以看到content既可以是纯字符串直接写入innerHTML也可以是一个接收(this: Graph, selection, contentElement)的函数返回值作为 HTML 渲染进选择容器。Graph API 详解Selection 插件通过 api.ts 以模块声明合并的方式向Graph原型注入了一套方法下面按功能分组说明。选区操作增删查改方法签名说明graph.select(...)select(cells: Cell \| string \| (Cell \| string)[]): this选中指定的节点/边。注意不会取消选中当前选区而是追加需要先清空请用resetSelection(...)。在非多选模式下multiple: false只会选中传入集合中的第一个单元graph.unselect(...)unselect(cells: Cell \| string \| (Cell \| string)[]): this取消选中指定的节点/边graph.isSelected(...)isSelected(cell: Cell \| string): boolean返回指定的节点/边是否被选中graph.resetSelection(...)resetSelection(cells?: Cell \| string \| (Cell \| string)[]): this先清空选区再选中提供的节点/边不传参数时等价于清空graph.getSelectedCells()getSelectedCells(): Cell[]获取选中的节点/边数组graph.getSelectedCellCount()getSelectedCellCount(): number获取选中单元数量源码扩展方法api.tsgraph.cleanSelection()cleanSelection(): this清空选区graph.isSelectionEmpty()isSelectionEmpty(): boolean返回选区是否为空注意select()内部会经过getCells()解析字符串参数会通过graph.getCellById()解析为单元不存在的 ID 会被过滤掉。测试覆盖了「选中不存在的 ID 不报错」「混合有效与无效单元」「重复选中同一单元」「取消选中未被选中的单元」等边界场景selection.spec.ts。选择能力开关方法签名说明graph.isSelectionEnabled()(): boolean返回是否启用选择能力graph.enableSelection()(): this启用选择能力graph.disableSelection()(): this禁用选择能力graph.toggleSelection(...)toggleSelection(enabled?: boolean): this切换选择启用状态enabled缺省时取反多选开关方法签名说明graph.isMultipleSelection()(): boolean返回是否启用多选graph.enableMultipleSelection()(): this启用多选graph.disableMultipleSelection()(): this禁用多选graph.toggleMultipleSelection(...)toggleMultipleSelection(multiple?: boolean): this切换多选启用状态multiple缺省时取反选区移动开关方法签名说明graph.isSelectionMovable()(): boolean返回选中节点/边是否可移动graph.enableSelectionMovable()(): this启用选中单元移动graph.disableSelectionMovable()(): this禁用选中单元移动graph.toggleSelectionMovable(...)toggleSelectionMovable(enabled?: boolean): this切换移动启用状态enabled缺省时取反框选开关方法签名说明graph.isRubberbandEnabled()(): boolean返回是否启用框选graph.enableRubberband()(): this启用框选graph.disableRubberband()(): this禁用框选graph.toggleRubberband(...)toggleRubberband(enabled?: boolean): this切换框选启用状态enabled缺省时取反严格框选开关方法签名说明graph.isStrictRubberband()(): boolean返回是否启用严格框选graph.enableStrictRubberband()(): this启用严格框选只有节点/边被选框完全包围时才选中graph.disableStrictRubberband()(): this禁用严格框选选框与节点/边的包围盒相交即可选中graph.toggleStrictRubberband(...)toggleStrictRubberband(enabled?: boolean): this切换严格框选状态enabled缺省时取反严格框选与相交框选的差异在测试中被精确验证selection.spec.ts同样的矩形区域strict: false时会选中部分相交的节点strict: true时只选中被完全包含的节点。过滤、修饰键与附加内容graph.setSelectionFilter( filter?: | null | (string | { id: string })[] | ((this: Graph, cell: Cell) boolean), ): this graph.setRubberbandModifiers(modifiers?: string | ModifierKey[] | null): this graph.setSelectionDisplayContent( content?: | null | false | string | ((this: Graph, selection: Selection, contentElement: HTMLElement) string), ): thissetSelectionFilter设置选择的过滤条件满足条件的节点/边才能被选中。开启filter后rubberband 框选过程中box:mousemove和box:mouseup事件返回的nodes、edges也会应用相同的过滤规则因此预览结果与最终 selection 保持一致——这得益于getCellsInArea()在拿到区域内的单元后统一经过filter()处理selection.ts测试也验证了框选时过滤结果与最终选区一致selection.spec.ts。setRubberbandModifiers设置框选修饰键只有同时按下修饰键时才能触发框选。setSelectionDisplayContent设置选中单元旁的附加显示内容字符串或函数。事件体系Selection 插件触发两类事件选区变化事件与框选过程事件box 事件。由于setup()会把插件事件同时转发到 graph你可以用graph.on(...)监听。选区变化事件事件名称参数类型描述cell:selected{ cell: Cell; options: Model.SetOptions }节点/边被选中时触发node:selected{ node: Node; options: Model.SetOptions }节点被选中时触发edge:selected{ edge: Edge; options: Model.SetOptions }边被选中时触发cell:unselected{ cell: Cell; options: Model.SetOptions }节点/边被取消选中时触发node:unselected{ node: Node; options: Model.SetOptions }节点被取消选中时触发edge:unselected{ edge: Edge; options: Model.SetOptions }边被取消选中时触发selection:changed{ added: Cell[]; removed: Cell[]; selected: Cell[]; options: Model.SetOptions }选区发生增删变化时触发这些事件由onCollectionUpdated()统一派发selection.ts新增单元触发cell:selected按类型再派发node:selected/edge:selected移除单元触发cell:unselected及对应细分事件最后聚合selection:changed。测试中验证了三类事件的触发selection.spec.ts。box 过程事件事件名称参数类型描述box:mousedown{ e; view; cell; x; y; nodes; edges }按下 selection box或在画布空白处开始 rubberband 框选时触发box:mousemove{ e; view; cell; x; y; nodes; edges }拖动 selection box或 rubberband 框选过程中实时触发box:mouseup{ e; view; cell; x; y; nodes; edges }结束拖动 selection box或结束 rubberband 框选时触发box:*事件的参数说明x、y当前鼠标所在的图坐标经过snapToGrid吸附nodes、edges当前 box 对应的节点和边集合view、cell当前关联的视图和单元在 blank 上进行 rubberband 框选时可能为null。nodes和edges在不同交互阶段的含义在画布空白处开始或拖动 rubberband 框选时表示当前选框实时命中的节点和边strict和filter配置会影响该结果在拖动已有 selection box 时表示当前 selection 中的节点和边。事件参数由notifyBoxEvent()getBoxEventCells()组装selection.ts。监听示例graph.on(node:selected, ({ node }) { console.log(node) }) graph.on(box:mousemove, ({ nodes, edges, cell }) { console.log(nodes, edges, cell) }) // 也可以在插件实例上监听事件 selection.on(node:selected, ({ node }) { console.log(node) })源码级原理框选与拖拽的实现细节框选的核心流程SelectionImpl中框选流程是startSelecting → adjustSelection → stopSelecting源码注释在 selection.ts 标注了这条链路startSelecting()在blank:mousedown且通过allowRubberband()校验插件已启用 修饰键匹配后触发初始化选框容器尺寸为 1×1 并记录起点派发box:mousedownadjustSelection()跟随鼠标移动实时更新选框的left/top/width/height并通过getCellsInArea()计算命中单元派发box:mousemove如果配合了 Scroller 插件还会调用autoScrollGraph()在拖到边缘时自动滚动stopSelecting()鼠标松开后通过getCellsInArea()最终计算命中单元调用reset(cells, { batch: true })批量重置选区派发box:mouseup。框选命中计算在getCellViewsInArea()selection.ts当rubberNode为真时通过model.getNodesInArea(rect, { strict })获取节点当rubberEdge为真时通过model.getEdgesInArea(rect, { strict })获取边——strict正是通过这里传给 Model 的命中算法实现了「完全包围」与「相交即可」两种语义。批量拖拽移动与 movingRouterFallbackmovable: true时拖动选框会对选中单元执行批量移动。实现上有两个值得注意的优化点逐帧批处理位移拖拽产生的偏移先累积到dragPendingOffset再通过requestAnimationFrame在下一帧统一应用updateSelectedNodesPosition()selection.ts降低高频translate带来的重绘开销连边路由临时降级movingRouterFallback在拖拽大量节点、且边的原始路由为manhattan时尤其有用——applyMovingRouterFallback()selection.ts会把与选中节点相连的边临时降级为指定路由如orth拖拽停止并空闲后由scheduleMovingRouterRestoreThrottle()延迟恢复原路由避免连线抖动有效提升拖拽流畅性。测试对这两点都有覆盖批量拖拽时 manhattan 路由被临时降级为 orth、空闲 300ms 后恢复selection.spec.ts同时单节点拖拽不会触发降级selection.spec.ts因为降级只在选中节点数 ≥ 2 时生效。选择框Selection Box的渲染与性能开启showNodeSelectionBox/showEdgeSelectionBox后插件会为每个选中单元创建带data-cell-id的 box 元素并整体放在一个选择容器中。相关实现要点选中单元的 bbox 变化时通过updateSelectionBoxes()以约 16ms 的节流间隔刷新selection.ts缩放/平移画布时使用requestAnimationFrame将多次 transform 合并为每帧一次刷新onGraphTransformed()selection.ts测试验证了缩放过程中选择框位置、尺寸与视图几何保持同步selection.spec.ts拖拽「仅节点」时选择框走translate3d的 transform 预览模式draggingPreviewMode: translate避免频繁改几何当选区包含需要实时变形的边选择框时自动切换到geometry模式getDraggingPreviewMode()selection.ts。pointerEvents 的用途开启showNodeSelectionBox后选择框会盖在节点上方形成一层元素导致节点自身的事件无法响应。此时把pointerEvents配置为none即可让事件穿透选择框落到节点上。源码在创建 box 时通过getPointerEventsValue()selection.ts取值并支持函数形式(cells) none | auto按需动态决定。小结X6 的Selection插件以极低的接入成本提供了完整的选择交互能力enabled控制插件开关multiplemultipleSelectionModifiers控制点击多选rubberbandmodifierseventTypes控制框选触发strict控制框选命中语义filter控制可选中范围content与setSelectionDisplayContent展示附加信息showNodeSelectionBox/showEdgeSelectionBox/pointerEvents控制选择框的视觉与事件穿透movingRouterFallback则面向大图批量拖拽的性能优化。配合 Graph API 的十余个开关方法与box:*、selection:changed事件体系你可以按需组合出适合自己业务的框选交互也可以随时用代码动态切换能力而无需重新初始化画布。如需进一步探索可以阅读 Selection 插件源码、Graph API 注入 以及 插件单元测试 三个文件它们完整覆盖了本文介绍的全部行为与边界场景。【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考