import type { DrawImageOption, DrawLineOption, DrawLinePolygonOption, DrawPathContext, DrawRectOption, DrawTextOption, } from './base.js' import { Drawing, } from './base.js' const canvas = document.createElement('canvas') const canvasCtx = canvas.getContext('2d') export class SvgDrawing extends Drawing { svg: SVGElement /** * */ constructor( width: number | string, height: number | string, scale: number = 0.8, ) { super() this.scale = scale this.width = Number(width) this.height = Number(height) this.svg = this.createElement('svg') // this.svg.setAttribute('width', this.width.toString());//(Number(width) * this.scale) // this.svg.setAttribute('height', this.height.toString());//(Number(height) * this.scale) // this.svg.style.transform = 'scale(' + this.scale + ')'; // this.svg.style.transformOrigin = 'left top'; this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`) } private createElement(name) { return document.createElementNS('http://www.w3.org/2000/svg', name) } toHTML(): string { return this.svg.outerHTML } clearAll() { this.svg.innerHTML = '' } drawPath(path: DrawPathContext, option: DrawLineOption) { let el = this.createElement('path') let pathStr = '' for (const item of path.data) { switch (item.method) { case 'M': pathStr += `M ${item.args.join(' ')} ` break case 'L': pathStr += `L ${item.args.join(' ')} ` break } } el.setAttribute('d', pathStr) el.setAttribute('stroke-width', option.lineWidth.toString()) el.setAttribute('stroke', option.strokeStyle) this.svg.appendChild(el) } drawLine( x0: number, y0: number, x1: number, y1: number, option: DrawLineOption, ) { let el = this.createElement('line') el.setAttribute('x1', x0.toString()) // (Number(x0) * this.scale) el.setAttribute('y1', y0.toString()) // (Number(y0) * this.scale) el.setAttribute('x2', x1.toString()) // (Number(x1) * this.scale) el.setAttribute('y2', y1.toString()) // (Number(y1) * this.scale) el.setAttribute('stroke', option.strokeStyle) el.setAttribute('fill', 'rgba(0,0,0,0)') el.setAttribute('stroke-width', option.lineWidth.toString()) this.svg.appendChild(el) } drawRect( x: number, y: number, width: number, height: number, option: DrawRectOption, ) { let el = this.createElement('rect') el.setAttribute('x', x.toString()) // ((x + option.baseLine / 2) * this.scale) el.setAttribute('y', y.toString()) // ((y + option.baseLine) * this.scale) el.setAttribute('width', width.toString()) // (Number(width) * this.scale) el.setAttribute('height', height.toString()) // (Number(height) * this.scale) if (option.fillStyle) { el.setAttribute('fill', option.fillStyle) el.setAttribute('stroke', option.strokeStyle) } else { el.setAttribute('fill', 'rgba(1,1,1,0)') el.setAttribute('stroke', option.strokeStyle) el.setAttribute( 'stroke-width', (Math.floor(option.lineWidth) + 0.5).toString(), ) } this.svg.appendChild(el) } drawText(text: string, x: number, y: number, option: DrawTextOption) { let el = this.createElement('text') el.setAttribute('x', x.toString()) // (Number(x) * this.scale) option['CncDict'] ? (x - Number(option.fontSize) / 6).toString() : el.setAttribute('y', y.toString()) // (Number(y) * this.scale) el.style.dominantBaseline = 'text-before-edge' if (option.resetFont) { el.style.dominantBaseline = 'middle' el.setAttribute('text-anchor', 'middle') } el.setAttribute('fill', option.fillStyle) el.setAttribute('font-size', `${option.fontSize}px`) el.setAttribute('font-weight', option.fontWeight) el.setAttribute('font-family', option.fontFamily) if (option.overflowText) { el.setAttribute('textLength', option.maxWidth.toString()) el.setAttribute('lengthAdjust', 'spacingAndGlyphs') } el.setAttribute('alignment-baseline', option.textBaseline) switch (option.textAlign) { case 'start': el.setAttribute('text-anchor', 'left') break case 'center': el.setAttribute('text-anchor', 'middle') break case 'right': el.setAttribute('text-anchor', 'end') break } if (typeof text === 'string') { el.innerHTML = text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>') } else { el.innerHTML = text } this.svg.appendChild(el) } drawForeignObjectText( text: string, x: number, y: number, option: DrawTextOption, ) { // let el = this.createElement('foreignObject'); // el.setAttribute('x',x.toString()); // el.setAttribute('y',y.toString()); // el.setAttribute('width',option.maxWidth.toString()); // el.setAttribute('height',option['maxHeight'].toString()); // let span = this.createElement('span'); // span.style.dominantBaseline = 'text-before-edge'; // if(option['resetFont']){ // span.style.dominantBaseline = 'middle'; // span.style.textAnchor = 'middle'; // } // span.style.fontSize = option.fontSize + 'px'; // span.style.fontWeight = option.fontWeight; // span.style.fontFamily = option.fontFamily; // span.style.display = 'inline-block'; // span.style.width = option.maxWidth + 'px'; // span.style.height = option['maxHeight'] + 'px'; // span.style.overflow = 'hidden'; // span.innerHTML = text; // el.appendChild(span); // this.svg.appendChild(el); } async drawImage( source: CanvasImageSource, x: number, y: number, width: number, height: number, option: DrawImageOption, ) { let src = '' if (source instanceof ImageBitmap) { canvas.width = source.width canvas.height = source.height canvasCtx.clearRect(0, 0, source.width, source.height) canvasCtx.drawImage(source, 0, 0, source.width, source.height) src = canvas.toDataURL() } else if (source instanceof HTMLImageElement) { src = source.src } return new Promise((resolve, reject) => { let el = this.createElement('image') el.setAttribute('x', x.toString()) // (Number(x) * this.scale) el.setAttribute('y', y.toString()) // (Number(y) * this.scale) el.setAttribute('width', width.toString()) // (Number(width) * this.scale) el.setAttribute('height', height.toString()) // (Number(height) * this.scale) el.setAttribute('xlink:href', src) el.setAttribute('preserveAspectRatio', 'none meet') this.svg.appendChild(el) resolve(null) }) } drawLinePolygon(points: Array, option: DrawLinePolygonOption) { let el = this.createElement('polygon') let pointsStr = '' for (let i = 0; i < points.length; i++) { if (i == points.length - 1) { pointsStr += `${points[i].x} ${points[i].y}` } else { pointsStr += `${points[i].x} ${points[i].y},` } } el.setAttribute('points', pointsStr) el.setAttribute('fill', option.isFill ? option.fillStyle : 'none') el.setAttribute('stroke', option.strokeStyle) el.setAttribute('stroke-width', option.lineWidth.toString())// 添加多段线的线宽--xyh this.svg.appendChild(el) } }