Files
cut-abstractions/tests/dev1/dataHandle/common/drawing/svgDrawing.ts

252 lines
7.4 KiB
TypeScript
Raw Normal View History

2025-07-22 18:22:31 +08:00
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('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')
} 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<any>, 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)
}
}