import { DrawLeafs, IBaseFill, IDrawNode, IEllipseDrawObject, IImageDrawObject, IRectangleDrawObject, IRenderer, IScene, ITextDrawObject, } from "@gxc-solutions/renderer-base/interfaces"; import { isDrawNode, isEllipseDrawObject, isImageDrawObject, isRectangleDrawObject, isTextDrawObject, } from "@gxc-solutions/renderer-base/utils/draw-object"; import { Artist } from "./artist"; import { degToRad } from "@gxc-solutions/math"; export class Canvas2DRenderer implements IRenderer { public readonly type = "2d"; private _context: CanvasRenderingContext2D; private _artist: Artist; get holder() { return this._canvas; } constructor(private _canvas: HTMLCanvasElement) { this._context = this._canvas.getContext("2d"); this._artist = new Artist(this._context); } public async render(scene: IScene): Promise { this._context.clearRect(0, 0, this._canvas.width, this._canvas.height); this.renderBg(scene.background); scene.static.forEach((leaf) => this.drawLeaf(leaf)); this.renderNode(scene.node); this.renderNode(scene.selection); } renderBg(fill: IBaseFill) { this._context.save(); this._artist.setFill(fill); this._context.fillRect(0, 0, this._canvas.width, this._canvas.height); this._context.restore(); } renderNode(drawNode: IDrawNode) { this._artist.setTransform(drawNode.transform); this._context.save(); drawNode.children.forEach((leaf) => { if (isDrawNode(leaf)) { this.renderNode(leaf); } else { this.drawLeaf(leaf as DrawLeafs); } }); this._context.restore(); } private drawLeaf(leaf: DrawLeafs) { if (isEllipseDrawObject(leaf)) { this._context.save(); const dn = leaf as IEllipseDrawObject; this._context.globalAlpha = dn.opacity; this._context.globalCompositeOperation = dn.blendMode as GlobalCompositeOperation; this._artist.setTransform(dn.transform); this._artist.setFill(dn.fill); this._context.ellipse( dn.ellipse.center.x - dn.ellipse.radiusX, dn.ellipse.center.y - dn.ellipse.radiusY, dn.ellipse.radiusX, dn.ellipse.radiusY, degToRad(0), 0, degToRad(360), ); this._context.fill(); this._artist.setStroke(dn.stroke); if (dn.stroke.width > 0) { this._context.stroke(); } this._context.restore(); } if (isImageDrawObject(leaf)) { this._context.save(); const dn = leaf as IImageDrawObject; this._context.globalAlpha = dn.opacity; this._context.globalCompositeOperation = dn.blendMode as GlobalCompositeOperation; this._artist.setTransform(dn.transform); if (dn.source instanceof HTMLImageElement) { this._artist.drawImageElement( dn.source, { x: dn.rectangle.x, y: dn.rectangle.y }, { width: dn.rectangle.width, height: dn.rectangle.height }, ); } else if (dn.source instanceof ImageBitmap) { this._artist.drawImageBitmap( dn.source, { x: dn.rectangle.x, y: dn.rectangle.y }, { width: dn.rectangle.width, height: dn.rectangle.height }, ); } else if (dn.source instanceof ImageData) { this._artist.drawImageData( dn.source, { x: dn.rectangle.x, y: dn.rectangle.y }, { width: dn.rectangle.width, height: dn.rectangle.height }, ); } this._context.restore(); } if (isRectangleDrawObject(leaf)) { this._context.save(); const dn = leaf as IRectangleDrawObject; this._context.globalAlpha = dn.opacity; this._context.globalCompositeOperation = dn.blendMode as GlobalCompositeOperation; this._artist.setTransform(dn.transform); this._artist.setFill(dn.fill); this._context.fillRect(dn.rectangle.x, dn.rectangle.y, dn.rectangle.width, dn.rectangle.height); this._artist.setStroke(dn.stroke); if (dn.stroke.width > 0) { this._context.strokeRect(dn.rectangle.x, dn.rectangle.y, dn.rectangle.width, dn.rectangle.height); } this._context.restore(); } if (isTextDrawObject(leaf)) { this._context.save(); const dn = leaf as ITextDrawObject; this._context.globalAlpha = dn.opacity; this._context.globalCompositeOperation = dn.blendMode as GlobalCompositeOperation; this._artist.setTransform(dn.transform); this._artist.setStroke(dn.textStroke); this._artist.setFont(dn.font); const metrics = this._context.measureText(dn.text); console.warn(metrics); if (dn.textStroke.width > 0) { this._context.strokeText(dn.text, dn.rectangle.x, dn.rectangle.y); } this._context.fillText(dn.text, dn.rectangle.x, dn.rectangle.y); this._context.restore(); } } }