252 lines
7.4 KiB
TypeScript
252 lines
7.4 KiB
TypeScript
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<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)
|
|
}
|
|
}
|