import { Text, G } from '@svgdotjs/svg.js' import { degToRad, camelCaseToHyphen } from '../utils' import merge from 'deepmerge' import MindMap from '..' interface WatermarkOptions { mindMap: MindMap } interface WatermarkConfig { text?: string | number lineSpacing?: number textSpacing?: number angle?: number textStyle?: Record belowNode?: boolean onlyExport?: boolean } interface TextStyle { [key: string]: any } // 水印插件 class Watermark { static instanceName: string = 'watermark' private mindMap: any private lineSpacing: number private textSpacing: number private angle: number private text: string private textStyle: TextStyle private watermarkDraw: G | null private isInExport: boolean private maxLong: number constructor(opt: WatermarkOptions = {} as WatermarkOptions) { this.mindMap = opt.mindMap this.lineSpacing = 0 this.textSpacing = 0 this.angle = 0 this.text = '' this.textStyle = {} this.watermarkDraw = null this.isInExport = false this.maxLong = this.getMaxLong() this.updateWatermark(this.mindMap.opt.watermarkConfig || {}) this.bindEvent() } private getMaxLong(): number { return Math.sqrt( Math.pow(this.mindMap.width, 2) + Math.pow(this.mindMap.height, 2) ) } private bindEvent(): void { this.onResize = this.onResize.bind(this) this.mindMap.on('resize', this.onResize) } private unBindEvent(): void { this.mindMap.off('resize', this.onResize) } private onResize(): void { this.maxLong = this.getMaxLong() this.draw() } private createContainer(): void { if (this.watermarkDraw) return this.watermarkDraw = new G() .css({ pointerEvents: 'none', userSelect: 'none' }) .addClass('smm-water-mark-container') this.updateLayer() } private updateLayer(): void { if (!this.watermarkDraw) return const { belowNode } = this.mindMap.opt.watermarkConfig if (belowNode) { this.watermarkDraw.insertBefore(this.mindMap.draw) } else { this.mindMap.svg.add(this.watermarkDraw) } } private removeContainer(): void { if (!this.watermarkDraw) { return } this.watermarkDraw.remove() this.watermarkDraw = null } private hasWatermark(): boolean { return !!this.text.trim() } private handleConfig({ text, lineSpacing, textSpacing, angle, textStyle }: WatermarkConfig): void { this.text = text === undefined ? '' : String(text).trim() this.lineSpacing = typeof lineSpacing === 'number' && lineSpacing > 0 ? lineSpacing : 100 this.textSpacing = typeof textSpacing === 'number' && textSpacing > 0 ? textSpacing : 100 this.angle = typeof angle === 'number' && angle >= 0 && angle <= 90 ? angle : 30 this.textStyle = Object.assign(this.textStyle, textStyle || {}) } private clear(): void { if (this.watermarkDraw) this.watermarkDraw.clear() } private draw(): void { this.clear() const { onlyExport } = this.mindMap.opt.watermarkConfig if (onlyExport && !this.isInExport) return if (!this.hasWatermark()) { this.removeContainer() return } this.createContainer() let x = 0 while (x < this.mindMap.width) { this.drawText(x) x += this.lineSpacing / Math.sin(degToRad(this.angle)) } let yOffset = this.lineSpacing / Math.cos(degToRad(this.angle)) || this.lineSpacing let y = yOffset while (y < this.mindMap.height) { this.drawText(0, y) y += yOffset } } private drawText(x: number, y?: number): void { let long = Math.min( this.maxLong, (this.mindMap.width - x) / Math.cos(degToRad(this.angle)) ) let g = new G() let bbox = null let bboxWidth = 0 let textHeight = -1 while (bboxWidth < long) { let text = new Text().text(this.text) g.add(text) text.transform({ translateX: bboxWidth }) this.setTextStyle(text) bbox = g.bbox() if (textHeight === -1) { textHeight = bbox.height } bboxWidth = bbox.width + this.textSpacing } let params = { rotate: this.angle, origin: 'top left', translateX: x, translateY: textHeight } if (y !== undefined) { params.translateY = y + textHeight } g.transform(params) this.watermarkDraw?.add(g) } private setTextStyle(text: Text): void { Object.keys(this.textStyle).forEach(item => { let value = this.textStyle[item] if (item === 'color') { text.fill(value) } else { text.css(camelCaseToHyphen(item) as CSSStyleName, value) } }) } public updateWatermark(config: WatermarkConfig): void { this.mindMap.opt.watermarkConfig = merge( this.mindMap.opt.watermarkConfig, config ) this.updateLayer() this.handleConfig(config) this.draw() } public beforePluginRemove(): void { this.unBindEvent() this.removeContainer() } public beforePluginDestroy(): void { this.unBindEvent() this.removeContainer() } } export default Watermark