From db7b9688b75b3932fef6e7cc210656c0f2e318be Mon Sep 17 00:00:00 2001 From: dengzuming Date: Mon, 4 Dec 2023 14:12:39 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Ruler=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=20&=20=E6=B7=BB=E5=8A=A0canvas:changeSize=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/graph/controller/view.ts | 1 + packages/pc/src/index.ts | 3 + packages/plugin/src/index.ts | 7 +- packages/plugin/src/ruler/constructor.ts | 147 +++++++++++ packages/plugin/src/ruler/index.ts | 279 +++++++++++++++++++++ packages/site/docs/api/Event.en.md | 2 +- packages/site/docs/api/Event.zh.md | 1 + packages/site/docs/api/Plugins.en.md | 52 ++++ packages/site/docs/api/Plugins.zh.md | 54 ++++ 9 files changed, 543 insertions(+), 3 deletions(-) create mode 100644 packages/plugin/src/ruler/constructor.ts create mode 100644 packages/plugin/src/ruler/index.ts diff --git a/packages/core/src/graph/controller/view.ts b/packages/core/src/graph/controller/view.ts index c5f914b52ac..b7cad6fe741 100644 --- a/packages/core/src/graph/controller/view.ts +++ b/packages/core/src/graph/controller/view.ts @@ -501,6 +501,7 @@ export default class ViewController { plugin.positionInit(); } }); + graph.emit('canvas:changeSize', { width, height }); } public destroy() { diff --git a/packages/pc/src/index.ts b/packages/pc/src/index.ts index 9b83d922a19..1be6f3d5c8a 100644 --- a/packages/pc/src/index.ts +++ b/packages/pc/src/index.ts @@ -35,6 +35,7 @@ const EdgeFilterLens = Plugin.EdgeFilterLens; const SnapLine = Plugin.SnapLine; const Legend = Plugin.Legend; const Annotation = Plugin.Annotation; +const Ruler = Plugin.Ruler; export * from '@antv/g6-core'; export * from './types'; @@ -62,6 +63,7 @@ export { SnapLine, Legend, Annotation, + Ruler, Arrow, Marker, Shape, @@ -91,6 +93,7 @@ export default { ToolBar: Plugin.ToolBar, Tooltip: Plugin.Tooltip, Legend: Plugin.Legend, + Ruler: Plugin.Ruler, TimeBar, SnapLine, Fisheye, diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index b2763f6aa20..b87fd3eba57 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -12,6 +12,7 @@ import SnapLine from './snapline'; import PluginBase from './base'; import Legend from './legend'; import Annotation from './annotation'; +import Ruler from './ruler'; export { PluginBase, @@ -27,7 +28,8 @@ export { EdgeFilterLens, SnapLine, Legend, - Annotation + Annotation, + Ruler }; const Plugin = { @@ -44,7 +46,8 @@ const Plugin = { EdgeFilterLens, SnapLine, Legend, - Annotation + Annotation, + Ruler }; export default Plugin; diff --git a/packages/plugin/src/ruler/constructor.ts b/packages/plugin/src/ruler/constructor.ts new file mode 100644 index 00000000000..0b3874a1731 --- /dev/null +++ b/packages/plugin/src/ruler/constructor.ts @@ -0,0 +1,147 @@ +export enum ruleDirection { HORIZONTAL = 1, VERTICAL = 2 } +export interface pointConfig { + lineWidth: number; + lineHeight: number; + strokeStyle: CanvasRenderingContext2D['strokeStyle'] + font: CanvasRenderingContext2D['font'] +} + +export interface ruleConfig { + width: number; + height: number; + scale: number; + unitInterval: number; + showUnitLabel: boolean; + unitLabelStyle: CanvasRenderingContext2D['strokeStyle'], + direction?: ruleDirection, +} +export default class RulerConstructor { + + private canvas: HTMLCanvasElement = document.createElement('canvas') + + public width: number = 0 + + private height: number = 0 + + private lineWidth: number = 0.5 + + private lineHeight: number = 10 + + private unitInterval: number = 10 + + private showUnitLabel: boolean = true + + public direction: ruleDirection = 1 + + private scale = 1 + + private unitLabelStyle: CanvasRenderingContext2D['strokeStyle'] = '#333333' + + private strokeStyle: CanvasRenderingContext2D['strokeStyle'] = '#b8b7b8' + + private font: CanvasRenderingContext2D['font'] = '10px sans-serif' + + public container?: HTMLElement + + constructor(config) { + const { lineWidth, width, height, lineHeight, showUnitLabel, unitLabelStyle, strokeStyle, font, unitInterval, direction, container } = config + this.width = width + this.height = height + this.canvas.width = width + this.canvas.height = height + this.lineWidth = lineWidth + this.lineHeight = lineHeight + this.showUnitLabel = showUnitLabel + this.unitLabelStyle = unitLabelStyle + this.strokeStyle = strokeStyle + this.font = font + this.unitInterval = unitInterval + this.direction = direction + this.container = container + this.init() + } + + public getCanvas() { + return this.canvas + } + + public init() { + this.initBrush() + } + + private initBrush() { + const ruleCanvas = this.canvas + const context = ruleCanvas.getContext('2d') + context.strokeStyle = this.strokeStyle + context.fillStyle = this.unitLabelStyle + context.font = this.font + context.lineWidth = this.lineWidth + this.drawPointsAndLine() + } + + + + public changeScale(scale: number) { + this.scale = +scale.toFixed(2) + this.drawPointsAndLine() + } + + public resetRulerSize(width: number, height: number) { + this.width = width + this.height = height + this.canvas.width = width + this.canvas.height = height + this.initBrush() + } + + + private drawPointsAndLine() { + const ruleCanvas = this.canvas + const unitInterval = Math.round(this.unitInterval / this.scale) + const showUnitLabel = this.showUnitLabel + const lineWidth = this.lineWidth + const lineHeight = this.lineHeight + const width = this.width + const height = this.height + const scaleCount = Math.round((width / this.scale) / unitInterval); + // lineWidth / 2是为了定义笔的起始位置, 防止单数宽度过宽的问题 + const m = lineWidth / 2 + const context = ruleCanvas.getContext('2d') + context.clearRect(0, 0, width, height); + context.beginPath(); + for (let i = 0; i <= scaleCount; i++) { + const step = Math.round(i * unitInterval) + /* 竖向的时候, 因为旋转的原因, 以横向来想, 从右边开始绘制0, 左边为最大的数字 */ + let pos = this.direction === 1 ? step * this.scale : width - step * this.scale + if (pos < 0) { pos = 0 } + /* 当间隔 * 10 显示文本, 考虑增加配置? */ + if (i % 10 === 0) { + // xPos防止最后一个线不显示 + const x = pos + m + const xPos = x >= width ? width - m : x + context.moveTo(xPos, 0); + if (showUnitLabel) { + const text = `${step}` + let x = pos + lineWidth + 2 + // 文本大小不固定, 需要计算0占用的大小 + const textWidth = context.measureText(text).width + // 对竖向0的文本,显示在最右边的偏左位置 + if (this.direction === 2 && !step) { + x = width - textWidth - lineWidth - 2 + } else if (this.direction === 1 && (pos + textWidth) >= width) { + x = width - textWidth - lineWidth - 2 + } + context.fillText(text, x, 10) + } + context.lineTo(xPos, height - lineWidth); + } else { + context.moveTo(pos + m, height - lineHeight - lineWidth); + // 需要减去底部线的高度 + context.lineTo(pos + m, height - lineWidth); + } + } + context.moveTo(0, height - m) + context.lineTo(width, height - m) + context.stroke(); + } +} \ No newline at end of file diff --git a/packages/plugin/src/ruler/index.ts b/packages/plugin/src/ruler/index.ts new file mode 100644 index 00000000000..bb29531f835 --- /dev/null +++ b/packages/plugin/src/ruler/index.ts @@ -0,0 +1,279 @@ +import { modifyCSS, createDom } from '@antv/dom-util'; +import { debounce } from '@antv/util'; +import { ICanvas } from '@antv/g-base'; +import { IAbstractGraph as IGraph } from '@antv/g6-core'; +import Base from '../base'; +import RulerConstructor, { pointConfig, ruleConfig, ruleDirection } from './constructor'; + +export interface RulerConfig extends pointConfig, ruleConfig { + directions: ruleDirection[] | ruleDirection, // 方向, 默认[1,2] + startLen: number // 距离 + showLock: boolean // 是否显示锁 + lockColor: string // 锁颜色 + lockZoom: boolean // 当前锁住缩放 + visible: boolean // 是否可见 + monitorZoom: boolean // 是否监听缩放改变刻度是否改变 + monitorSize: boolean +} + +export default class Ruler extends Base { + private lockContainer: HTMLElement + + private rulerWrap: HTMLElement + + private rulerInstances: RulerConstructor[] = [] + + private defaultBegin: Function + + private toggerFn: EventListenerOrEventListenerObject + + constructor(config?: RulerConfig) { + super(config); + } + + public getDefaultCfgs(): RulerConfig { + return { + lineWidth: 1, + lineHeight: 10, + startLen: 25, + scale: 1, + width: 0, + height: 25, + unitInterval: 10, + showUnitLabel: true, + lockZoom: false, + unitLabelStyle: '#333333', + strokeStyle: '#b8b7b8', + font: '10px sans-serif', + directions: [1, 2], + showLock: true, + lockColor: '#7F7F7F', + visible: true, + monitorZoom: true, + monitorSize: true + }; + } + + public init() { + const graph: IGraph = this.get('graph'); + const graphContainer = graph.get('container'); + const canvas: HTMLDivElement = graph.get('canvas').get('el'); + // 高度默认25, 竖向默认取canvas的高度 + const height = this.get('height') || canvas.clientHeight + const lineWidth = this.get('lineWidth') + const showLock = this.get('showLock') + const monitorSize = this.get('monitorSize') + const monitorZoom = this.get('monitorZoom') + + let startLen = this.get('startLen') + let directions = this.get('directions') + let rulerHeight = height + + this.rulerWrap = createDom( + `
`, + ); + if (directions) { + if (typeof directions === 'number') { + directions = [directions] + } else { + // 当时2个标尺的时候, 需要取最大的值作为尺子的高度, 偏移也是 + rulerHeight = Math.max(height, startLen) + startLen = rulerHeight + } + directions.forEach(direction => { + const rulerMaxWidth = this.getRulerMaxWidth(direction) + if (direction === 2) { + rulerHeight += lineWidth + } + const container = createDom( + `
`, + ); + + this.rulerWrap.append(container) + const rulerInstance = new RulerConstructor({ + ...this._cfgs, + width: rulerMaxWidth, + height: rulerHeight, + direction, + container + }) + this.rulerInstances.push(rulerInstance) + const ruleCanvas = rulerInstance.getCanvas() + if (direction === 1) { + modifyCSS(container, { + left: `${startLen}px`, + top: 0, + width: `${rulerMaxWidth}px`, + }); + } else { + modifyCSS(container, { + left: 0, + top: 0, + width: `${rulerMaxWidth}px`, + transform: `rotate(${90}deg)`, + // 旋转的原点, 需要在横线第一条线绘制结束开始, 防止选中后跟第一条线不重叠 + 'transform-origin': `${lineWidth}px calc(100% - ${lineWidth}px)` + }); + modifyCSS(ruleCanvas, { + transform: 'rotate(180deg)', + 'transform-origin': `center center`, + }); + } + + container.appendChild(ruleCanvas) + }) + } + + if (startLen && showLock) { + this.createLockDom() + } + + if (monitorSize) { + graph.on('canvas:changeSize', (e) => { + this.resetRulerSize(e.width) + }) + } + + if (monitorZoom) { + graph.on('wheelzoom', () => { + const zoom = graph.getZoom(); + this.changeScale(zoom) + }) + } + + graphContainer.insertBefore(this.rulerWrap, canvas) + } + + private getRulerMaxWidth(direction) { + const graph: IGraph = this.get('graph'); + const canvas: HTMLDivElement = graph.get('canvas').get('el'); + const width = this.get('width') || canvas.clientWidth + const startLen = this.get('startLen') + const lineWidth = this.get('lineWidth') + let rulerMaxWidth = width - startLen + if (direction === 2) { + // 下面旋转了一个线条的宽度, 需要增加 + const vWidth = this.get('width') || canvas.clientHeight + rulerMaxWidth = vWidth - startLen + lineWidth + } + return rulerMaxWidth + } + + + private createLockDom() { + const graph: IGraph = this.get('graph'); + const modeController = graph.get('modeController') + const { startLen, lineWidth, lockZoom } = this._cfgs + + const zoomCanvas = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === 'zoom-canvas')?.[0]; + + if (zoomCanvas && zoomCanvas.shouldBegin) { + this.set('lockZoom', !zoomCanvas.shouldBegin()) + this.defaultBegin = zoomCanvas.shouldBegin + } + + this.lockContainer = createDom( + `
`, + ); + + modifyCSS(this.lockContainer, { + height: `${startLen}px`, + width: `${startLen}px`, + left: 0, + top: 0, + position: 'absolute', + overflow: 'hidden', + display: 'flex', + 'align-items': 'center', + 'justify-content': 'center', + cursor: 'pointer', + // 因为竖向旋转原点改变, 导致需要向上移动线重叠的距离, + // 3.1px -0.2px 是因为svg图标大小不一致 + transform: `translate(-3.1px, -${lineWidth / 2 + 0.2}px)`, + background: '#fff' + }) + const lockColor = this.get('lockColor') + const LOCK_SVG_DOM = createDom(``) + const UNLOCK_SVG_DOM = createDom(``) + + this.lockContainer.append(LOCK_SVG_DOM, UNLOCK_SVG_DOM) + this.rulerWrap.append(this.lockContainer) + // 缓存函数, 绑定this, 用于销毁能正确解除事件 + this.toggerFn = this.toggerLockZoom.bind(this) + this.lockContainer.addEventListener('click', this.toggerFn) + } + + public resetRulerSize(width, height?) { + this.set('width', width); + if (height) { + this.set('height', height) + } + const graph: IGraph = this.get('graph'); + const canvas: HTMLDivElement = graph.get('canvas').get('el'); + const maxHeight = this.get('height') || canvas.clientHeight + this.rulerInstances.forEach((ruler: RulerConstructor) => { + const rulerMaxWidth = this.getRulerMaxWidth(ruler.direction) + ruler.resetRulerSize(rulerMaxWidth, maxHeight) + modifyCSS(ruler.container, { + width: `${rulerMaxWidth}px`, + }) + }) + } + + public toggerLockZoom() { + this.changeLockZoom(!this.get('lockZoom')) + } + + public changeLockZoom(flag: boolean) { + const graph: IGraph = this.get('graph'); + const LOCK_SVG_DOM = this.lockContainer.querySelector('.lock-svg') as HTMLElement + const UNLOCK_SVG_DOM = this.lockContainer.querySelector('.unLock-svg') as HTMLElement + + if (flag) { + // 锁定缩放 + this.lockContainer.title = '解锁缩放' + LOCK_SVG_DOM.style.display = 'none' + UNLOCK_SVG_DOM.style.display = 'block' + graph.updateBehavior('zoom-canvas', { shouldBegin: () => false }) + } else { + // 解锁缩放 + this.lockContainer.title = '锁定缩放' + LOCK_SVG_DOM.style.display = 'block' + UNLOCK_SVG_DOM.style.display = 'none' + graph.updateBehavior('zoom-canvas', { shouldBegin: this.defaultBegin }) + } + this.set('lockZoom', flag) + } + + public changeVisible(flag: boolean) { + this.rulerWrap.style.display = flag ? 'flex' : 'none' + this.set('visible', flag) + } + + public toggerVisible() { + const visible = this.get('visible') + this.set('visible', !visible) + this.rulerWrap.style.display = !visible ? 'flex' : 'none' + } + + public changeScale(scale: number) { + this.set('scale', scale) + this.rulerInstances.forEach(instance => { + instance.changeScale(scale) + }) + } + + public destroy() { + const graph: IGraph = this.get('graph'); + const graphContainer = graph.get('container'); + this.lockContainer.removeEventListener('click', this.toggerFn) + graphContainer.removeChild(this.rulerWrap as HTMLElement) + this.rulerInstances = [] + this.lockContainer = null + graph.updateBehavior('zoom-canvas', { shouldBegin: this.defaultBegin }) + } +} \ No newline at end of file diff --git a/packages/site/docs/api/Event.en.md b/packages/site/docs/api/Event.en.md index 031d1037126..e6f3af24cf7 100644 --- a/packages/site/docs/api/Event.en.md +++ b/packages/site/docs/api/Event.en.md @@ -139,7 +139,7 @@ Combo inherit all the interaction events of Node. | canvas:touchstart | On touch screen, this event is activated when user begin to touch the canvas | | canvas:touchmove | On touch screen, this event is activated when user is touching the canvas | | canvas:touchend | On touch screen, this event is activated when user finish touching the canvas | - +| canvas:changeSize | Triggered when the canvas size changes | ## Timing Events Before and after being called some functions, G6 exports the timing events. These timing events can be listened by the following way: diff --git a/packages/site/docs/api/Event.zh.md b/packages/site/docs/api/Event.zh.md index 8a4bcaa3d4b..b0ebe7d164f 100644 --- a/packages/site/docs/api/Event.zh.md +++ b/packages/site/docs/api/Event.zh.md @@ -139,6 +139,7 @@ Combo 继承所有 Node 事件。 | canvas:touchstart | 在触控屏上,当画布开始被触碰的时候触发的事件 | | canvas:touchmove | 在触控屏上,当画布开始被触碰过程中触发的事件 | | canvas:touchend | 在触控屏上,当画布开始被触碰结束的时候触发的事件 | +| canvas:changeSize | 当画布大小发生变化的时候触发 | ## 时机事件 diff --git a/packages/site/docs/api/Plugins.en.md b/packages/site/docs/api/Plugins.en.md index 113954c44e1..b9963fdbf4c 100644 --- a/packages/site/docs/api/Plugins.en.md +++ b/packages/site/docs/api/Plugins.en.md @@ -88,6 +88,58 @@ Grid plugin draws grids on the canvas. Use the code in [Configure to Graph](#configure-to-graph) to instantiate grid plugin with the following configurations. +## Ruler + +The Ruler plugin in G6 can be used to draw rulers on the canvas. It provides various configuration options and methods for customization. + + +### 配置项 + +| 名称 | 类型 | 描述 | +| ---- | ------ | ---------------------------- | +| directions | Direction: 1 for horizontal, 2 for vertical, default is [1, 2] | +| width | number | Default width based on canvas width for horizontal, canvas height for vertical (including `startLen`) | +| height | number | Height of the ruler, default is `25`. Takes the maximum of startLen and height when both are present | +| startLen | number | Starting position of the ruler, default is `25`. Takes the maximum of startLen and height when both are present | +| scale | number | Current scale size | +| unitInterval | number | Unit interval | +| showUnitLabel | boolean | Whether to show unit interval labels | +| unitLabelStyle | CanvasRenderingContext2D['strokeStyle'] | Color of the unit labels | +| font | CanvasRenderingContext2D['font'] | Font for the text | +| visible | boolean | Visibility, default is true | +| lineWidth | number | Width of the lines | +| lineHeight | number | Height of the lines | +| strokeStyle | CanvasRenderingContext2D['strokeStyle'] | Color of the lines | +| showLock | number | Whether to show the lock icon | +| lockColor | string | Color of the lock icon | +| lockZoom | boolean | Whether to lock the zoom | +| monitorZoom | boolean | Whether to monitor zoom changes and update the scale, default is true | +| monitorSize | boolean | `changeSize`, Whether to change the ruler size | + +### 方法 + +| 名称 | 描述 | +| ---- | ---------------------------- | +| resetRulerSize | Change the size of the ruler, automatically called when using `changeSize` method by default | +| toggerVisible | Toggle visibility of the ruler | +| changeVisible | Change visibility, accepts a boolean value | +| toggerLockZoom | Toggle whether zoom is locked | +| changeLockZoom | Change whether zoom is locked, accepts a boolean value | +| changeScale | Change the scale size | + +### 用法 + +实例化 Ruler 插件时。 + +```javascript +// Instantiate the Ruler plugin +const ruler = new G6.Ruler(); +const graph = new G6.Graph({ + //... other configurations + plugins: [ruler], // Configure the Ruler plugin +}); +``` + ### Configuration | Name | Type | Required | Description | diff --git a/packages/site/docs/api/Plugins.zh.md b/packages/site/docs/api/Plugins.zh.md index 0e5e918c7fb..82d34b5c698 100644 --- a/packages/site/docs/api/Plugins.zh.md +++ b/packages/site/docs/api/Plugins.zh.md @@ -96,6 +96,60 @@ Grid 插件在画布上绘制了网格。 | ---- | ------ | ---------------------------- | | img | String | grid 图片,base64 格式字符串 | + + +## Ruler + +Ruler 插件在画布上绘制了标尺。通过左上角的锁还可以锁定缩放。 + + +### 配置项 + +| 名称 | 类型 | 描述 | +| ---- | ------ | ---------------------------- | +| directions | ruleDirection[] 或者 ruleDirection | 方向, 1代表横向, 2代表纵向, 默认[1, 2]| +| width | number | 默认横向以canvas宽度, 纵向以canvas高度, 需要注意宽度需要算上`startLen`的大小 | +| height | number | 尺子的高度, 默认是`25`, 当存在 startLen 和 height 会取2个最大的值 | +| startLen | number | 尺子的开始位置, 默认`25`, 当存在 startLen 和 height 会取2个最大的值 | +| scale | number | 当前缩放的大小 | +| unitInterval | number | 单位间隔 | +| showUnitLabel | boolean | 是否单位间隔的文字 | +| unitLabelStyle | CanvasRenderingContext2D['strokeStyle'] | 单位文字的颜色 | +| font | CanvasRenderingContext2D['font'] | 文字的字体 | +| visible | boolean | 是否可见, 默认true | +| lineWidth | number | 线的宽度 | +| lineHeight | number | 线的高度 | +| strokeStyle | CanvasRenderingContext2D['strokeStyle'] | 线的颜色 | +| showLock | number | 是否显示锁icon | +| lockColor | string | 锁的颜色 | +| lockZoom | boolean | 是否锁住缩放 | +| monitorZoom | boolean | 是否监听缩放改变刻度改变, 默认true | +| monitorSize | boolean | `changeSize`, 是否改变尺子的大小 | + +### 方法 + +| 名称 | 描述 | +| ---- | ---------------------------- | +| resetRulerSize | 改变尺子的大小, 默认情况下, 使用`changeSize`方法会自动调用 | +| toggerVisible | 切换显示显示 | +| changeVisible | 改变是否显示, 接受一个布尔值 | +| toggerLockZoom | 切换锁是否可以缩放 | +| changeLockZoom | 改变锁是否可以缩放, 接受一个布尔值 | +| changeScale | 改变缩放大小 | + +### 用法 + +实例化 Ruler 插件时。 + +```javascript +// 实例化 Ruler 插件 +const ruler = new G6.Ruler(); +const graph = new G6.Graph({ + //... 其他配置项 + plugins: [ruler], // 配置 Ruler 插件 +}); +``` + ## Minimap Minimap 是用于快速预览和探索图的工具。 From 5ccf484d0af72ef9a794c6dc02aa5e2769379f1f Mon Sep 17 00:00:00 2001 From: dengzuming Date: Mon, 4 Dec 2023 15:02:58 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/plugin/src/ruler/index.ts | 6 ++- packages/plugin/tests/unit/ruler-spec.ts | 48 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/plugin/tests/unit/ruler-spec.ts diff --git a/packages/plugin/src/ruler/index.ts b/packages/plugin/src/ruler/index.ts index bb29531f835..56757272c0d 100644 --- a/packages/plugin/src/ruler/index.ts +++ b/packages/plugin/src/ruler/index.ts @@ -144,7 +144,7 @@ export default class Ruler extends Base { this.changeScale(zoom) }) } - + this.set('container', this.rulerWrap); graphContainer.insertBefore(this.rulerWrap, canvas) } @@ -267,6 +267,10 @@ export default class Ruler extends Base { }) } + public getContainer(): HTMLDivElement { + return this.get('container'); + } + public destroy() { const graph: IGraph = this.get('graph'); const graphContainer = graph.get('container'); diff --git a/packages/plugin/tests/unit/ruler-spec.ts b/packages/plugin/tests/unit/ruler-spec.ts new file mode 100644 index 00000000000..2619f395e1f --- /dev/null +++ b/packages/plugin/tests/unit/ruler-spec.ts @@ -0,0 +1,48 @@ +import G6 from '@antv/g6'; +import Ruler from '../../src/ruler'; + +const div = document.createElement('div'); +div.id = 'ruler-spec'; +document.body.appendChild(div); + + +describe('ruler', () => { + const ruler = new Ruler(); + const graph = new G6.Graph({ + container: div, + width: 800, + height: 600, + modes: { + default: ['drag-canvas', 'zoom-canvas', 'drag-node'], + }, + plugins: [ruler], + }); + + graph.addItem('node', { x: 100, y: 100 }); + graph.addItem('node', { x: -100, y: -100 }); + + it('ruler', () => { + const container = ruler.getContainer(); + + expect(container).not.toBe(undefined); + expect(container.childNodes.length).toEqual(3); + + ruler.resetRulerSize(800) + expect(ruler.get("width")).toEqual(800); + + ruler.changeVisible(false) + expect(ruler.get("visible")).toEqual(false); + expect(container.style.display).toEqual('none'); + + ruler.changeLockZoom(false) + expect(ruler.get("lockZoom")).toEqual(false); + + }); + it('ruler destroy', () => { + const container = graph.get('container'); + expect(container.childNodes.length).toEqual(2); + + graph.destroy(); + expect(container.childNodes.length).toEqual(0); + }); +}); From e22567d587bb9a4008a914e853c148bde93b9a12 Mon Sep 17 00:00:00 2001 From: dengzuming Date: Mon, 11 Dec 2023 18:18:10 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=E5=B0=BA=E5=AD=90=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=A2=9E=E5=8A=A0=E8=A7=86=E5=8F=A3=E6=94=B9=E5=8F=98?= =?UTF-8?q?,=20=E5=88=BB=E5=BA=A6=E4=B9=9F=E6=94=B9=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/graph/controller/view.ts | 2 +- packages/plugin/src/ruler/constructor.ts | 100 ++++--- packages/plugin/src/ruler/index.ts | 333 ++++++++++++++++----- packages/site/docs/api/Event.en.md | 2 +- packages/site/docs/api/Event.zh.md | 2 +- packages/site/docs/api/Plugins.en.md | 33 +- packages/site/docs/api/Plugins.zh.md | 18 +- 7 files changed, 343 insertions(+), 147 deletions(-) diff --git a/packages/core/src/graph/controller/view.ts b/packages/core/src/graph/controller/view.ts index b7cad6fe741..10002529404 100644 --- a/packages/core/src/graph/controller/view.ts +++ b/packages/core/src/graph/controller/view.ts @@ -501,7 +501,7 @@ export default class ViewController { plugin.positionInit(); } }); - graph.emit('canvas:changeSize', { width, height }); + graph.emit('canvas:changesize', { width, height }); } public destroy() { diff --git a/packages/plugin/src/ruler/constructor.ts b/packages/plugin/src/ruler/constructor.ts index 0b3874a1731..432defd3266 100644 --- a/packages/plugin/src/ruler/constructor.ts +++ b/packages/plugin/src/ruler/constructor.ts @@ -1,4 +1,5 @@ -export enum ruleDirection { HORIZONTAL = 1, VERTICAL = 2 } +export enum ruleDirection { HORIZONTAL = 'horizontal', VERTICAL = 'vertical' } + export interface pointConfig { lineWidth: number; lineHeight: number; @@ -6,58 +7,67 @@ export interface pointConfig { font: CanvasRenderingContext2D['font'] } -export interface ruleConfig { +export interface RulerConf { width: number; height: number; scale: number; unitInterval: number; showUnitLabel: boolean; unitLabelStyle: CanvasRenderingContext2D['strokeStyle'], - direction?: ruleDirection, + background: CanvasRenderingContext2D['fillStyle'], + direction: ruleDirection, + container?: HTMLDivElement, + startNumber: number | string } + export default class RulerConstructor { private canvas: HTMLCanvasElement = document.createElement('canvas') + // canvas width public width: number = 0 + // canvas height private height: number = 0 + // line width private lineWidth: number = 0.5 + // line height private lineHeight: number = 10 + // unit interval size private unitInterval: number = 10 + // show unit label private showUnitLabel: boolean = true - public direction: ruleDirection = 1 + // direction + public direction: ruleDirection = ruleDirection.HORIZONTAL + // scale private scale = 1 + // unit label style private unitLabelStyle: CanvasRenderingContext2D['strokeStyle'] = '#333333' + // stroke style private strokeStyle: CanvasRenderingContext2D['strokeStyle'] = '#b8b7b8' + // font private font: CanvasRenderingContext2D['font'] = '10px sans-serif' + // background style + private background: CanvasRenderingContext2D['fillStyle'] = '#ffffff' + + // 从什么数字开始 + private startNumber: number = 0 + + // 包裹这个ruler canvas的容器是啥 public container?: HTMLElement constructor(config) { - const { lineWidth, width, height, lineHeight, showUnitLabel, unitLabelStyle, strokeStyle, font, unitInterval, direction, container } = config - this.width = width - this.height = height - this.canvas.width = width - this.canvas.height = height - this.lineWidth = lineWidth - this.lineHeight = lineHeight - this.showUnitLabel = showUnitLabel - this.unitLabelStyle = unitLabelStyle - this.strokeStyle = strokeStyle - this.font = font - this.unitInterval = unitInterval - this.direction = direction - this.container = container + this.initConfig(config) this.init() } @@ -69,66 +79,71 @@ export default class RulerConstructor { this.initBrush() } + public initConfig(config) { + Object.keys(config).forEach(key => { + this[key] = config[key] + }) + this.canvas.width = this.width + this.canvas.height = this.height + } + + public changeConfig(config) { + this.initConfig(config) + this.initBrush() + } + private initBrush() { const ruleCanvas = this.canvas const context = ruleCanvas.getContext('2d') context.strokeStyle = this.strokeStyle - context.fillStyle = this.unitLabelStyle context.font = this.font context.lineWidth = this.lineWidth this.drawPointsAndLine() } - - - public changeScale(scale: number) { - this.scale = +scale.toFixed(2) - this.drawPointsAndLine() - } - - public resetRulerSize(width: number, height: number) { - this.width = width - this.height = height - this.canvas.width = width - this.canvas.height = height - this.initBrush() - } - - private drawPointsAndLine() { const ruleCanvas = this.canvas - const unitInterval = Math.round(this.unitInterval / this.scale) + let unitInterval = Math.round(this.unitInterval / this.scale) + // fix: 当计算少于1, 就取4位 + if (unitInterval < 1) { + unitInterval = +(this.unitInterval / this.scale).toFixed(4) + } const showUnitLabel = this.showUnitLabel const lineWidth = this.lineWidth const lineHeight = this.lineHeight const width = this.width const height = this.height + const startNumber = this.startNumber const scaleCount = Math.round((width / this.scale) / unitInterval); // lineWidth / 2是为了定义笔的起始位置, 防止单数宽度过宽的问题 const m = lineWidth / 2 const context = ruleCanvas.getContext('2d') context.clearRect(0, 0, width, height); context.beginPath(); + context.fillStyle = this.background; + context.fillRect(0, 0, width, height) + context.fillStyle = this.unitLabelStyle for (let i = 0; i <= scaleCount; i++) { const step = Math.round(i * unitInterval) /* 竖向的时候, 因为旋转的原因, 以横向来想, 从右边开始绘制0, 左边为最大的数字 */ - let pos = this.direction === 1 ? step * this.scale : width - step * this.scale + let pos = this.direction === ruleDirection.HORIZONTAL ? step * this.scale : width - step * this.scale if (pos < 0) { pos = 0 } - /* 当间隔 * 10 显示文本, 考虑增加配置? */ + /* 当间隔 * 10 显示文本, 考虑增加配置 */ if (i % 10 === 0) { // xPos防止最后一个线不显示 const x = pos + m const xPos = x >= width ? width - m : x context.moveTo(xPos, 0); if (showUnitLabel) { - const text = `${step}` + const text = `${startNumber + step}` let x = pos + lineWidth + 2 - // 文本大小不固定, 需要计算0占用的大小 + // 文本不固定, 需要计算占用的大小 const textWidth = context.measureText(text).width // 对竖向0的文本,显示在最右边的偏左位置 - if (this.direction === 2 && !step) { + if (this.direction === 'vertical' && !step) { x = width - textWidth - lineWidth - 2 - } else if (this.direction === 1 && (pos + textWidth) >= width) { + } else if (this.direction === ruleDirection.HORIZONTAL && (pos + textWidth) >= width) { + // 横向最后一个数字同理 x = width - textWidth - lineWidth - 2 } context.fillText(text, x, 10) @@ -140,6 +155,7 @@ export default class RulerConstructor { context.lineTo(pos + m, height - lineWidth); } } + // 底部的线, 对于上面的线, 不做绘制, 当画布沾满全屏, 标尺在左上, 非全屏, 在包裹容器外添加 同宽度 左、上 边框即可 context.moveTo(0, height - m) context.lineTo(width, height - m) context.stroke(); diff --git a/packages/plugin/src/ruler/index.ts b/packages/plugin/src/ruler/index.ts index 56757272c0d..ab0fe15aa5b 100644 --- a/packages/plugin/src/ruler/index.ts +++ b/packages/plugin/src/ruler/index.ts @@ -1,31 +1,44 @@ import { modifyCSS, createDom } from '@antv/dom-util'; -import { debounce } from '@antv/util'; import { ICanvas } from '@antv/g-base'; import { IAbstractGraph as IGraph } from '@antv/g6-core'; import Base from '../base'; -import RulerConstructor, { pointConfig, ruleConfig, ruleDirection } from './constructor'; +import RulerConstructor, { pointConfig, RulerConf, ruleDirection } from './constructor'; -export interface RulerConfig extends pointConfig, ruleConfig { - directions: ruleDirection[] | ruleDirection, // 方向, 默认[1,2] - startLen: number // 距离 +export interface RulerConfig extends pointConfig, RulerConf { + directions: ruleDirection[] | ruleDirection, // 方向, 默认['horizontal', 'vertical'] + startLen: number // 开始长度 / 距离 showLock: boolean // 是否显示锁 lockColor: string // 锁颜色 - lockZoom: boolean // 当前锁住缩放 + lock: boolean // 当前锁住缩放 和 拖放 visible: boolean // 是否可见 - monitorZoom: boolean // 是否监听缩放改变刻度是否改变 - monitorSize: boolean + monitorZoom: boolean // 监听缩放改变刻度 + monitorSize: boolean // 监听大小改变刻度 + monitorDrag: boolean // 监听拖拽改变刻度 +} + +interface EventFnObj { + [key: string]: (e: any) => void } export default class Ruler extends Base { + + // 锁的容器DOM private lockContainer: HTMLElement + // 标尺的包裹DOM private rulerWrap: HTMLElement - private rulerInstances: RulerConstructor[] = [] + // 所有标尺的实例 + public rulerInstances: RulerConstructor[] = [] - private defaultBegin: Function + // 缓存行为的是否执行函数 + private shouldBeginFnObj: Object = {} - private toggerFn: EventListenerOrEventListenerObject + // 锁定会控制哪些行为 + private lockBehaviorKey: Array = ['zoom-canvas', 'drag-canvas', 'scroll-canvas'] + + // 所有绑定的事件函数, 改变this + private eventFnObj: EventFnObj = {} constructor(config?: RulerConfig) { super(config); @@ -35,62 +48,70 @@ export default class Ruler extends Base { return { lineWidth: 1, lineHeight: 10, - startLen: 25, + startLen: 0, scale: 1, width: 0, height: 25, unitInterval: 10, showUnitLabel: true, - lockZoom: false, + lock: false, unitLabelStyle: '#333333', strokeStyle: '#b8b7b8', font: '10px sans-serif', - directions: [1, 2], + directions: [ruleDirection.HORIZONTAL, ruleDirection.VERTICAL], showLock: true, lockColor: '#7F7F7F', visible: true, monitorZoom: true, - monitorSize: true + monitorSize: true, + monitorDrag: true, + background: '#ffffff', + startNumber: 0, + direction: ruleDirection.HORIZONTAL }; } public init() { const graph: IGraph = this.get('graph'); const graphContainer = graph.get('container'); - const canvas: HTMLDivElement = graph.get('canvas').get('el'); + const canvas: HTMLCanvasElement = graph.get('canvas').get('el'); // 高度默认25, 竖向默认取canvas的高度 - const height = this.get('height') || canvas.clientHeight + const height = this.get('height') || graphContainer.clientHeight const lineWidth = this.get('lineWidth') const showLock = this.get('showLock') const monitorSize = this.get('monitorSize') const monitorZoom = this.get('monitorZoom') + const monitorDrag = this.get('monitorDrag') let startLen = this.get('startLen') let directions = this.get('directions') + const startNumber = this.get('startNumber') let rulerHeight = height this.rulerWrap = createDom( `
`, ); if (directions) { - if (typeof directions === 'number') { + if (typeof directions === 'string') { directions = [directions] } else { - // 当时2个标尺的时候, 需要取最大的值作为尺子的高度, 偏移也是 + // 当有2个标尺的时候, 需要取最大的值作为尺子的高度, 开始长度也是 rulerHeight = Math.max(height, startLen) startLen = rulerHeight + this.set('startLen', startLen) } directions.forEach(direction => { const rulerMaxWidth = this.getRulerMaxWidth(direction) - if (direction === 2) { + // 竖向需要增加线的宽度, 跟横向的第一条线对齐 + if (direction === ruleDirection.VERTICAL) { rulerHeight += lineWidth } const container = createDom( - `
`, + `
`, ); this.rulerWrap.append(container) @@ -99,11 +120,12 @@ export default class Ruler extends Base { width: rulerMaxWidth, height: rulerHeight, direction, - container + container, + startNumber }) this.rulerInstances.push(rulerInstance) const ruleCanvas = rulerInstance.getCanvas() - if (direction === 1) { + if (direction === ruleDirection.HORIZONTAL) { modifyCSS(container, { left: `${startLen}px`, top: 0, @@ -111,12 +133,13 @@ export default class Ruler extends Base { }); } else { modifyCSS(container, { - left: 0, - top: 0, + // 上面高度增加了, 偏移相应增加 + left: directions.length > 1 ? `${startLen + lineWidth}px` : `${height}px`, + // 跟横向底部线重叠 + top: directions.length > 1 ? `${startLen - lineWidth}px` : `${startLen}px`, width: `${rulerMaxWidth}px`, transform: `rotate(${90}deg)`, - // 旋转的原点, 需要在横线第一条线绘制结束开始, 防止选中后跟第一条线不重叠 - 'transform-origin': `${lineWidth}px calc(100% - ${lineWidth}px)` + 'transform-origin': `0 0` }); modifyCSS(ruleCanvas, { transform: 'rotate(180deg)', @@ -131,57 +154,143 @@ export default class Ruler extends Base { if (startLen && showLock) { this.createLockDom() } - + this.moveCanvasAndChangeSize() if (monitorSize) { - graph.on('canvas:changeSize', (e) => { - this.resetRulerSize(e.width) - }) + this.eventFnObj['canvas:changesize'] = this.bindChangeSizeFn.bind(this) + graph.on('canvas:changesize', this.eventFnObj['canvas:changesize']) } if (monitorZoom) { - graph.on('wheelzoom', () => { - const zoom = graph.getZoom(); - this.changeScale(zoom) - }) + this.eventFnObj.wheelzoom = this.bindChangeScaleFn.bind(this) + graph.on('wheelzoom', this.eventFnObj.wheelzoom) + } + + if (monitorDrag) { + this.eventFnObj.viewportchange = this.bindViewPortFn.bind(this) + graph.on('viewportchange', this.eventFnObj.viewportchange) } + this.set('container', this.rulerWrap); graphContainer.insertBefore(this.rulerWrap, canvas) } + private getStartNumber(direction: ruleDirection) { + const graph: IGraph = this.get('graph'); + const scale = this.get('scale') + const canvas: HTMLCanvasElement = graph.get('canvas').get('el'); + const maxWidth = direction === ruleDirection.HORIZONTAL ? canvas.width : canvas.height + const directionKey = direction === ruleDirection.HORIZONTAL ? 'x' : 'y' + const centerLoc = this.get('graph').getViewPortCenterPoint() + const startNumber = centerLoc[directionKey] - (maxWidth / scale) / 2 + return Math.round(startNumber) + } + private getRulerMaxWidth(direction) { const graph: IGraph = this.get('graph'); - const canvas: HTMLDivElement = graph.get('canvas').get('el'); - const width = this.get('width') || canvas.clientWidth + const graphContainer = graph.get('container'); + const overflow = graphContainer.style.overflow + graphContainer.style.overflow = 'hidden' + const canvas: HTMLCanvasElement = graph.get('canvas').get('el'); const startLen = this.get('startLen') + const width = graphContainer.clientWidth - startLen const lineWidth = this.get('lineWidth') - let rulerMaxWidth = width - startLen - if (direction === 2) { - // 下面旋转了一个线条的宽度, 需要增加 - const vWidth = this.get('width') || canvas.clientHeight - rulerMaxWidth = vWidth - startLen + lineWidth + let rulerMaxWidth = canvas.clientWidth < width ? canvas.clientWidth : width + if (direction === ruleDirection.VERTICAL) { + // 增加top的部分 + const vWidth = graphContainer.clientHeight - startLen + lineWidth + rulerMaxWidth = canvas.clientHeight < vWidth ? canvas.clientHeight : vWidth } + graphContainer.style.overflow = overflow return rulerMaxWidth } + private moveCanvasAndChangeSize(reset = false) { + const graph: IGraph = this.get('graph') + const graphContainer = graph.get('container'); + const gridContainer: HTMLDivElement = graphContainer.querySelector('.g6-grid-container') + const canvas: HTMLCanvasElement = graph.get('canvas').get('el'); + const lineWidth = this.get('lineWidth') + const startLen = this.get('startLen') + const rulerHeight = this.get('height') || graphContainer.clientHeight + const maxStartLen = Math.max(startLen, rulerHeight) + + let left = maxStartLen + lineWidth + let top = maxStartLen + + let width = canvas.width + let height = canvas.height + + /* 兼容只有一个尺子的情况下 */ + if (this.rulerInstances.length && this.rulerInstances.length < 2) { + const direction = this.rulerInstances[0].direction + if (direction === ruleDirection.HORIZONTAL) { + left = 0 + } else { + top = 0 + } + } + + const maxWidth = width + left + const maxHeight = height + top + + if (reset) { + height = maxHeight + width = maxWidth + left = 0 + top = 0 + } else { + // 内容区能放下就不减少 + if (graphContainer.clientHeight <= maxHeight) { + height -= top + } + + if (graphContainer.clientWidth <= maxWidth) { + width -= left + } + } + + modifyCSS(canvas, { + 'margin-left': `${left}px`, + 'margin-top': `${top}px` + }) + modifyCSS(gridContainer, { + 'margin-left': `${left}px`, + 'margin-top': `${top}px` + }) + // 因为有偏移, 所以需要改变canvas的宽度, 以避免startLen的内容给覆盖 + graph.changeSize(width, height) + } private createLockDom() { + const graph: IGraph = this.get('graph'); + const background = this.get('background') + const startLen = this.get('startLen') + const lock = this.get('lock') + const lineWidth = this.get('lineWidth') const modeController = graph.get('modeController') - const { startLen, lineWidth, lockZoom } = this._cfgs - const zoomCanvas = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === 'zoom-canvas')?.[0]; + const lockFlagArr = [] - if (zoomCanvas && zoomCanvas.shouldBegin) { - this.set('lockZoom', !zoomCanvas.shouldBegin()) - this.defaultBegin = zoomCanvas.shouldBegin - } + this.lockBehaviorKey.forEach(key => { + const behavior = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === key)?.[0]; + if (behavior) { + if (behavior.shouldBegin) { + this.shouldBeginFnObj[key] = behavior.shouldBegin + lockFlagArr.push(!behavior.shouldBegin()) + } + lockFlagArr.push(false) + } + }) + + this.set('lock', lockFlagArr.some(flag => flag)) this.lockContainer = createDom( - `
`, + `
`, ); - modifyCSS(this.lockContainer, { - height: `${startLen}px`, + // 高度需要减去竖向标尺与横向标尺重叠的线的宽度, 以防止背景重叠线 + height: `${startLen - lineWidth}px`, width: `${startLen}px`, left: 0, top: 0, @@ -191,82 +300,140 @@ export default class Ruler extends Base { 'align-items': 'center', 'justify-content': 'center', cursor: 'pointer', - // 因为竖向旋转原点改变, 导致需要向上移动线重叠的距离, - // 3.1px -0.2px 是因为svg图标大小不一致 - transform: `translate(-3.1px, -${lineWidth / 2 + 0.2}px)`, - background: '#fff' + background, }) + const lockColor = this.get('lockColor') - const LOCK_SVG_DOM = createDom(``) - const UNLOCK_SVG_DOM = createDom(``) + const LOCK_SVG_DOM = createDom(``) + const UNLOCK_SVG_DOM = createDom(``) + + // 3.1px -0.2px 是因为svg图标大小不一致 + modifyCSS(LOCK_SVG_DOM, { + display: !lock ? 'block' : 'none', + transform: `translate(-3.1px, -0.2px)`, + }) + + modifyCSS(UNLOCK_SVG_DOM, { + display: lock ? 'block' : 'none', + transform: `translate(-3.1px, -0.2px)`, + }) this.lockContainer.append(LOCK_SVG_DOM, UNLOCK_SVG_DOM) this.rulerWrap.append(this.lockContainer) // 缓存函数, 绑定this, 用于销毁能正确解除事件 - this.toggerFn = this.toggerLockZoom.bind(this) - this.lockContainer.addEventListener('click', this.toggerFn) + this.eventFnObj.lock = this.toggerLock.bind(this) + this.lockContainer.addEventListener('click', this.eventFnObj.lock) } - public resetRulerSize(width, height?) { - this.set('width', width); + private resetRulerSize(height?) { + const graph: IGraph = this.get('graph'); if (height) { this.set('height', height) } - const graph: IGraph = this.get('graph'); const canvas: HTMLDivElement = graph.get('canvas').get('el'); const maxHeight = this.get('height') || canvas.clientHeight this.rulerInstances.forEach((ruler: RulerConstructor) => { const rulerMaxWidth = this.getRulerMaxWidth(ruler.direction) - ruler.resetRulerSize(rulerMaxWidth, maxHeight) + const startNumber = this.getStartNumber(ruler.direction) + ruler.changeConfig({ + width: rulerMaxWidth, + height: maxHeight, + startNumber + }) modifyCSS(ruler.container, { width: `${rulerMaxWidth}px`, }) }) } - public toggerLockZoom() { - this.changeLockZoom(!this.get('lockZoom')) + // 改变尺子的配合, 配置改变就会重新绘制尺子, 不管改变什么, 例如改变颜色, 改变缩放 + public changeRulerConfig(config) { + this.rulerInstances.forEach(rulerInstance => { + rulerInstance.changeConfig(config) + }) + } + + private moveAndResetCanvasChangeFn(reset = false) { + const graph: IGraph = this.get('graph') + graph.off('canvas:changesize', this.eventFnObj['canvas:changesize']) + this.moveCanvasAndChangeSize(reset) + graph.on('canvas:changesize', this.eventFnObj['canvas:changesize']) + } + + private bindChangeSizeFn() { + this.moveAndResetCanvasChangeFn() + this.resetRulerSize() } - public changeLockZoom(flag: boolean) { + private bindChangeScaleFn() { + const graph: IGraph = this.get('graph') + const zoom = graph.getZoom(); + this.changeScaleSelf(+zoom.toFixed(2)) + } + + private bindViewPortFn() { + this.rulerInstances.forEach(rulerInstance => { + const startNumber = this.getStartNumber(rulerInstance.direction) + rulerInstance.changeConfig({ + startNumber + }) + }) + } + + public toggerLock() { + this.changeLock(!this.get('lock')) + } + + public changeLock(flag: boolean) { + const graph: IGraph = this.get('graph'); const LOCK_SVG_DOM = this.lockContainer.querySelector('.lock-svg') as HTMLElement const UNLOCK_SVG_DOM = this.lockContainer.querySelector('.unLock-svg') as HTMLElement if (flag) { // 锁定缩放 - this.lockContainer.title = '解锁缩放' + this.lockContainer.title = '解锁' LOCK_SVG_DOM.style.display = 'none' UNLOCK_SVG_DOM.style.display = 'block' - graph.updateBehavior('zoom-canvas', { shouldBegin: () => false }) } else { // 解锁缩放 - this.lockContainer.title = '锁定缩放' + this.lockContainer.title = '锁定' LOCK_SVG_DOM.style.display = 'block' UNLOCK_SVG_DOM.style.display = 'none' - graph.updateBehavior('zoom-canvas', { shouldBegin: this.defaultBegin }) } - this.set('lockZoom', flag) + + this.lockBehaviorKey.forEach(key => { + graph.updateBehavior(key, { shouldBegin: flag ? () => false : this.shouldBeginFnObj[key] }) + }) + this.set('lock', flag) } public changeVisible(flag: boolean) { this.rulerWrap.style.display = flag ? 'flex' : 'none' + this.moveAndResetCanvasChangeFn(!flag) this.set('visible', flag) } public toggerVisible() { const visible = this.get('visible') - this.set('visible', !visible) - this.rulerWrap.style.display = !visible ? 'flex' : 'none' + this.changeVisible(!visible) } - public changeScale(scale: number) { + private changeScaleSelf(scale: number) { this.set('scale', scale) this.rulerInstances.forEach(instance => { - instance.changeScale(scale) + const startNumber = this.getStartNumber(instance.direction) + instance.changeConfig({ scale, startNumber }) }) } + public changeScale(scale: number) { + const graph: IGraph = this.get('graph'); + this.changeScaleSelf(scale) + // 主动触发需要同步改变canvas的大小 + graph.zoom(scale) + } + public getContainer(): HTMLDivElement { return this.get('container'); } @@ -274,10 +441,16 @@ export default class Ruler extends Base { public destroy() { const graph: IGraph = this.get('graph'); const graphContainer = graph.get('container'); - this.lockContainer.removeEventListener('click', this.toggerFn) + this.moveAndResetCanvasChangeFn(true) + Object.keys(this.eventFnObj).forEach(key => { + if (key === 'lock') { + this.lockContainer.removeEventListener('click', this.eventFnObj.lock) + } + graph.off(key, this.eventFnObj[key]) + }) graphContainer.removeChild(this.rulerWrap as HTMLElement) - this.rulerInstances = [] - this.lockContainer = null - graph.updateBehavior('zoom-canvas', { shouldBegin: this.defaultBegin }) + this.lockBehaviorKey.forEach(key => { + graph.updateBehavior(key, { shouldBegin: this.shouldBeginFnObj[key] }) + }) } } \ No newline at end of file diff --git a/packages/site/docs/api/Event.en.md b/packages/site/docs/api/Event.en.md index e6f3af24cf7..422764e83d7 100644 --- a/packages/site/docs/api/Event.en.md +++ b/packages/site/docs/api/Event.en.md @@ -139,7 +139,7 @@ Combo inherit all the interaction events of Node. | canvas:touchstart | On touch screen, this event is activated when user begin to touch the canvas | | canvas:touchmove | On touch screen, this event is activated when user is touching the canvas | | canvas:touchend | On touch screen, this event is activated when user finish touching the canvas | -| canvas:changeSize | Triggered when the canvas size changes | +| canvas:changesize | Triggered when the canvas size changes | ## Timing Events Before and after being called some functions, G6 exports the timing events. These timing events can be listened by the following way: diff --git a/packages/site/docs/api/Event.zh.md b/packages/site/docs/api/Event.zh.md index b0ebe7d164f..3103e0905ab 100644 --- a/packages/site/docs/api/Event.zh.md +++ b/packages/site/docs/api/Event.zh.md @@ -139,7 +139,7 @@ Combo 继承所有 Node 事件。 | canvas:touchstart | 在触控屏上,当画布开始被触碰的时候触发的事件 | | canvas:touchmove | 在触控屏上,当画布开始被触碰过程中触发的事件 | | canvas:touchend | 在触控屏上,当画布开始被触碰结束的时候触发的事件 | -| canvas:changeSize | 当画布大小发生变化的时候触发 | +| canvas:changesize | 当画布大小发生变化的时候触发 | ## 时机事件 diff --git a/packages/site/docs/api/Plugins.en.md b/packages/site/docs/api/Plugins.en.md index b9963fdbf4c..963893726a5 100644 --- a/packages/site/docs/api/Plugins.en.md +++ b/packages/site/docs/api/Plugins.en.md @@ -92,12 +92,15 @@ Use the code in [Configure to Graph](#configure-to-graph) to instantiate grid pl The Ruler plugin in G6 can be used to draw rulers on the canvas. It provides various configuration options and methods for customization. +⚠️ notice: Because the ruler will occupy a certain space, the interior will reduce the width and height of the canvas and offset the size of the ruler, and the grid will also need to offset, if necessary, you need to register the grid first -### 配置项 -| 名称 | 类型 | 描述 | + +### Configuration + +| Name | Type | Description | | ---- | ------ | ---------------------------- | -| directions | Direction: 1 for horizontal, 2 for vertical, default is [1, 2] | +| directions | ruleDirection[] or ruleDirection | 'horizontal' for horizontal, 'vertical' for vertical, default `['horizontal', 'vertical']`| | width | number | Default width based on canvas width for horizontal, canvas height for vertical (including `startLen`) | | height | number | Height of the ruler, default is `25`. Takes the maximum of startLen and height when both are present | | startLen | number | Starting position of the ruler, default is `25`. Takes the maximum of startLen and height when both are present | @@ -110,26 +113,28 @@ The Ruler plugin in G6 can be used to draw rulers on the canvas. It provides var | lineWidth | number | Width of the lines | | lineHeight | number | Height of the lines | | strokeStyle | CanvasRenderingContext2D['strokeStyle'] | Color of the lines | +| background | CanvasRenderingContext2D['fillStyle'] | The color of the ruler | | showLock | number | Whether to show the lock icon | | lockColor | string | Color of the lock icon | -| lockZoom | boolean | Whether to lock the zoom | -| monitorZoom | boolean | Whether to monitor zoom changes and update the scale, default is true | -| monitorSize | boolean | `changeSize`, Whether to change the ruler size | +| lock | boolean | Whether to lock | +| monitorZoom | boolean | Whether the `wheelzoom` event changes the scale | +| monitorSize | boolean |Whether the `changeSize` event changes the size of the ruler | +| monitorViewPort | boolean | Whether the `viewportchange` event changes the scale | -### 方法 +### API -| 名称 | 描述 | +| Name | Description | | ---- | ---------------------------- | -| resetRulerSize | Change the size of the ruler, automatically called when using `changeSize` method by default | +| changeRulerConfig | Change the configuration of the ruler, such as color, text size, etc. | | toggerVisible | Toggle visibility of the ruler | | changeVisible | Change visibility, accepts a boolean value | -| toggerLockZoom | Toggle whether zoom is locked | -| changeLockZoom | Change whether zoom is locked, accepts a boolean value | -| changeScale | Change the scale size | +| toggerLock | Toggle the lock status. When locked, dragging, moving, and scrolling are disabled. | +| changeLock | Change the lock status, accepts a boolean value. | +| changeScale | Change the scale size. Internally triggers the `graph.zoom` event. | -### 用法 +### Usage -实例化 Ruler 插件时。 +When the Ruler plug-in is instantiated. ```javascript // Instantiate the Ruler plugin diff --git a/packages/site/docs/api/Plugins.zh.md b/packages/site/docs/api/Plugins.zh.md index 82d34b5c698..49154f7c226 100644 --- a/packages/site/docs/api/Plugins.zh.md +++ b/packages/site/docs/api/Plugins.zh.md @@ -102,13 +102,13 @@ Grid 插件在画布上绘制了网格。 Ruler 插件在画布上绘制了标尺。通过左上角的锁还可以锁定缩放。 +⚠️ 注意: 因为尺子会占有占一定空间, 所以内部会对`canvas`减少`宽高`以及`偏移尺子的大小`, 并且`grid`也需要偏移, 如需要, 需要先注册`grid` ### 配置项 | 名称 | 类型 | 描述 | | ---- | ------ | ---------------------------- | -| directions | ruleDirection[] 或者 ruleDirection | 方向, 1代表横向, 2代表纵向, 默认[1, 2]| -| width | number | 默认横向以canvas宽度, 纵向以canvas高度, 需要注意宽度需要算上`startLen`的大小 | +| directions | ruleDirection[] 或者 ruleDirection | 方向, `horizontal`代表横向, `vertical`代表纵向, 默认`['horizontal', 'vertical']`| | height | number | 尺子的高度, 默认是`25`, 当存在 startLen 和 height 会取2个最大的值 | | startLen | number | 尺子的开始位置, 默认`25`, 当存在 startLen 和 height 会取2个最大的值 | | scale | number | 当前缩放的大小 | @@ -120,22 +120,24 @@ Ruler 插件在画布上绘制了标尺。通过左上角的锁还可以锁定 | lineWidth | number | 线的宽度 | | lineHeight | number | 线的高度 | | strokeStyle | CanvasRenderingContext2D['strokeStyle'] | 线的颜色 | +| background | CanvasRenderingContext2D['fillStyle'] | 尺子的颜色 | | showLock | number | 是否显示锁icon | | lockColor | string | 锁的颜色 | -| lockZoom | boolean | 是否锁住缩放 | -| monitorZoom | boolean | 是否监听缩放改变刻度改变, 默认true | +| lock | boolean | 是否锁住 | +| monitorZoom | boolean | `wheelzoom`, 事件是否改变刻度 | | monitorSize | boolean | `changeSize`, 是否改变尺子的大小 | +| monitorViewPort | boolean | `viewportchange`, 事件是否改变刻度 | ### 方法 | 名称 | 描述 | | ---- | ---------------------------- | -| resetRulerSize | 改变尺子的大小, 默认情况下, 使用`changeSize`方法会自动调用 | +| changeRulerConfig | 改变尺子的配置,例如改变颜色、文本大小等 | | toggerVisible | 切换显示显示 | | changeVisible | 改变是否显示, 接受一个布尔值 | -| toggerLockZoom | 切换锁是否可以缩放 | -| changeLockZoom | 改变锁是否可以缩放, 接受一个布尔值 | -| changeScale | 改变缩放大小 | +| toggerLock | 切换锁定状态, 锁定状态下, 无法拖拽、移动、滚动 | +| changeLock | 改变锁定状态, 接受一个布尔值 | +| changeScale | 改变缩放大小, 内部会触发`graph.zoom`事件 | ### 用法 From 03e6e21021c17ee2af92a78feb2a27e83d38074c Mon Sep 17 00:00:00 2001 From: dengzuming Date: Mon, 11 Dec 2023 18:52:05 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix=EF=BC=9A=20=E4=BF=AE=E5=A4=8D=E9=94=80?= =?UTF-8?q?=E6=AF=81=EF=BC=8C=E6=B2=A1=E6=9C=89=E6=AD=A3=E7=A1=AE=E6=94=B9?= =?UTF-8?q?=E5=8F=98=E9=BB=98=E8=AE=A4=E8=A1=8C=E4=B8=BA=EF=BC=8C=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A1=8C=E4=B8=BA=E6=9B=B4=E6=96=B0=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/plugin/src/ruler/index.ts | 20 +++++++++++++++++--- packages/site/docs/api/Plugins.en.md | 1 + packages/site/docs/api/Plugins.zh.md | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/plugin/src/ruler/index.ts b/packages/plugin/src/ruler/index.ts index ab0fe15aa5b..09b92bb2cf7 100644 --- a/packages/plugin/src/ruler/index.ts +++ b/packages/plugin/src/ruler/index.ts @@ -271,12 +271,13 @@ export default class Ruler extends Base { const modeController = graph.get('modeController') const lockFlagArr = [] + this.updateShouldBegin() + // 初始锁定状态 this.lockBehaviorKey.forEach(key => { const behavior = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === key)?.[0]; if (behavior) { if (behavior.shouldBegin) { - this.shouldBeginFnObj[key] = behavior.shouldBegin lockFlagArr.push(!behavior.shouldBegin()) } lockFlagArr.push(false) @@ -346,6 +347,19 @@ export default class Ruler extends Base { }) } + public updateShouldBegin() { + const graph: IGraph = this.get('graph') + const modeController = graph.get('modeController') + this.lockBehaviorKey.forEach(key => { + const behavior = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === key)?.[0]; + if (behavior) { + if (behavior.shouldBegin) { + this.shouldBeginFnObj[key] = behavior.shouldBegin + } + } + }) + } + // 改变尺子的配合, 配置改变就会重新绘制尺子, 不管改变什么, 例如改变颜色, 改变缩放 public changeRulerConfig(config) { this.rulerInstances.forEach(rulerInstance => { @@ -403,7 +417,7 @@ export default class Ruler extends Base { } this.lockBehaviorKey.forEach(key => { - graph.updateBehavior(key, { shouldBegin: flag ? () => false : this.shouldBeginFnObj[key] }) + graph.updateBehavior(key, { shouldBegin: flag ? () => false : this.shouldBeginFnObj[key] || (() => true) }) }) this.set('lock', flag) } @@ -450,7 +464,7 @@ export default class Ruler extends Base { }) graphContainer.removeChild(this.rulerWrap as HTMLElement) this.lockBehaviorKey.forEach(key => { - graph.updateBehavior(key, { shouldBegin: this.shouldBeginFnObj[key] }) + graph.updateBehavior(key, { shouldBegin: this.shouldBeginFnObj[key] || (() => true) }) }) } } \ No newline at end of file diff --git a/packages/site/docs/api/Plugins.en.md b/packages/site/docs/api/Plugins.en.md index 963893726a5..084bd484ea1 100644 --- a/packages/site/docs/api/Plugins.en.md +++ b/packages/site/docs/api/Plugins.en.md @@ -131,6 +131,7 @@ The Ruler plugin in G6 can be used to draw rulers on the canvas. It provides var | toggerLock | Toggle the lock status. When locked, dragging, moving, and scrolling are disabled. | | changeLock | Change the lock status, accepts a boolean value. | | changeScale | Change the scale size. Internally triggers the `graph.zoom` event. | +| updateShouldBegin | Behavioral updates such as scaling, drag and drop need to be called to keep the lock switch up-to-date | ### Usage diff --git a/packages/site/docs/api/Plugins.zh.md b/packages/site/docs/api/Plugins.zh.md index 49154f7c226..38af658ff96 100644 --- a/packages/site/docs/api/Plugins.zh.md +++ b/packages/site/docs/api/Plugins.zh.md @@ -138,6 +138,7 @@ Ruler 插件在画布上绘制了标尺。通过左上角的锁还可以锁定 | toggerLock | 切换锁定状态, 锁定状态下, 无法拖拽、移动、滚动 | | changeLock | 改变锁定状态, 接受一个布尔值 | | changeScale | 改变缩放大小, 内部会触发`graph.zoom`事件 | +| updateShouldBegin | 缩放、拖拽等行为更新需要调用, 用于锁定切换能保留最新 | ### 用法 From 0fd112dac680c77037aa8b7edc338b893bf33cf7 Mon Sep 17 00:00:00 2001 From: dengzuming Date: Wed, 13 Dec 2023 15:48:27 +0800 Subject: [PATCH 5/6] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E5=91=BD=E5=90=8D=E5=92=8C=E6=B3=A8=E9=87=8A=20&&=20f?= =?UTF-8?q?ix:=20=E4=BF=AE=E5=A4=8D=E6=9B=B4=E6=96=B0shouldBegin=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/plugin/src/ruler/constructor.ts | 56 +++++++++--------- packages/plugin/src/ruler/index.ts | 73 ++++++++++++------------ packages/site/docs/api/Plugins.en.md | 7 +-- packages/site/docs/api/Plugins.zh.md | 7 +-- 4 files changed, 70 insertions(+), 73 deletions(-) diff --git a/packages/plugin/src/ruler/constructor.ts b/packages/plugin/src/ruler/constructor.ts index 432defd3266..d2208dd8a3c 100644 --- a/packages/plugin/src/ruler/constructor.ts +++ b/packages/plugin/src/ruler/constructor.ts @@ -1,21 +1,21 @@ -export enum ruleDirection { HORIZONTAL = 'horizontal', VERTICAL = 'vertical' } +export enum RuleDirection { HORIZONTAL = 'horizontal', VERTICAL = 'vertical' } -export interface pointConfig { +export interface PointConfig { lineWidth: number; lineHeight: number; strokeStyle: CanvasRenderingContext2D['strokeStyle'] font: CanvasRenderingContext2D['font'] } -export interface RulerConf { +export interface RulerConfig { width: number; height: number; scale: number; unitInterval: number; - showUnitLabel: boolean; - unitLabelStyle: CanvasRenderingContext2D['strokeStyle'], + showTickLabel: boolean; + tickLabelStyle: CanvasRenderingContext2D['strokeStyle'], background: CanvasRenderingContext2D['fillStyle'], - direction: ruleDirection, + direction: RuleDirection, container?: HTMLDivElement, startNumber: number | string } @@ -24,46 +24,46 @@ export default class RulerConstructor { private canvas: HTMLCanvasElement = document.createElement('canvas') - // canvas width + /** canvas 宽度 */ public width: number = 0 - // canvas height + /** canvas 高度 */ private height: number = 0 - // line width + /** 线宽度 */ private lineWidth: number = 0.5 - // line height + /** 线高度 */ private lineHeight: number = 10 - // unit interval size + /** 单位区间 */ private unitInterval: number = 10 - // show unit label - private showUnitLabel: boolean = true + /** 是否显示刻度值 */ + private showTickLabel: boolean = true - // direction - public direction: ruleDirection = ruleDirection.HORIZONTAL + /** 方向 */ + public direction: RuleDirection = RuleDirection.HORIZONTAL - // scale + /** 当前缩放大小 */ private scale = 1 - // unit label style - private unitLabelStyle: CanvasRenderingContext2D['strokeStyle'] = '#333333' + /** 刻度值的颜色 */ + private tickLabelStyle: CanvasRenderingContext2D['strokeStyle'] = '#333333' - // stroke style + /** 刻度的颜色 */ private strokeStyle: CanvasRenderingContext2D['strokeStyle'] = '#b8b7b8' - // font + /** 文本 */ private font: CanvasRenderingContext2D['font'] = '10px sans-serif' - // background style + /** canvas背景 */ private background: CanvasRenderingContext2D['fillStyle'] = '#ffffff' - // 从什么数字开始 + /** 从什么数字开始计算刻度的值 */ private startNumber: number = 0 - // 包裹这个ruler canvas的容器是啥 + /** 包裹这个ruler canvas的容器是啥 */ public container?: HTMLElement constructor(config) { @@ -108,7 +108,7 @@ export default class RulerConstructor { if (unitInterval < 1) { unitInterval = +(this.unitInterval / this.scale).toFixed(4) } - const showUnitLabel = this.showUnitLabel + const showTickLabel = this.showTickLabel const lineWidth = this.lineWidth const lineHeight = this.lineHeight const width = this.width @@ -122,11 +122,11 @@ export default class RulerConstructor { context.beginPath(); context.fillStyle = this.background; context.fillRect(0, 0, width, height) - context.fillStyle = this.unitLabelStyle + context.fillStyle = this.tickLabelStyle for (let i = 0; i <= scaleCount; i++) { const step = Math.round(i * unitInterval) /* 竖向的时候, 因为旋转的原因, 以横向来想, 从右边开始绘制0, 左边为最大的数字 */ - let pos = this.direction === ruleDirection.HORIZONTAL ? step * this.scale : width - step * this.scale + let pos = this.direction === RuleDirection.HORIZONTAL ? step * this.scale : width - step * this.scale if (pos < 0) { pos = 0 } /* 当间隔 * 10 显示文本, 考虑增加配置 */ if (i % 10 === 0) { @@ -134,7 +134,7 @@ export default class RulerConstructor { const x = pos + m const xPos = x >= width ? width - m : x context.moveTo(xPos, 0); - if (showUnitLabel) { + if (showTickLabel) { const text = `${startNumber + step}` let x = pos + lineWidth + 2 // 文本不固定, 需要计算占用的大小 @@ -142,7 +142,7 @@ export default class RulerConstructor { // 对竖向0的文本,显示在最右边的偏左位置 if (this.direction === 'vertical' && !step) { x = width - textWidth - lineWidth - 2 - } else if (this.direction === ruleDirection.HORIZONTAL && (pos + textWidth) >= width) { + } else if (this.direction === RuleDirection.HORIZONTAL && (pos + textWidth) >= width) { // 横向最后一个数字同理 x = width - textWidth - lineWidth - 2 } diff --git a/packages/plugin/src/ruler/index.ts b/packages/plugin/src/ruler/index.ts index 09b92bb2cf7..b04c395aabf 100644 --- a/packages/plugin/src/ruler/index.ts +++ b/packages/plugin/src/ruler/index.ts @@ -2,10 +2,10 @@ import { modifyCSS, createDom } from '@antv/dom-util'; import { ICanvas } from '@antv/g-base'; import { IAbstractGraph as IGraph } from '@antv/g6-core'; import Base from '../base'; -import RulerConstructor, { pointConfig, RulerConf, ruleDirection } from './constructor'; +import RulerConstructor, { PointConfig, RulerConfig, RuleDirection } from './constructor'; -export interface RulerConfig extends pointConfig, RulerConf { - directions: ruleDirection[] | ruleDirection, // 方向, 默认['horizontal', 'vertical'] +export interface RulerSettings extends PointConfig, RulerConfig { + directions: RuleDirection[] | RuleDirection, // 方向, 默认['horizontal', 'vertical'] startLen: number // 开始长度 / 距离 showLock: boolean // 是否显示锁 lockColor: string // 锁颜色 @@ -13,7 +13,7 @@ export interface RulerConfig extends pointConfig, RulerConf { visible: boolean // 是否可见 monitorZoom: boolean // 监听缩放改变刻度 monitorSize: boolean // 监听大小改变刻度 - monitorDrag: boolean // 监听拖拽改变刻度 + monitorViewPort: boolean // 监听视口改变刻度 } interface EventFnObj { @@ -22,29 +22,29 @@ interface EventFnObj { export default class Ruler extends Base { - // 锁的容器DOM + /** 锁的容器DOM */ private lockContainer: HTMLElement - // 标尺的包裹DOM + /** 标尺的包裹DOM */ private rulerWrap: HTMLElement - // 所有标尺的实例 + /** 所有标尺的实例 */ public rulerInstances: RulerConstructor[] = [] - // 缓存行为的是否执行函数 + /** 缓存行为的是否执行函数 */ private shouldBeginFnObj: Object = {} - // 锁定会控制哪些行为 + /** 锁定会控制哪些行为 */ private lockBehaviorKey: Array = ['zoom-canvas', 'drag-canvas', 'scroll-canvas'] - // 所有绑定的事件函数, 改变this + /** 所有绑定的事件函数, 改变this */ private eventFnObj: EventFnObj = {} - constructor(config?: RulerConfig) { + constructor(config?: RulerSettings) { super(config); } - public getDefaultCfgs(): RulerConfig { + public getDefaultCfgs(): RulerSettings { return { lineWidth: 1, lineHeight: 10, @@ -53,21 +53,21 @@ export default class Ruler extends Base { width: 0, height: 25, unitInterval: 10, - showUnitLabel: true, + showTickLabel: true, lock: false, - unitLabelStyle: '#333333', + tickLabelStyle: '#333333', strokeStyle: '#b8b7b8', font: '10px sans-serif', - directions: [ruleDirection.HORIZONTAL, ruleDirection.VERTICAL], + directions: [RuleDirection.HORIZONTAL, RuleDirection.VERTICAL], showLock: true, lockColor: '#7F7F7F', visible: true, monitorZoom: true, monitorSize: true, - monitorDrag: true, + monitorViewPort: true, background: '#ffffff', startNumber: 0, - direction: ruleDirection.HORIZONTAL + direction: RuleDirection.HORIZONTAL }; } @@ -81,11 +81,13 @@ export default class Ruler extends Base { const showLock = this.get('showLock') const monitorSize = this.get('monitorSize') const monitorZoom = this.get('monitorZoom') - const monitorDrag = this.get('monitorDrag') + const monitorViewPort = this.get('monitorViewPort') + + this.set('scale', graph.getZoom()) let startLen = this.get('startLen') let directions = this.get('directions') - const startNumber = this.get('startNumber') + let rulerHeight = height this.rulerWrap = createDom( @@ -107,7 +109,7 @@ export default class Ruler extends Base { directions.forEach(direction => { const rulerMaxWidth = this.getRulerMaxWidth(direction) // 竖向需要增加线的宽度, 跟横向的第一条线对齐 - if (direction === ruleDirection.VERTICAL) { + if (direction === RuleDirection.VERTICAL) { rulerHeight += lineWidth } const container = createDom( @@ -115,6 +117,7 @@ export default class Ruler extends Base { ); this.rulerWrap.append(container) + const startNumber = this.getStartNumber(direction) const rulerInstance = new RulerConstructor({ ...this._cfgs, width: rulerMaxWidth, @@ -125,7 +128,7 @@ export default class Ruler extends Base { }) this.rulerInstances.push(rulerInstance) const ruleCanvas = rulerInstance.getCanvas() - if (direction === ruleDirection.HORIZONTAL) { + if (direction === RuleDirection.HORIZONTAL) { modifyCSS(container, { left: `${startLen}px`, top: 0, @@ -165,7 +168,7 @@ export default class Ruler extends Base { graph.on('wheelzoom', this.eventFnObj.wheelzoom) } - if (monitorDrag) { + if (monitorViewPort) { this.eventFnObj.viewportchange = this.bindViewPortFn.bind(this) graph.on('viewportchange', this.eventFnObj.viewportchange) } @@ -174,12 +177,12 @@ export default class Ruler extends Base { graphContainer.insertBefore(this.rulerWrap, canvas) } - private getStartNumber(direction: ruleDirection) { + private getStartNumber(direction: RuleDirection) { const graph: IGraph = this.get('graph'); const scale = this.get('scale') const canvas: HTMLCanvasElement = graph.get('canvas').get('el'); - const maxWidth = direction === ruleDirection.HORIZONTAL ? canvas.width : canvas.height - const directionKey = direction === ruleDirection.HORIZONTAL ? 'x' : 'y' + const maxWidth = direction === RuleDirection.HORIZONTAL ? canvas.width : canvas.height + const directionKey = direction === RuleDirection.HORIZONTAL ? 'x' : 'y' const centerLoc = this.get('graph').getViewPortCenterPoint() const startNumber = centerLoc[directionKey] - (maxWidth / scale) / 2 return Math.round(startNumber) @@ -195,7 +198,7 @@ export default class Ruler extends Base { const width = graphContainer.clientWidth - startLen const lineWidth = this.get('lineWidth') let rulerMaxWidth = canvas.clientWidth < width ? canvas.clientWidth : width - if (direction === ruleDirection.VERTICAL) { + if (direction === RuleDirection.VERTICAL) { // 增加top的部分 const vWidth = graphContainer.clientHeight - startLen + lineWidth rulerMaxWidth = canvas.clientHeight < vWidth ? canvas.clientHeight : vWidth @@ -220,10 +223,10 @@ export default class Ruler extends Base { let width = canvas.width let height = canvas.height - /* 兼容只有一个尺子的情况下 */ + /** 兼容只有一个尺子的情况下 */ if (this.rulerInstances.length && this.rulerInstances.length < 2) { const direction = this.rulerInstances[0].direction - if (direction === ruleDirection.HORIZONTAL) { + if (direction === RuleDirection.HORIZONTAL) { left = 0 } else { top = 0 @@ -266,25 +269,26 @@ export default class Ruler extends Base { const graph: IGraph = this.get('graph'); const background = this.get('background') const startLen = this.get('startLen') - const lock = this.get('lock') + let lock = this.get('lock') const lineWidth = this.get('lineWidth') const modeController = graph.get('modeController') - const lockFlagArr = [] this.updateShouldBegin() // 初始锁定状态 this.lockBehaviorKey.forEach(key => { - const behavior = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === key)?.[0]; + const behavior = modeController?.currentBehaves?.find(behavior => behavior.type === key); if (behavior) { if (behavior.shouldBegin) { lockFlagArr.push(!behavior.shouldBegin()) + } else { + lockFlagArr.push(false) } - lockFlagArr.push(false) } }) - this.set('lock', lockFlagArr.some(flag => flag)) + lock = lockFlagArr.some(flag => flag) + this.set('lock', lock) this.lockContainer = createDom( `
`, @@ -351,7 +355,7 @@ export default class Ruler extends Base { const graph: IGraph = this.get('graph') const modeController = graph.get('modeController') this.lockBehaviorKey.forEach(key => { - const behavior = modeController?.modes[modeController.mode]?.filter(behavior => behavior.type === key)?.[0]; + const behavior = modeController?.currentBehaves?.find(behavior => behavior.type === key) if (behavior) { if (behavior.shouldBegin) { this.shouldBeginFnObj[key] = behavior.shouldBegin @@ -399,7 +403,6 @@ export default class Ruler extends Base { } public changeLock(flag: boolean) { - const graph: IGraph = this.get('graph'); const LOCK_SVG_DOM = this.lockContainer.querySelector('.lock-svg') as HTMLElement const UNLOCK_SVG_DOM = this.lockContainer.querySelector('.unLock-svg') as HTMLElement diff --git a/packages/site/docs/api/Plugins.en.md b/packages/site/docs/api/Plugins.en.md index 084bd484ea1..3bd1ce12129 100644 --- a/packages/site/docs/api/Plugins.en.md +++ b/packages/site/docs/api/Plugins.en.md @@ -104,19 +104,16 @@ The Ruler plugin in G6 can be used to draw rulers on the canvas. It provides var | width | number | Default width based on canvas width for horizontal, canvas height for vertical (including `startLen`) | | height | number | Height of the ruler, default is `25`. Takes the maximum of startLen and height when both are present | | startLen | number | Starting position of the ruler, default is `25`. Takes the maximum of startLen and height when both are present | -| scale | number | Current scale size | | unitInterval | number | Unit interval | -| showUnitLabel | boolean | Whether to show unit interval labels | -| unitLabelStyle | CanvasRenderingContext2D['strokeStyle'] | Color of the unit labels | +| showTickLabel | boolean | Whether to show unit interval labels | +| tickLabelStyle | CanvasRenderingContext2D['strokeStyle'] | Color of the unit labels | | font | CanvasRenderingContext2D['font'] | Font for the text | -| visible | boolean | Visibility, default is true | | lineWidth | number | Width of the lines | | lineHeight | number | Height of the lines | | strokeStyle | CanvasRenderingContext2D['strokeStyle'] | Color of the lines | | background | CanvasRenderingContext2D['fillStyle'] | The color of the ruler | | showLock | number | Whether to show the lock icon | | lockColor | string | Color of the lock icon | -| lock | boolean | Whether to lock | | monitorZoom | boolean | Whether the `wheelzoom` event changes the scale | | monitorSize | boolean |Whether the `changeSize` event changes the size of the ruler | | monitorViewPort | boolean | Whether the `viewportchange` event changes the scale | diff --git a/packages/site/docs/api/Plugins.zh.md b/packages/site/docs/api/Plugins.zh.md index 38af658ff96..f6f7fac809c 100644 --- a/packages/site/docs/api/Plugins.zh.md +++ b/packages/site/docs/api/Plugins.zh.md @@ -111,19 +111,16 @@ Ruler 插件在画布上绘制了标尺。通过左上角的锁还可以锁定 | directions | ruleDirection[] 或者 ruleDirection | 方向, `horizontal`代表横向, `vertical`代表纵向, 默认`['horizontal', 'vertical']`| | height | number | 尺子的高度, 默认是`25`, 当存在 startLen 和 height 会取2个最大的值 | | startLen | number | 尺子的开始位置, 默认`25`, 当存在 startLen 和 height 会取2个最大的值 | -| scale | number | 当前缩放的大小 | | unitInterval | number | 单位间隔 | -| showUnitLabel | boolean | 是否单位间隔的文字 | -| unitLabelStyle | CanvasRenderingContext2D['strokeStyle'] | 单位文字的颜色 | +| showTickLabel | boolean | 是否单位间隔的文字 | +| tickLabelStyle | CanvasRenderingContext2D['strokeStyle'] | 单位文字的颜色 | | font | CanvasRenderingContext2D['font'] | 文字的字体 | -| visible | boolean | 是否可见, 默认true | | lineWidth | number | 线的宽度 | | lineHeight | number | 线的高度 | | strokeStyle | CanvasRenderingContext2D['strokeStyle'] | 线的颜色 | | background | CanvasRenderingContext2D['fillStyle'] | 尺子的颜色 | | showLock | number | 是否显示锁icon | | lockColor | string | 锁的颜色 | -| lock | boolean | 是否锁住 | | monitorZoom | boolean | `wheelzoom`, 事件是否改变刻度 | | monitorSize | boolean | `changeSize`, 是否改变尺子的大小 | | monitorViewPort | boolean | `viewportchange`, 事件是否改变刻度 | From 42a3a672eb3af5e57c76f616edab4b5f10cffbea Mon Sep 17 00:00:00 2001 From: dengzuming Date: Fri, 15 Dec 2023 17:01:53 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=94=B9=E5=8F=98?= =?UTF-8?q?canvas=E5=A4=A7=E5=B0=8F,=20=E7=AB=96=E5=90=91canvas=E9=AB=98?= =?UTF-8?q?=E5=BA=A6=E4=B8=8D=E5=AF=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/plugin/src/ruler/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/plugin/src/ruler/index.ts b/packages/plugin/src/ruler/index.ts index b04c395aabf..b742932cd5b 100644 --- a/packages/plugin/src/ruler/index.ts +++ b/packages/plugin/src/ruler/index.ts @@ -330,19 +330,19 @@ export default class Ruler extends Base { this.lockContainer.addEventListener('click', this.eventFnObj.lock) } - private resetRulerSize(height?) { + private resetRulerSize() { const graph: IGraph = this.get('graph'); - if (height) { - this.set('height', height) - } const canvas: HTMLDivElement = graph.get('canvas').get('el'); - const maxHeight = this.get('height') || canvas.clientHeight + const height = this.get('height') || canvas.clientHeight + const startLen = this.get('startLen') + const lineWidth = this.get('lineWidth') + const maxHeight = Math.max(height, startLen) this.rulerInstances.forEach((ruler: RulerConstructor) => { const rulerMaxWidth = this.getRulerMaxWidth(ruler.direction) const startNumber = this.getStartNumber(ruler.direction) ruler.changeConfig({ width: rulerMaxWidth, - height: maxHeight, + height: ruler.direction === RuleDirection.HORIZONTAL ? maxHeight : maxHeight + lineWidth, startNumber }) modifyCSS(ruler.container, {