UNPKG

@antv/g6

Version:

A Graph Visualization Framework in JavaScript

247 lines 9.89 kB
import { CommonEvent } from '../../constants'; import { Circle } from '../../elements'; import { idOf } from '../../utils/id'; import { parsePoint, toPointObject } from '../../utils/point'; import { positionOf } from '../../utils/position'; import { distance } from '../../utils/vector'; import { BasePlugin } from '../base-plugin'; const defaultLensStyle = { fill: '#fff', fillOpacity: 1, lineWidth: 1, stroke: '#000', strokeOpacity: 0.8, zIndex: -Infinity, }; const DELTA = 0.05; /** * <zh/> 边过滤镜插件 * * <en/> Edge filter lens plugin * @remarks * <zh/> 边过滤镜可以将关注的边保留在过滤镜范围内,其他边将在该范围内不显示。 * * <en/> EdgeFilterLens can keep the focused edges within the lens range, while other edges will not be displayed within that range. */ export class EdgeFilterLens extends BasePlugin { constructor(context, options) { super(context, Object.assign({}, EdgeFilterLens.defaultOptions, options)); this.shapes = new Map(); this.r = this.options.r; this.onEdgeFilter = (event) => { if (this.options.trigger === 'drag' && this.isLensOn) return; const origin = parsePoint(event.canvas); this.renderLens(origin); this.renderFocusElements(); }; this.renderLens = (origin) => { const style = Object.assign({}, defaultLensStyle, this.options.style); if (!this.isLensOn) { this.lens = new Circle({ style }); this.canvas.appendChild(this.lens); } Object.assign(style, toPointObject(origin), { size: this.r * 2 }); this.lens.update(style); }; this.getFilterData = () => { const { filter } = this.options; const { model } = this.context; const data = model.getData(); if (!filter) return data; const { nodes, edges, combos } = data; return { nodes: nodes.filter((node) => filter(idOf(node), 'node')), edges: edges.filter((edge) => filter(idOf(edge), 'edge')), combos: combos.filter((combo) => filter(idOf(combo), 'combo')), }; }; this.getFocusElements = (origin) => { const { nodes, edges } = this.getFilterData(); const focusNodes = nodes.filter((datum) => distance(positionOf(datum), origin) < this.r); const focusNodeIds = focusNodes.map((node) => idOf(node)); const focusEdges = edges.filter((datum) => { const { source, target } = datum; const isSourceFocus = focusNodeIds.includes(source); const isTargetFocus = focusNodeIds.includes(target); switch (this.options.nodeType) { case 'both': return isSourceFocus && isTargetFocus; case 'either': return isSourceFocus !== isTargetFocus; case 'source': return isSourceFocus && !isTargetFocus; case 'target': return !isSourceFocus && isTargetFocus; default: return false; } }); return { nodes: focusNodes, edges: focusEdges }; }; this.renderFocusElements = () => { const { element, graph } = this.context; if (!this.isLensOn) return; const origin = this.lens.getCenter(); const { nodes, edges } = this.getFocusElements(origin); const ids = new Set(); const iterate = (datum) => { const id = idOf(datum); ids.add(id); const shape = element.getElement(id); if (!shape) return; const cloneShape = this.shapes.get(id) || shape.cloneNode(); cloneShape.setPosition(shape.getPosition()); cloneShape.id = shape.id; if (!this.shapes.has(id)) { this.canvas.appendChild(cloneShape); this.shapes.set(id, cloneShape); } else { Object.entries(shape.attributes).forEach(([key, value]) => { if (cloneShape.style[key] !== value) cloneShape.style[key] = value; }); } const elementType = graph.getElementType(id); const style = this.getElementStyle(elementType, datum); // @ts-ignore cloneShape.update(style); }; nodes.forEach(iterate); edges.forEach(iterate); this.shapes.forEach((shape, id) => { if (!ids.has(id)) { shape.destroy(); this.shapes.delete(id); } }); }; this.scaleRByWheel = (event) => { var _a; if (this.options.preventDefault) event.preventDefault(); const { clientX, clientY, deltaX, deltaY } = event; const { graph, canvas } = this.context; const scaleOrigin = graph.getCanvasByClient([clientX, clientY]); const origin = (_a = this.lens) === null || _a === void 0 ? void 0 : _a.getCenter(); if (!this.isLensOn || distance(scaleOrigin, origin) > this.r) { return; } const { maxR, minR } = this.options; const ratio = deltaX + deltaY > 0 ? 1 / (1 - DELTA) : 1 - DELTA; const canvasR = Math.min(...canvas.getSize()) / 2; this.r = Math.max(minR || 0, Math.min(maxR || canvasR, this.r * ratio)); this.renderLens(origin); this.renderFocusElements(); }; this.isLensDragging = false; this.onDragStart = (event) => { var _a; const dragOrigin = parsePoint(event.canvas); const origin = (_a = this.lens) === null || _a === void 0 ? void 0 : _a.getCenter(); if (!this.isLensOn || distance(dragOrigin, origin) > this.r) return; this.isLensDragging = true; }; this.onDrag = (event) => { if (!this.isLensDragging) return; const dragOrigin = parsePoint(event.canvas); this.renderLens(dragOrigin); this.renderFocusElements(); }; this.onDragEnd = () => { this.isLensDragging = false; }; this.bindEvents(); } get canvas() { return this.context.canvas.getLayer('transient'); } get isLensOn() { return this.lens && !this.lens.destroyed; } getElementStyle(elementType, datum) { const styler = elementType === 'node' ? this.options.nodeStyle : this.options.edgeStyle; if (typeof styler === 'function') return styler(datum); return styler; } get graphDom() { return this.context.graph.getCanvas().getContextService().getDomElement(); } bindEvents() { var _a; const { graph } = this.context; const { trigger, scaleRBy } = this.options; const canvas = graph.getCanvas().getLayer(); if (['click', 'drag'].includes(trigger)) { canvas.addEventListener(CommonEvent.CLICK, this.onEdgeFilter); } if (trigger === 'pointermove') { canvas.addEventListener(CommonEvent.POINTER_MOVE, this.onEdgeFilter); } else if (trigger === 'drag') { canvas.addEventListener(CommonEvent.DRAG_START, this.onDragStart); canvas.addEventListener(CommonEvent.DRAG, this.onDrag); canvas.addEventListener(CommonEvent.DRAG_END, this.onDragEnd); } if (scaleRBy === 'wheel') { (_a = this.graphDom) === null || _a === void 0 ? void 0 : _a.addEventListener(CommonEvent.WHEEL, this.scaleRByWheel, { passive: false }); } } unbindEvents() { var _a; const { graph } = this.context; const { trigger, scaleRBy } = this.options; const canvas = graph.getCanvas().getLayer(); if (['click', 'drag'].includes(trigger)) { canvas.removeEventListener(CommonEvent.CLICK, this.onEdgeFilter); } if (trigger === 'pointermove') { canvas.removeEventListener(CommonEvent.POINTER_MOVE, this.onEdgeFilter); } else if (trigger === 'drag') { canvas.removeEventListener(CommonEvent.DRAG_START, this.onDragStart); canvas.removeEventListener(CommonEvent.DRAG, this.onDrag); canvas.removeEventListener(CommonEvent.DRAG_END, this.onDragEnd); } if (scaleRBy === 'wheel') { (_a = this.graphDom) === null || _a === void 0 ? void 0 : _a.removeEventListener(CommonEvent.WHEEL, this.scaleRByWheel); } } update(options) { var _a; this.unbindEvents(); super.update(options); this.r = (_a = options.r) !== null && _a !== void 0 ? _a : this.r; this.bindEvents(); } destroy() { this.unbindEvents(); if (this.isLensOn) { this.lens.destroy(); } this.shapes.forEach((shape, id) => { shape.destroy(); this.shapes.delete(id); }); super.destroy(); } } EdgeFilterLens.defaultOptions = { trigger: 'pointermove', r: 60, nodeType: 'both', filter: () => true, style: { lineWidth: 2 }, nodeStyle: { label: false }, edgeStyle: { label: true }, scaleRBy: 'wheel', preventDefault: true, }; //# sourceMappingURL=index.js.map