From 74c19e903e3b1a281bb08c24460e23584419a23f Mon Sep 17 00:00:00 2001 From: hustcc Date: Thu, 21 Mar 2024 20:12:31 +0800 Subject: [PATCH] feat: add plugin watermark (#5569) * chore: init type define * fix: badge typo in readme * feat: plugin watermark * feat: add watmermark plugin * test: add tc for watermark, but should skip it * docs: add demos for watermark * test: add tc for mock * chore: fix cr * test: use jest-canvas-mock make ci works * chore: update demo * docs: add case panel * refactor: render watermark into div * chore: update --- README.en-US.md | 2 +- README.md | 2 +- packages/g6/__tests__/demo/case/index.ts | 2 + .../demo/case/plugin-watermark-image.ts | 47 +++++++++ .../__tests__/demo/case/plugin-watermark.ts | 60 ++++++++++++ packages/g6/__tests__/setup.ts | 1 + .../unit/plugins/plugin-watermark.spec.ts | 33 +++++++ packages/g6/__tests__/unit/utils/dom.spec.ts | 15 ++- packages/g6/package.json | 1 + packages/g6/src/plugins/grid-line.ts | 21 ++-- packages/g6/src/plugins/index.ts | 2 + packages/g6/src/plugins/watermark.ts | 98 +++++++++++++++++++ packages/g6/src/registry/build-in.ts | 3 +- packages/g6/src/utils/dom.ts | 21 ++++ packages/g6/src/utils/watermark.ts | 93 ++++++++++++++++++ .../tool/watermarker/demo/background.ts | 41 ++++++++ .../tool/watermarker/demo/imgWaterMarker.ts | 37 ------- .../tool/watermarker/demo/imgWatermark.ts | 36 +++++++ .../examples/tool/watermarker/demo/meta.json | 16 ++- .../tool/watermarker/demo/textWaterMarker.ts | 42 -------- .../tool/watermarker/demo/textWatermark.ts | 37 +++++++ 21 files changed, 509 insertions(+), 101 deletions(-) create mode 100644 packages/g6/__tests__/demo/case/plugin-watermark-image.ts create mode 100644 packages/g6/__tests__/demo/case/plugin-watermark.ts create mode 100644 packages/g6/__tests__/unit/plugins/plugin-watermark.spec.ts create mode 100644 packages/g6/src/plugins/watermark.ts create mode 100644 packages/g6/src/utils/watermark.ts create mode 100644 packages/site/examples/tool/watermarker/demo/background.ts delete mode 100644 packages/site/examples/tool/watermarker/demo/imgWaterMarker.ts create mode 100644 packages/site/examples/tool/watermarker/demo/imgWatermark.ts delete mode 100644 packages/site/examples/tool/watermarker/demo/textWaterMarker.ts create mode 100644 packages/site/examples/tool/watermarker/demo/textWatermark.ts diff --git a/README.en-US.md b/README.en-US.md index 89d94b0af11..4e98b558092 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -9,7 +9,7 @@ ![](https://user-images.githubusercontent.com/6113694/45008751-ea465300-b036-11e8-8e2a-166cbb338ce2.png) -[![npm Version](https://img.shields.io/npm/v/@antv/g6.svg@beta)](https://www.npmjs.com/package/@antv/g6) +[![npm Version](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6) [![Build Status](https://github.com/antvis/g6/workflows/build/badge.svg?branch=v5)](https://github.com/antvis/g6/actions) [![Coverage Status](https://coveralls.io/repos/github/antvis/G6/badge.svg)](https://coveralls.io/github/antvis/G6) [![npm Download](https://img.shields.io/npm/dm/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6) diff --git a/README.md b/README.md index 0d9088907d6..30f3b5c3ae1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ![](https://user-images.githubusercontent.com/6113694/45008751-ea465300-b036-11e8-8e2a-166cbb338ce2.png) -[![npm Version](https://img.shields.io/npm/v/@antv/g6.svg@beta)](https://www.npmjs.com/package/@antv/g6) +[![npm Version](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6) [![Build Status](https://github.com/antvis/g6/workflows/build/badge.svg?branch=v5)](https://github.com/antvis/g6/actions) [![Coverage Status](https://coveralls.io/repos/github/antvis/G6/badge.svg)](https://coveralls.io/github/antvis/G6) [![npm Download](https://img.shields.io/npm/dm/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6) diff --git a/packages/g6/__tests__/demo/case/index.ts b/packages/g6/__tests__/demo/case/index.ts index b4edf0cb217..20ee1cb51e4 100644 --- a/packages/g6/__tests__/demo/case/index.ts +++ b/packages/g6/__tests__/demo/case/index.ts @@ -43,5 +43,7 @@ export * from './layout-radial-prevent-overlap'; export * from './layout-radial-prevent-overlap-unstrict'; export * from './layout-radial-sort'; export * from './plugin-grid-line'; +export * from './plugin-watermark'; +export * from './plugin-watermark-image'; export * from './theme'; export * from './viewport-fit'; diff --git a/packages/g6/__tests__/demo/case/plugin-watermark-image.ts b/packages/g6/__tests__/demo/case/plugin-watermark-image.ts new file mode 100644 index 00000000000..5a952d65854 --- /dev/null +++ b/packages/g6/__tests__/demo/case/plugin-watermark-image.ts @@ -0,0 +1,47 @@ +import { Graph } from '@/src'; +import data from '@@/dataset/cluster.json'; +import type { STDTestCase } from '../types'; + +export const pluginWatermarkImage: STDTestCase = async (context) => { + const graph = new Graph({ + ...context, + autoResize: true, + data, + layout: { type: 'd3force' }, + plugins: [ + { + type: 'watermark', + width: 100, + height: 100, + imageURL: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*7svFR6wkPMoAAAAAAAAAAAAADmJ7AQ/original', + }, + ], + }); + + await graph.render(); + + pluginWatermarkImage.form = (panel) => { + const config = { + width: 200, + height: 100, + fontSize: 20, + textFill: 'red', + }; + return [ + panel + .add(config, 'width', 150, 400, 10) + .name('Width') + .onChange(() => {}), + panel + .add(config, 'height', 100, 200, 10) + .name('Width') + .onChange(() => {}), + panel + .add(config, 'fontSize', 10, 32, 1) + .name('FontSize') + .onChange(() => {}), + ]; + }; + + return graph; +}; diff --git a/packages/g6/__tests__/demo/case/plugin-watermark.ts b/packages/g6/__tests__/demo/case/plugin-watermark.ts new file mode 100644 index 00000000000..192b487ae59 --- /dev/null +++ b/packages/g6/__tests__/demo/case/plugin-watermark.ts @@ -0,0 +1,60 @@ +import { Graph, PluginOptions } from '@/src'; +import data from '@@/dataset/cluster.json'; +import { isObject } from '@antv/util'; +import type { STDTestCase } from '../types'; + +export const pluginWatermark: STDTestCase = async (context) => { + const graph = new Graph({ + ...context, + autoResize: true, + data, + layout: { type: 'd3force' }, + plugins: [ + { + type: 'watermark', + text: 'hello, \na watermark.', + textFontSize: 12, + }, + ], + }); + + await graph.render(); + + function updatePlugin(type: string, config: object) { + return (plugins: PluginOptions) => { + return plugins.map((plugin) => { + if (isObject(plugin) && plugin.type === type) return { ...plugin, ...config }; + return plugin; + }); + }; + } + pluginWatermark.form = (panel) => { + const config = { + width: 200, + height: 100, + textFontSize: 12, + }; + return [ + panel + .add(config, 'width', 150, 400, 10) + .name('Width') + .onChange((width: number) => { + graph.setPlugins(updatePlugin('watermark', { width })); + }), + panel + .add(config, 'height', 100, 200, 10) + .name('Height') + .onChange((height: number) => { + graph.setPlugins(updatePlugin('watermark', { height })); + }), + panel + .add(config, 'textFontSize', 10, 32, 1) + .name('TextFontSize') + .onChange((textFontSize: number) => { + graph.setPlugins(updatePlugin('watermark', { textFontSize })); + }), + ]; + }; + + return graph; +}; diff --git a/packages/g6/__tests__/setup.ts b/packages/g6/__tests__/setup.ts index f2f82d14ae7..e84b9397aca 100644 --- a/packages/g6/__tests__/setup.ts +++ b/packages/g6/__tests__/setup.ts @@ -1,3 +1,4 @@ import '@/src/preset'; +import 'jest-canvas-mock'; import './utils/to-be-close-to'; import './utils/use-snapshot-matchers'; diff --git a/packages/g6/__tests__/unit/plugins/plugin-watermark.spec.ts b/packages/g6/__tests__/unit/plugins/plugin-watermark.spec.ts new file mode 100644 index 00000000000..35ddead2a18 --- /dev/null +++ b/packages/g6/__tests__/unit/plugins/plugin-watermark.spec.ts @@ -0,0 +1,33 @@ +import { pluginWatermark, pluginWatermarkImage } from '@@/demo/case'; +import { createDemoGraph } from '@@/utils'; + +describe('plugin watermark', () => { + it('watermark text', async () => { + const graph = await createDemoGraph(pluginWatermark); + const container = graph.getCanvas().getContainer()!; + + const el = container.querySelector('.g6-watermark') as HTMLDivElement; + + expect(graph.getPlugins()).toEqual([{ type: 'watermark', text: 'hello, \na watermark.', textFontSize: 12 }]); + expect(el.style.backgroundImage).toContain('data:image/png;base64'); + + await graph.destroy(); + expect(container.querySelector('.g6-watermark')).toBeFalsy(); + }); + + it('watermark image', async () => { + const graph = await createDemoGraph(pluginWatermarkImage); + const container = graph.getCanvas().getContainer()!; + + expect(graph.getPlugins()).toEqual([ + { + type: 'watermark', + width: 100, + height: 100, + imageURL: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*7svFR6wkPMoAAAAAAAAAAAAADmJ7AQ/original', + }, + ]); + await graph.destroy(); + expect(container.querySelector('.g6-watermark')).toBeFalsy(); + }); +}); diff --git a/packages/g6/__tests__/unit/utils/dom.spec.ts b/packages/g6/__tests__/unit/utils/dom.spec.ts index 7d2caf2a646..ad3843e52e3 100644 --- a/packages/g6/__tests__/unit/utils/dom.spec.ts +++ b/packages/g6/__tests__/unit/utils/dom.spec.ts @@ -1,4 +1,4 @@ -import { sizeOf } from '@/src/utils/dom'; +import { createPluginContainer, sizeOf } from '@/src/utils/dom'; describe('sizeOf', () => { it('should return the size of the graph container', () => { @@ -13,4 +13,17 @@ describe('sizeOf', () => { // Assert the result expect(result).toEqual([500, 300]); }); + + it('createPluginContainer', () => { + const el = createPluginContainer('test'); + expect(el.style.position).toBe('absolute'); + expect(el.style.display).toBe('block'); + expect(el.style.top).toBe('0px'); + expect(el.style.left).toBe('0px'); + expect(el.style.height).toBe('100%'); + expect(el.style.width).toBe('100%'); + expect(el.style.overflow).toBe('hidden'); + expect(el.style.pointerEvents).toBe('none'); + expect(el.getAttribute('class')).toBe('g6-test'); + }); }); diff --git a/packages/g6/package.json b/packages/g6/package.json index 2abc130a748..aebbe3cfafd 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -67,6 +67,7 @@ "@antv/layout-gpu": "^1.1.5", "@antv/layout-wasm": "^1.4.0", "@types/xmlserializer": "^0.6.6", + "jest-canvas-mock": "^2.5.1", "jest-random-mock": "^1.0.0", "lil-gui": "^0.19.2", "stats.js": "^0.17.0", diff --git a/packages/g6/src/plugins/grid-line.ts b/packages/g6/src/plugins/grid-line.ts index 5ad1b87c5bd..42e2484c431 100644 --- a/packages/g6/src/plugins/grid-line.ts +++ b/packages/g6/src/plugins/grid-line.ts @@ -2,6 +2,7 @@ import { BaseStyleProps } from '@antv/g'; import { GraphEvent } from '../constants'; import type { RuntimeContext } from '../runtime/types'; import { Point } from '../types'; +import { createPluginContainer } from '../utils/dom'; import { ViewportEvent } from '../utils/event'; import { add, mod } from '../utils/vector'; import type { BasePluginOptions } from './base-plugin'; @@ -35,13 +36,17 @@ export class GridLine extends BasePlugin { tickStrokeOpacity: 0.5, }; - private $element: HTMLElement = document.createElement('div'); + private $element: HTMLElement = createPluginContainer('grid-line'); private offset: Point = [0, 0]; constructor(context: RuntimeContext, options: GridLineOptions) { super(context, Object.assign({}, GridLine.defaultOptions, options)); - this.render(); + + const $container = this.context.canvas.getContainer()!; + $container.appendChild(this.$element); + + this.updateStyle(); this.bindEvents(); } @@ -55,22 +60,10 @@ export class GridLine extends BasePlugin { graph.on(GraphEvent.AFTER_TRANSFORM, this.onTransform); } - private render() { - const { canvas } = this.context; - const $container = canvas.getContainer(); - if (!$container) return; - - this.$element.className = 'g6-grid-line'; - this.updateStyle(); - $container.appendChild(this.$element); - } - private updateStyle() { const { size, stroke, lineWidth, border, borderLineWidth, borderStroke, borderStyle } = this.options; Object.assign(this.$element.style, { - width: '100%', - height: '100%', border: border ? `${borderLineWidth}px ${borderStyle} ${borderStroke}` : 'none', backgroundImage: `linear-gradient(${stroke} ${lineWidth}px, transparent ${lineWidth}px), linear-gradient(90deg, ${stroke} ${lineWidth}px, transparent ${lineWidth}px)`, backgroundSize: `${size}px ${size}px`, diff --git a/packages/g6/src/plugins/index.ts b/packages/g6/src/plugins/index.ts index c5093cc5f74..f82bab12e32 100644 --- a/packages/g6/src/plugins/index.ts +++ b/packages/g6/src/plugins/index.ts @@ -1,5 +1,7 @@ export { BasePlugin } from './base-plugin'; export { GridLine } from './grid-line'; +export { Watermark } from './watermark'; export type { BasePluginOptions } from './base-plugin'; export type { GridLineOptions } from './grid-line'; +export type { WatermarkOptions } from './watermark'; diff --git a/packages/g6/src/plugins/watermark.ts b/packages/g6/src/plugins/watermark.ts new file mode 100644 index 00000000000..fdc8f2452b5 --- /dev/null +++ b/packages/g6/src/plugins/watermark.ts @@ -0,0 +1,98 @@ +import type { RuntimeContext } from '../runtime/types'; +import { createPluginContainer } from '../utils/dom'; +import { getImageWatermark, getTextWateramrk } from '../utils/watermark'; +import type { BasePluginOptions } from './base-plugin'; +import { BasePlugin } from './base-plugin'; + +export type WatermarkOptions = BasePluginOptions & { + /** 单独一个水印的大小,这个水印最终会用来填充整个大小,所以 repeat 后的间距大小,通过这个 width height 设置 */ + width?: number; + height?: number; + /** 透明度 */ + opacity?: number; + /** 旋转角度 */ + rotate?: number; + /** 图片地址,如果有值,则使用,否则使用文本 */ + imageURL?: string; + /** 水印文本 */ + text?: string; + /** 文本水印的文本样式 */ + textFill: string; + textFontSize: number; + textFontFamily: string; + textFontWeight: string; + textFontVariant: string; + textAlign: CanvasTextAlign; + textBaseline: CanvasTextBaseline; + /** 背景的 CSS 样式 */ + backgroundAttachment: string; + backgroundBlendMode: string; + backgroundClip: string; + backgroundColor: string; + backgroundImage: string; + backgroundOrigin: string; + backgroundPosition: string; + backgroundPositionX: string; + backgroundPositionY: string; + backgroundRepeat: string; + backgroundSize: string; +}; + +/** + * 支持使用文本和图片作为水印,实现原理是在 Graph 容器的 div 上加上 background-image 属性,然后就可以通过 css 来控制水印的位置和样式。 + * 对于文本,会使用隐藏 canvas 转成图片的方式来实现。 + * Support using text and images as watermarks. + * The principle is to add the background-image property to the div of the Graph container, + * and then you can control the position and style of the watermark through css. For text, + * it will be converted to an image using a hidden canvas. + */ +export class Watermark extends BasePlugin { + static defaultOptions: Partial = { + width: 200, + height: 100, + opacity: 0.2, + rotate: Math.PI / 12, + textFill: '#000', + textFontSize: 16, + textAlign: 'center', + textBaseline: 'middle', + backgroundRepeat: 'repeat', + }; + + private $element: HTMLElement = createPluginContainer('watermark'); + + constructor(context: RuntimeContext, options: WatermarkOptions) { + super(context, Object.assign({}, Watermark.defaultOptions, options)); + + const $container = this.context.canvas.getContainer(); + $container!.appendChild(this.$element); + + this.update(options); + } + + public async update(options: Partial) { + super.update(options); + + const { width, height, text, imageURL, ...rest } = this.options; + + // Set the background style. + Object.keys(rest).forEach((key) => { + if (key.startsWith('background')) { + // @ts-expect-error ignore + this.$element.style[key] = options[key]; + } + }); + + // Set the background image. + const base64 = imageURL + ? await getImageWatermark(width, height, imageURL, rest) + : await getTextWateramrk(width, height, text, rest); + this.$element.style.backgroundImage = `url(${base64})`; + } + + public destroy(): void { + super.destroy(); + // Remove the background dom. + this.$element.remove(); + } +} diff --git a/packages/g6/src/registry/build-in.ts b/packages/g6/src/registry/build-in.ts index d12b62c95ec..61d402b436f 100644 --- a/packages/g6/src/registry/build-in.ts +++ b/packages/g6/src/registry/build-in.ts @@ -36,7 +36,7 @@ import { mindmap, } from '../layouts'; import { blues, greens, oranges, spectral } from '../palettes'; -import { GridLine } from '../plugins'; +import { GridLine, Watermark } from '../plugins'; import { dark, light } from '../themes'; import type { ExtensionRegistry } from './types'; @@ -107,5 +107,6 @@ export const BUILT_IN_EXTENSIONS: ExtensionRegistry = { }, plugin: { 'grid-line': GridLine, + watermark: Watermark, }, }; diff --git a/packages/g6/src/utils/dom.ts b/packages/g6/src/utils/dom.ts index 181b1129e00..27c43f83617 100644 --- a/packages/g6/src/utils/dom.ts +++ b/packages/g6/src/utils/dom.ts @@ -42,3 +42,24 @@ export function sizeOf(container: HTMLElement): [number, number] { Math.max(isNumber(effectiveHeight) ? effectiveHeight : MIN_CHART_HEIGHT, MIN_CHART_HEIGHT), ]; } + +/** + * Create a plugin DOM element. + * @param type - plugin type + * @returns plugin DOM element + */ +export function createPluginContainer(type: string) { + const el = document.createElement('div'); + + el.style.position = 'absolute'; + el.style.display = 'block'; + el.style.top = '0px'; + el.style.left = '0px'; + el.style.height = '100%'; + el.style.width = '100%'; + el.style.overflow = 'hidden'; + el.style.pointerEvents = 'none'; + el.setAttribute('class', `g6-${type}`); + + return el; +} diff --git a/packages/g6/src/utils/watermark.ts b/packages/g6/src/utils/watermark.ts new file mode 100644 index 00000000000..fa0b4f44d7e --- /dev/null +++ b/packages/g6/src/utils/watermark.ts @@ -0,0 +1,93 @@ +// Only use one instance. +let canvas: HTMLCanvasElement; + +/** + * Create a canvas instance. + * @param width - width + * @param height - height + * @returns a new Canvas + */ +function createCanvas(width: number, height: number): HTMLCanvasElement { + if (!canvas) { + canvas = document.createElement('canvas'); + } + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + ctx!.clearRect(0, 0, width, height); + return canvas; +} + +/** + * 从文本获取水印的 base64 + * @param width - width + * @param height - height + * @param text - 样式 + * @param style - 样式 + * @returns 水印的 base64 + */ +export async function getTextWateramrk(width: number, height: number, text: string, style: any) { + const canvas = createCanvas(width, height); + const ctx = canvas.getContext('2d')!; + + const { + rotate, + opacity, + textFill, + textFontSize, + textFontFamily, + textFontVariant, + textFontWeight, + textAlign, + textBaseline, + } = style; + + // Set the style. + // Default is align center and middle. + ctx.textAlign = textAlign; + ctx.textBaseline = textBaseline; + ctx.translate(width / 2, height / 2); + + ctx.font = `${textFontSize}px ${textFontFamily} ${textFontVariant} ${textFontWeight}`; + + rotate && ctx.rotate(rotate); + opacity && (ctx.globalAlpha = opacity); + if (textFill) { + ctx.fillStyle = textFill; + // Draw the text. + ctx.fillText(`${text}`, 0, 0); + } + + // Return the base64. + return canvas.toDataURL(); +} + +/** + * Get the image base64 of the watermark. + * @param width - width + * @param height - height + * @param imageURL - image URL + * @param style - 样式 + * @returns 水印的 base64 + */ +export async function getImageWatermark(width: number, height: number, imageURL: string, style: any) { + const canvas = createCanvas(width, height); + const ctx = canvas.getContext('2d')!; + const { rotate, opacity } = style; + + rotate && ctx.rotate(rotate); + opacity && (ctx.globalAlpha = opacity); + + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.src = imageURL; + + return new Promise((resolve) => { + img.onload = function () { + const sepX = width > img.width ? (width - img.width) / 2 : 0; + const sepY = height > img.height ? (height - img.height) / 2 : 0; + ctx.drawImage(img, 0, 0, img.width, img.height, sepX, sepY, width - sepX * 2, height - sepY * 2); + resolve(canvas.toDataURL()); + }; + }); +} diff --git a/packages/site/examples/tool/watermarker/demo/background.ts b/packages/site/examples/tool/watermarker/demo/background.ts new file mode 100644 index 00000000000..a5ecda26586 --- /dev/null +++ b/packages/site/examples/tool/watermarker/demo/background.ts @@ -0,0 +1,41 @@ +import { Graph } from '@antv/g6'; + +const data = { + nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }], + edges: [ + { source: 'node-0', target: 'node-1' }, + { source: 'node-0', target: 'node-2' }, + { source: 'node-0', target: 'node-3' }, + { source: 'node-0', target: 'node-4' }, + { source: 'node-1', target: 'node-0' }, + { source: 'node-2', target: 'node-0' }, + { source: 'node-3', target: 'node-0' }, + { source: 'node-4', target: 'node-0' }, + { source: 'node-5', target: 'node-0' }, + ], +}; + +const graph = new Graph({ + container: 'container', + width: 800, + height: 600, + data, + layout: { + type: 'force', + }, + behaviors: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], + plugins: [ + { + type: 'watermark', + // 保持和下面图片大小一致,显示更清晰 + width: 1280, + height: 830, + rotate: 0, + opacity: 0.7, + imageURL: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*0Qq0ToQm1rEAAAAAAAAAAAAADmJ7AQ/original', + backgroundSize: 'cover', + }, + ], +}); + +graph.render(); diff --git a/packages/site/examples/tool/watermarker/demo/imgWaterMarker.ts b/packages/site/examples/tool/watermarker/demo/imgWaterMarker.ts deleted file mode 100644 index 3955cd4da82..00000000000 --- a/packages/site/examples/tool/watermarker/demo/imgWaterMarker.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Graph as BaseGraph, Extensions, Util, extend } from '@antv/g6'; - -const Graph = extend(BaseGraph, { - plugins: { - 'water-marker': Extensions.WaterMarker, - }, -}); - -const container = document.getElementById('container') as HTMLElement; -const width = container.scrollWidth; -const height = (container.scrollHeight || 500) - 110; -const data = Util.mock(6).circle(); - -const graph = new Graph({ - container, - width, - height, - data, - modes: { - default: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], - }, - plugins: [ - { - key: 'water-marker-plg', - type: 'water-marker', - begin: [0, 50], - image: { - imgURL: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg', - width: 94, - height: 28, - rotate: 30, - }, - }, - ], -}); - -window.graph = graph; diff --git a/packages/site/examples/tool/watermarker/demo/imgWatermark.ts b/packages/site/examples/tool/watermarker/demo/imgWatermark.ts new file mode 100644 index 00000000000..7454f162403 --- /dev/null +++ b/packages/site/examples/tool/watermarker/demo/imgWatermark.ts @@ -0,0 +1,36 @@ +import { Graph } from '@antv/g6'; + +const data = { + nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }], + edges: [ + { source: 'node-0', target: 'node-1' }, + { source: 'node-0', target: 'node-2' }, + { source: 'node-0', target: 'node-3' }, + { source: 'node-0', target: 'node-4' }, + { source: 'node-1', target: 'node-0' }, + { source: 'node-2', target: 'node-0' }, + { source: 'node-3', target: 'node-0' }, + { source: 'node-4', target: 'node-0' }, + { source: 'node-5', target: 'node-0' }, + ], +}; + +const graph = new Graph({ + container: 'container', + data, + layout: { + type: 'force', + }, + behaviors: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], + plugins: [ + { + type: 'watermark', + width: 200, + height: 100, + rotate: Math.PI / 12, + imageURL: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg', + }, + ], +}); + +graph.render(); diff --git a/packages/site/examples/tool/watermarker/demo/meta.json b/packages/site/examples/tool/watermarker/demo/meta.json index e8929e98e97..d7853463c6e 100644 --- a/packages/site/examples/tool/watermarker/demo/meta.json +++ b/packages/site/examples/tool/watermarker/demo/meta.json @@ -5,18 +5,26 @@ }, "demos": [ { - "filename": "textWaterMarker.ts", + "filename": "textWatermark.ts", "title": { "zh": "文本水印", - "en": "Text WaterMarker" + "en": "Text Watermark" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*GPRuRqBUIsoAAAAAAAAAAAAADmJ7AQ/original" }, { - "filename": "imgWaterMarker.ts", + "filename": "imgWatermark.ts", "title": { "zh": "图片水印", - "en": "Image WaterMarker" + "en": "Image Watermark" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*_FYsQ7SYtcAAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "background.ts", + "title": { + "zh": "背景图片", + "en": "Background Image" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*_FYsQ7SYtcAAAAAAAAAAAAAADmJ7AQ/original" } diff --git a/packages/site/examples/tool/watermarker/demo/textWaterMarker.ts b/packages/site/examples/tool/watermarker/demo/textWaterMarker.ts deleted file mode 100644 index 7d1e5e4c8c5..00000000000 --- a/packages/site/examples/tool/watermarker/demo/textWaterMarker.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Graph as BaseGraph, Extensions, Util, extend } from '@antv/g6'; - -const Graph = extend(BaseGraph, { - plugins: { - 'water-marker': Extensions.WaterMarker, - }, -}); - -const container = document.getElementById('container') as HTMLElement; -const width = container.scrollWidth; -const height = (container.scrollHeight || 500) - 110; -const data = Util.mock(6).circle(); - -const graph = new Graph({ - container, - width, - height, - data, - modes: { - default: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], - }, - plugins: [ - { - key: 'water-marker-plg', - type: 'water-marker', - mode: 'text', - begin: [100, 50], - separation: [50, 50], - text: { - texts: ['AntV G6', 'Graph Visualization'], - lineHeight: 20, - fontSize: 14, - fontFamily: 'Microsoft YaHei', - fill: 'rgba(0, 0, 0, 0.1)', - rotate: 20, - textAlign: 'center', - }, - }, - ], -}); - -window.graph = graph; diff --git a/packages/site/examples/tool/watermarker/demo/textWatermark.ts b/packages/site/examples/tool/watermarker/demo/textWatermark.ts new file mode 100644 index 00000000000..202b959680d --- /dev/null +++ b/packages/site/examples/tool/watermarker/demo/textWatermark.ts @@ -0,0 +1,37 @@ +import { Graph } from '@antv/g6'; + +const data = { + nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }], + edges: [ + { source: 'node-0', target: 'node-1' }, + { source: 'node-0', target: 'node-2' }, + { source: 'node-0', target: 'node-3' }, + { source: 'node-0', target: 'node-4' }, + { source: 'node-1', target: 'node-0' }, + { source: 'node-2', target: 'node-0' }, + { source: 'node-3', target: 'node-0' }, + { source: 'node-4', target: 'node-0' }, + { source: 'node-5', target: 'node-0' }, + ], +}; + +const graph = new Graph({ + container: 'container', + data, + layout: { + type: 'force', + }, + behaviors: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], + plugins: [ + { + type: 'watermark', + text: 'G6: Graph Visualization', + textFontSize: 14, + textFontFamily: 'Microsoft YaHei', + fill: 'rgba(0, 0, 0, 0.1)', + rotate: Math.PI / 12, + }, + ], +}); + +graph.render();