From 65425ea4064e1e2224d7062dbd5740bcb0db8dd9 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 16 Apr 2024 21:29:21 +0800 Subject: [PATCH] refactor: toDataURL support export overall mode (#5648) * feat(canvas): toDataURL support by overall graph * test: update snapshots * test: add test case * refactor: adjust code --- .../g6/__tests__/demos/graph-to-data-url.ts | 11 ++-- .../elements/position-combo/default.svg | 2 +- .../layouts/combo-layout/combined.svg | 2 +- .../layouts/compact-box/left-align.svg | 2 +- .../layouts/compact-box/top-to-bottom.svg | 2 +- .../layouts/dagre/antv-flow-combo.svg | 2 +- .../snapshots/layouts/mindmap/h-left.svg | 2 +- .../snapshots/layouts/mindmap/h-right.svg | 2 +- .../snapshots/plugins/tooltip/edge.svg | 2 +- .../snapshots/plugins/tooltip/hover.svg | 2 +- .../snapshots/plugins/tooltip/node.svg | 2 +- .../plugins/tooltip/show-tooltip-by-id.svg | 2 +- .../unit/runtime/graph/graph.spec.ts | 6 ++- packages/g6/src/runtime/canvas.ts | 51 ++++++++++++++++--- packages/g6/src/runtime/graph.ts | 18 ++++++- 15 files changed, 83 insertions(+), 25 deletions(-) diff --git a/packages/g6/__tests__/demos/graph-to-data-url.ts b/packages/g6/__tests__/demos/graph-to-data-url.ts index 562cdeb2397..d5cd0a96240 100644 --- a/packages/g6/__tests__/demos/graph-to-data-url.ts +++ b/packages/g6/__tests__/demos/graph-to-data-url.ts @@ -16,13 +16,14 @@ export const graphToDataURL: TestCase = async (context) => { graphToDataURL.form = (panel) => { const config = { toDataURL: () => { - graph.toDataURL().then((url) => { + graph.toDataURL({ mode: config.mode } as any).then((url) => { navigator.clipboard.writeText(url); alert('The data URL has been copied to the clipboard'); }); }, + mode: 'viewport', download: async () => { - const dataURL = await graph.toDataURL(); + const dataURL = await graph.toDataURL({ mode: config.mode } as any); const [head, content] = dataURL.split(','); const contentType = head.match(/:(.*?);/)![1]; @@ -43,7 +44,11 @@ export const graphToDataURL: TestCase = async (context) => { a.click(); }, }; - return [panel.add(config, 'toDataURL'), panel.add(config, 'download').name('Download')]; + return [ + panel.add(config, 'toDataURL'), + panel.add(config, 'mode', ['viewport', 'overall']), + panel.add(config, 'download').name('Download'), + ]; }; await graph.render(); diff --git a/packages/g6/__tests__/snapshots/elements/position-combo/default.svg b/packages/g6/__tests__/snapshots/elements/position-combo/default.svg index bb03aeaa901..ae187cc7ed2 100644 --- a/packages/g6/__tests__/snapshots/elements/position-combo/default.svg +++ b/packages/g6/__tests__/snapshots/elements/position-combo/default.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/layouts/combo-layout/combined.svg b/packages/g6/__tests__/snapshots/layouts/combo-layout/combined.svg index 55ab1a8f816..32d9b642cf0 100644 --- a/packages/g6/__tests__/snapshots/layouts/combo-layout/combined.svg +++ b/packages/g6/__tests__/snapshots/layouts/combo-layout/combined.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/layouts/compact-box/left-align.svg b/packages/g6/__tests__/snapshots/layouts/compact-box/left-align.svg index 772cf42a91b..c6e681f3b5e 100644 --- a/packages/g6/__tests__/snapshots/layouts/compact-box/left-align.svg +++ b/packages/g6/__tests__/snapshots/layouts/compact-box/left-align.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/layouts/compact-box/top-to-bottom.svg b/packages/g6/__tests__/snapshots/layouts/compact-box/top-to-bottom.svg index 2e3d9ff7365..6a82e7e4914 100644 --- a/packages/g6/__tests__/snapshots/layouts/compact-box/top-to-bottom.svg +++ b/packages/g6/__tests__/snapshots/layouts/compact-box/top-to-bottom.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/layouts/dagre/antv-flow-combo.svg b/packages/g6/__tests__/snapshots/layouts/dagre/antv-flow-combo.svg index f9b1447c15f..a998225a889 100644 --- a/packages/g6/__tests__/snapshots/layouts/dagre/antv-flow-combo.svg +++ b/packages/g6/__tests__/snapshots/layouts/dagre/antv-flow-combo.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/layouts/mindmap/h-left.svg b/packages/g6/__tests__/snapshots/layouts/mindmap/h-left.svg index 2d60d5a6b99..fc02236a0d6 100644 --- a/packages/g6/__tests__/snapshots/layouts/mindmap/h-left.svg +++ b/packages/g6/__tests__/snapshots/layouts/mindmap/h-left.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/layouts/mindmap/h-right.svg b/packages/g6/__tests__/snapshots/layouts/mindmap/h-right.svg index 350ee3eaa94..47609c22cb2 100644 --- a/packages/g6/__tests__/snapshots/layouts/mindmap/h-right.svg +++ b/packages/g6/__tests__/snapshots/layouts/mindmap/h-right.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/tooltip/edge.svg b/packages/g6/__tests__/snapshots/plugins/tooltip/edge.svg index 44cb21773f1..98914cc51a4 100644 --- a/packages/g6/__tests__/snapshots/plugins/tooltip/edge.svg +++ b/packages/g6/__tests__/snapshots/plugins/tooltip/edge.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/tooltip/hover.svg b/packages/g6/__tests__/snapshots/plugins/tooltip/hover.svg index 44cb21773f1..98914cc51a4 100644 --- a/packages/g6/__tests__/snapshots/plugins/tooltip/hover.svg +++ b/packages/g6/__tests__/snapshots/plugins/tooltip/hover.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/tooltip/node.svg b/packages/g6/__tests__/snapshots/plugins/tooltip/node.svg index 44cb21773f1..98914cc51a4 100644 --- a/packages/g6/__tests__/snapshots/plugins/tooltip/node.svg +++ b/packages/g6/__tests__/snapshots/plugins/tooltip/node.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/tooltip/show-tooltip-by-id.svg b/packages/g6/__tests__/snapshots/plugins/tooltip/show-tooltip-by-id.svg index 44cb21773f1..98914cc51a4 100644 --- a/packages/g6/__tests__/snapshots/plugins/tooltip/show-tooltip-by-id.svg +++ b/packages/g6/__tests__/snapshots/plugins/tooltip/show-tooltip-by-id.svg @@ -1,6 +1,6 @@ - + diff --git a/packages/g6/__tests__/unit/runtime/graph/graph.spec.ts b/packages/g6/__tests__/unit/runtime/graph/graph.spec.ts index ec6fecf96ee..b32035d35e9 100644 --- a/packages/g6/__tests__/unit/runtime/graph/graph.spec.ts +++ b/packages/g6/__tests__/unit/runtime/graph/graph.spec.ts @@ -313,8 +313,10 @@ describe('Graph', () => { }); it('toDataURL', async () => { - const url = await graph.toDataURL(); - expect(url).toBeDefined(); + expect(await graph.toDataURL()).toBeDefined(); + expect(await graph.toDataURL({ mode: 'overall' })).toBeDefined(); + expect((await graph.toDataURL({ type: 'image/jpeg' })).startsWith('data:image/jpeg')).toBe(true); + expect((await graph.toDataURL({ type: 'image/png' })).startsWith('data:image/png')).toBe(true); }); it('resize', () => { diff --git a/packages/g6/src/runtime/canvas.ts b/packages/g6/src/runtime/canvas.ts index e122589c68b..eb16657dc7a 100644 --- a/packages/g6/src/runtime/canvas.ts +++ b/packages/g6/src/runtime/canvas.ts @@ -1,8 +1,8 @@ import type { Cursor, - DataURLOptions, DisplayObject, CanvasConfig as GCanvasConfig, + DataURLOptions as GDataURLOptions, IAnimation, IRenderer, PointLike, @@ -13,7 +13,7 @@ import { Plugin as DragNDropPlugin } from '@antv/g-plugin-dragndrop'; import { createDOM, isFunction, isString } from '@antv/util'; import type { CanvasOptions } from '../spec/canvas'; import type { CanvasLayer } from '../types/canvas'; -import { getCombinedBBox } from '../utils/bbox'; +import { getBBoxSize, getCombinedBBox } from '../utils/bbox'; export interface CanvasConfig extends Pick { @@ -21,6 +21,19 @@ export interface CanvasConfig background?: string; } +export interface DataURLOptions extends GDataURLOptions { + /** + * 导出模式 + * - viewport: 导出视口内容 + * - overall: 导出整个画布 + * + * export mode + * - viewport: export the content of the viewport + * - overall: export the entire canvas + */ + mode?: 'viewport' | 'overall'; +} + /** * @deprecated this canvas will be replace by layered canvas */ @@ -173,7 +186,7 @@ export class Canvas { Object.values(this.canvas) .map((canvas) => canvas.document.documentElement) .filter((el) => el.childNodes.length > 0) - .map((el) => el.getBounds()), + .map((el) => el.getRenderBounds()), ); } @@ -211,7 +224,21 @@ export class Canvas { public async toDataURL(options: Partial = {}) { const devicePixelRatio = window.devicePixelRatio || 1; - const { width, height } = this.config; + const { mode = 'viewport', ...restOptions } = options; + + let startX = 0; + let startY = 0; + let width = 0; + let height = 0; + + if (mode === 'viewport') { + [width, height] = [this.config.width || 0, this.config.height || 0]; + } else if (mode === 'overall') { + const bounds = this.getBounds(); + const size = getBBoxSize(bounds); + [startX, startY] = bounds.min; + [width, height] = size; + } const container: HTMLElement = createDOM('
'); @@ -244,15 +271,23 @@ export class Canvas { const camera = this.main.getCamera(); const offscreenCamera = offscreenCanvas.getCamera(); - offscreenCamera.setZoom(camera.getZoom()); - offscreenCamera.setPosition(camera.getPosition()); - offscreenCamera.setFocalPoint(camera.getFocalPoint()); + + if (mode === 'viewport') { + offscreenCamera.setZoom(camera.getZoom()); + offscreenCamera.setPosition(camera.getPosition()); + offscreenCamera.setFocalPoint(camera.getFocalPoint()); + } else if (mode === 'overall') { + const [x, y, z] = offscreenCamera.getPosition(); + const [fx, fy, fz] = offscreenCamera.getFocalPoint(); + offscreenCamera.setPosition([x + startX, y + startY, z]); + offscreenCamera.setFocalPoint([fx + startX, fy + startY, fz]); + } const contextService = offscreenCanvas.getContextService(); return new Promise((resolve) => { offscreenCanvas.on(CanvasEvent.AFTER_RENDER, async () => { - const url = await contextService.toDataURL(options); + const url = await contextService.toDataURL(restOptions); resolve(url); }); }); diff --git a/packages/g6/src/runtime/graph.ts b/packages/g6/src/runtime/graph.ts index 362cb911cd6..5e68c2cdfd6 100644 --- a/packages/g6/src/runtime/graph.ts +++ b/packages/g6/src/runtime/graph.ts @@ -1,5 +1,5 @@ import EventEmitter from '@antv/event-emitter'; -import type { AABB, BaseStyleProps, DataURLOptions } from '@antv/g'; +import type { AABB, BaseStyleProps } from '@antv/g'; import type { ID } from '@antv/graphlib'; import { debounce, isEqual, isFunction, isNumber, isObject, isString, omit } from '@antv/util'; import { COMBO_KEY, GraphEvent } from '../constants'; @@ -49,6 +49,7 @@ import { zIndexOf } from '../utils/style'; import { subtract } from '../utils/vector'; import { BatchController } from './batch'; import { BehaviorController } from './behavior'; +import type { DataURLOptions } from './canvas'; import { Canvas } from './canvas'; import type { HierarchyKey } from './data'; import { DataController } from './data'; @@ -1051,6 +1052,21 @@ export class Graph extends EventEmitter { else if (elementType === 'combo') this.updateComboData([{ id, style: { collapsed } }]); } + /** + * 导出画布内容为 DataURL + * + * Export canvas content as DataURL + * @param options - 导出配置 | export configuration + * @param mode + * 导出模式 + * - viewport: 导出视口内容 + * - overall: 导出整个画布 + * + * export mode + * - viewport: export the content of the viewport + * - overall: export the entire canvas + * @returns DataURL | DataURL + */ public async toDataURL(options: Partial = {}): Promise { return this.context.canvas!.toDataURL(options); }