generated from gxc-solutions/gxc-template-repo
Added base rendering
This commit is contained in:
parent
63818eb2b8
commit
a100988848
7 changed files with 587 additions and 85 deletions
178
lib/src/artist.ts
Normal file
178
lib/src/artist.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
import { degToRad } from "@gxc-solutions/math/functions";
|
||||||
|
import { IPoint, ISize } from "@gxc-solutions/math/interfaces";
|
||||||
|
import { IFont, IStroke, ITransform } from "@gxc-solutions/renderer-base";
|
||||||
|
import {
|
||||||
|
IBaseFill,
|
||||||
|
ISolidFill,
|
||||||
|
IConicGradient,
|
||||||
|
ILinerGradient,
|
||||||
|
IRadialGradient,
|
||||||
|
ITextureFill,
|
||||||
|
} from "@gxc-solutions/renderer-base/interfaces/fill";
|
||||||
|
import {
|
||||||
|
isTextureFill,
|
||||||
|
isConicGradientFill,
|
||||||
|
isLinerGradientFill,
|
||||||
|
isRadialGradientFill,
|
||||||
|
isSolidFill,
|
||||||
|
} from "@gxc-solutions/renderer-base/utils/fill";
|
||||||
|
|
||||||
|
export class Artist {
|
||||||
|
constructor(private _context: CanvasRenderingContext2D) {}
|
||||||
|
|
||||||
|
setFont(font: IFont) {
|
||||||
|
const { color, font: fontFamily, size } = font;
|
||||||
|
this._context.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
|
||||||
|
|
||||||
|
this._context.font = `${size}px ${fontFamily}`;
|
||||||
|
this._context.textAlign = "left";
|
||||||
|
this._context.textBaseline = "middle";
|
||||||
|
this._context.direction = "ltr";
|
||||||
|
this._context.fontKerning = "auto";
|
||||||
|
this._context.fontStretch = "normal";
|
||||||
|
this._context.fontVariantCaps = "normal";
|
||||||
|
// this._context.letterSpacing
|
||||||
|
this._context.textRendering = "auto";
|
||||||
|
//this._context.wordSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
setStroke(stroke: IStroke) {
|
||||||
|
const { color, dash, width } = stroke;
|
||||||
|
this._context.lineWidth = width;
|
||||||
|
this._context.strokeStyle = this._context.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
|
||||||
|
this._context.setLineDash(dash);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTransform(transform: ITransform) {
|
||||||
|
this._context.translate(transform.translateX, transform.translateY);
|
||||||
|
this._context.scale(transform.scaleX, transform.scaleY);
|
||||||
|
this._context.rotate(degToRad(transform.rotate));
|
||||||
|
}
|
||||||
|
|
||||||
|
setFill(fill: IBaseFill) {
|
||||||
|
if (isSolidFill(fill)) {
|
||||||
|
this._setSolidFill(fill);
|
||||||
|
}
|
||||||
|
if (isRadialGradientFill(fill)) {
|
||||||
|
this._setRadialGradientFill(fill);
|
||||||
|
}
|
||||||
|
if (isLinerGradientFill(fill)) {
|
||||||
|
this._setLinerGradientFill(fill);
|
||||||
|
}
|
||||||
|
if (isConicGradientFill(fill)) {
|
||||||
|
this._setConicGradientFill(fill);
|
||||||
|
}
|
||||||
|
if (isTextureFill(fill)) {
|
||||||
|
this._setTextureFill(fill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setSolidFill(fill: ISolidFill) {
|
||||||
|
const { color } = fill;
|
||||||
|
this._context.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setRadialGradientFill(fill: IRadialGradient) {
|
||||||
|
console.warn(`${fill.type} not implemented!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setLinerGradientFill(fill: ILinerGradient) {
|
||||||
|
console.warn(`${fill.type} not implemented!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setConicGradientFill(fill: IConicGradient) {
|
||||||
|
console.warn(`${fill.type} not implemented!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setTextureFill(fill: ITextureFill) {
|
||||||
|
console.warn(`${fill.type} not implemented!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawRotatedStrokeRect(position: IPoint, size: ISize, angle: number, color: string) {
|
||||||
|
this._context.save(); // сохраняем систему координат
|
||||||
|
this._context.translate(position.x + size.width / 2, position.y + size.height / 2); // переносим начало в центр фигуры
|
||||||
|
this._context.rotate(degToRad(angle)); // вращаем систему координат
|
||||||
|
this._context.strokeStyle = color;
|
||||||
|
this._context.lineWidth = 2;
|
||||||
|
this._context.strokeRect(-size.width / 2, -size.height / 2, size.width, size.height);
|
||||||
|
this._context.restore(); // возвращаем всё обратно
|
||||||
|
}
|
||||||
|
|
||||||
|
drawRect(position: IPoint, size: ISize, color: string) {
|
||||||
|
this._context.strokeStyle = color;
|
||||||
|
this._context.lineWidth = 2;
|
||||||
|
this._context.strokeRect(size.width, size.height, size.width, size.height);
|
||||||
|
this._context.restore(); // возвращаем всё обратно
|
||||||
|
}
|
||||||
|
|
||||||
|
drawRotatedRect(position: IPoint, size: ISize, angle: number, color: string) {
|
||||||
|
this._context.save();
|
||||||
|
// переносим систему координат в центр прямоугольника
|
||||||
|
this._context.translate(position.x + size.width / 2, position.y + size.height / 2);
|
||||||
|
// поворот вокруг центра
|
||||||
|
this._context.rotate(degToRad(angle));
|
||||||
|
// рисуем прямоугольник так, чтобы его центр оказался в (0,0)
|
||||||
|
this._context.fillStyle = color;
|
||||||
|
this._context.fillRect(-size.width / 2, -size.height / 2, size.width, size.height);
|
||||||
|
|
||||||
|
this._context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawEllipse(position: IPoint, size: ISize, angle: number, color: string, segment = 360) {
|
||||||
|
this._context.save();
|
||||||
|
this._context.fillStyle = color;
|
||||||
|
this._context.beginPath();
|
||||||
|
this._context.ellipse(position.x, position.y, size.width / 2, size.height / 2, degToRad(angle), 0, degToRad(segment));
|
||||||
|
this._context.fill();
|
||||||
|
this._context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawLine(start: IPoint, end: IPoint, width: number, color: string) {
|
||||||
|
this._context.save();
|
||||||
|
this._context.beginPath();
|
||||||
|
this._context.lineWidth = width;
|
||||||
|
this._context.strokeStyle = color;
|
||||||
|
this._context.moveTo(start.x, start.y);
|
||||||
|
this._context.lineTo(end.x, end.y);
|
||||||
|
this._context.stroke();
|
||||||
|
this._context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawText(text: string, position: IPoint, font: string, size: number, angle: number, color: string) {
|
||||||
|
this._context.save();
|
||||||
|
this._context.translate(position.x, position.y);
|
||||||
|
this._context.rotate(degToRad(angle));
|
||||||
|
this._context.fillStyle = color;
|
||||||
|
this._context.font = `${size}px ${font}`;
|
||||||
|
// TODO
|
||||||
|
this._context.textAlign = "left";
|
||||||
|
// TODO
|
||||||
|
this._context.textBaseline = "middle";
|
||||||
|
this._context.direction = "ltr";
|
||||||
|
console.warn(this._context.measureText(text));
|
||||||
|
|
||||||
|
this._context.fillText(text, position.x, position.y);
|
||||||
|
this._context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawImage(source: HTMLImageElement, position: IPoint, size: ISize) {
|
||||||
|
this._context.save();
|
||||||
|
const scaleY = size.height / source.naturalHeight;
|
||||||
|
const scaleX = size.width / source.naturalWidth;
|
||||||
|
|
||||||
|
this._context.translate(position.x + size.width / 2, position.y + size.height / 2);
|
||||||
|
this._context.scale(scaleY, scaleX);
|
||||||
|
|
||||||
|
this._context.drawImage(source, -source.naturalWidth / 2, -source.naturalHeight / 2);
|
||||||
|
this._context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawPath(path: string) {
|
||||||
|
const path2d = new Path2D(path);
|
||||||
|
this._context.stroke(path2d);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
// this._context.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,51 +1,29 @@
|
||||||
import { degToRad } from "@gxc-solutions/math/functions";
|
import {
|
||||||
import { IDrawObject, IRenderer, IScene } from "@gxc-solutions/renderer-base/interfaces";
|
DrawLeafs,
|
||||||
import { SequentialDrawThread } from "./draw-thread";
|
IBaseFill,
|
||||||
|
IDrawNode,
|
||||||
const drawRotatedRect = (
|
IEllipseDrawObject,
|
||||||
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
IImageDrawObject,
|
||||||
x: number,
|
IRectangleDrawObject,
|
||||||
y: number,
|
IRenderer,
|
||||||
width: number,
|
IScene,
|
||||||
height: number,
|
ITextDrawObject,
|
||||||
angle: number,
|
} from "@gxc-solutions/renderer-base/interfaces";
|
||||||
color: string,
|
import {
|
||||||
) => {
|
isDrawNode,
|
||||||
context.save();
|
isEllipseDrawObject,
|
||||||
// переносим систему координат в центр прямоугольника
|
isImageDrawObject,
|
||||||
context.translate(x + width / 2, y + height / 2);
|
isRectangleDrawObject,
|
||||||
// поворот вокруг центра
|
isTextDrawObject,
|
||||||
context.rotate(degToRad(angle));
|
} from "@gxc-solutions/renderer-base/utils/draw-object";
|
||||||
// рисуем прямоугольник так, чтобы его центр оказался в (0,0)
|
import { Artist } from "./artist";
|
||||||
context.fillStyle = color;
|
import { degToRad } from "@gxc-solutions/math";
|
||||||
context.fillRect(-width / 2, -height / 2, width, height);
|
|
||||||
|
|
||||||
context.restore();
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawRotatedStrokeRect = (
|
|
||||||
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
angle: number,
|
|
||||||
color: string,
|
|
||||||
) => {
|
|
||||||
context.save(); // сохраняем систему координат
|
|
||||||
context.translate(x + width / 2, y + height / 2); // переносим начало в центр фигуры
|
|
||||||
context.rotate(degToRad(angle)); // вращаем систему координат
|
|
||||||
context.strokeStyle = color;
|
|
||||||
context.lineWidth = 2;
|
|
||||||
context.strokeRect(-width / 2, -height / 2, width, height);
|
|
||||||
context.restore(); // возвращаем всё обратно
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Canvas2DRenderer implements IRenderer {
|
export class Canvas2DRenderer implements IRenderer {
|
||||||
public readonly type = "2d";
|
public readonly type = "2d";
|
||||||
|
|
||||||
private _context: CanvasRenderingContext2D;
|
private _context: CanvasRenderingContext2D;
|
||||||
private _thread: SequentialDrawThread<(offscreenCanvas: OffscreenCanvasRenderingContext2D, objects: IDrawObject[]) => void>;
|
private _artist: Artist;
|
||||||
|
|
||||||
get holder() {
|
get holder() {
|
||||||
return this._canvas;
|
return this._canvas;
|
||||||
|
|
@ -53,41 +31,103 @@ export class Canvas2DRenderer implements IRenderer {
|
||||||
|
|
||||||
constructor(private _canvas: HTMLCanvasElement) {
|
constructor(private _canvas: HTMLCanvasElement) {
|
||||||
this._context = this._canvas.getContext("2d");
|
this._context = this._canvas.getContext("2d");
|
||||||
|
this._artist = new Artist(this._context);
|
||||||
this._thread = new SequentialDrawThread(
|
|
||||||
(context2d, objects: IDrawObject[]) => {
|
|
||||||
const canvas = context2d.canvas;
|
|
||||||
context2d.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
objects.forEach((drawObject) => {
|
|
||||||
if (drawObject.type === "rectangle-object") {
|
|
||||||
drawRotatedRect(
|
|
||||||
context2d,
|
|
||||||
drawObject.x,
|
|
||||||
drawObject.y,
|
|
||||||
drawObject.width,
|
|
||||||
drawObject.height,
|
|
||||||
drawObject.angle,
|
|
||||||
drawObject.color, // TODO в worker не знает про toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
this._canvas.width,
|
|
||||||
this._canvas.height,
|
|
||||||
[drawRotatedRect, drawRotatedStrokeRect, degToRad],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async render(scene: IScene): Promise<void> {
|
public async render(scene: IScene): Promise<void> {
|
||||||
const bitmap = await this._thread.run([scene.objects]);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
const prev = this._context.globalCompositeOperation;
|
renderBg(fill: IBaseFill) {
|
||||||
this._context.globalCompositeOperation = "copy";
|
this._context.save();
|
||||||
// Важно: убедись, что размеры совпадают
|
this._artist.setFill(fill);
|
||||||
this._context.drawImage(bitmap, 0, 0, this._canvas.width, this._canvas.height);
|
this._context.fillRect(0, 0, this._canvas.width, this._canvas.height);
|
||||||
this._context.globalCompositeOperation = prev;
|
this._context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
// Освобождаем ресурсы
|
renderNode(drawNode: IDrawNode) {
|
||||||
if ("close" in bitmap) bitmap.close();
|
drawNode.children.forEach((leaf) => {
|
||||||
|
if (isDrawNode(leaf)) {
|
||||||
|
this._artist.setTransform(leaf.transform);
|
||||||
|
this._context.save();
|
||||||
|
this.renderNode(leaf);
|
||||||
|
this._context.restore();
|
||||||
|
}
|
||||||
|
this.drawLeaf(leaf as DrawLeafs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
this._artist.drawImage(
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@gxc-solutions/renderer-canvas-2d",
|
"name": "@gxc-solutions/renderer-canvas-2d",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "GXC Solutions",
|
"author": "GXC Solutions",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|
|
||||||
15
package-lock.json
generated
15
package-lock.json
generated
|
|
@ -1,16 +1,16 @@
|
||||||
{
|
{
|
||||||
"name": "template-of-lib-repo",
|
"name": "renderer-canvas-2d",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "template-of-lib-repo",
|
"name": "renderer-canvas-2d",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gxc-solutions/math": "^0.0.1",
|
"@gxc-solutions/math": "^0.0.1",
|
||||||
"@gxc-solutions/renderer-base": "^0.0.3"
|
"@gxc-solutions/renderer-base": "^0.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.4",
|
"@eslint/eslintrc": "^3.3.4",
|
||||||
|
|
@ -701,9 +701,12 @@
|
||||||
"integrity": "sha512-m6lxTkjXkyaUoI3+cJKpgt/AQlyApLSJ2p9D2EJ9+XzHCSjOWs29GzczPLgopYdLEWYN/YPr77V4CScGpR7qxw=="
|
"integrity": "sha512-m6lxTkjXkyaUoI3+cJKpgt/AQlyApLSJ2p9D2EJ9+XzHCSjOWs29GzczPLgopYdLEWYN/YPr77V4CScGpR7qxw=="
|
||||||
},
|
},
|
||||||
"node_modules/@gxc-solutions/renderer-base": {
|
"node_modules/@gxc-solutions/renderer-base": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.9",
|
||||||
"resolved": "https://npm.gxc-solutions.ru/@gxc-solutions/renderer-base/-/renderer-base-0.0.3.tgz",
|
"resolved": "https://npm.gxc-solutions.ru/@gxc-solutions/renderer-base/-/renderer-base-0.0.9.tgz",
|
||||||
"integrity": "sha512-rNglFmWe3LNbTn/KV8s7waV5IPgZ7T+NUbNSChiK+TdGK8bAfuE5yyAeJ7pmvpYbAaRC4emmgnpDQlRr0Kx3nA=="
|
"integrity": "sha512-aLu3eTyzDcGQw+FsYt25MNg/6gNORXa8FynUX5rtEFinmdFEDdqxOr08dhFViw1FIT3ZdYtJbY9aegFK3V/OcA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@gxc-solutions/math": "^0.0.1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@humanfs/core": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "template-of-lib-repo",
|
"name": "renderer-canvas-2d",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -32,6 +32,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gxc-solutions/math": "^0.0.1",
|
"@gxc-solutions/math": "^0.0.1",
|
||||||
"@gxc-solutions/renderer-base": "^0.0.3"
|
"@gxc-solutions/renderer-base": "^0.0.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
<title>Playground</title>
|
<title>Playground</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello world!</h1>
|
<h1>2D Canvas Renderer Playground</h1>
|
||||||
|
<canvas width="800" height="800" id="canvas"></canvas>
|
||||||
|
|
||||||
<script type="module" src="./index.ts"></script>
|
<script type="module" src="./index.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -1 +1,281 @@
|
||||||
console.warn("Hello world!");
|
import { Canvas2DRenderer } from "./renderer-canvas-2d/canvas-2d-renderer";
|
||||||
|
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||||
|
const renderer = new Canvas2DRenderer(canvas);
|
||||||
|
|
||||||
|
const image = new Image(100, 100);
|
||||||
|
image.src = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSX7vk1hi2Qnqbwbkz4t9x-akEeuuHA78H9PA&s";
|
||||||
|
await new Promise<void>((resolve) => image.addEventListener("load", () => resolve()));
|
||||||
|
|
||||||
|
await renderer.render({
|
||||||
|
background: {
|
||||||
|
type: "solid",
|
||||||
|
color: {
|
||||||
|
r: 200,
|
||||||
|
g: 255,
|
||||||
|
b: 255,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
id: "a",
|
||||||
|
type: "node",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "rectangle",
|
||||||
|
blendMode: "source-over",
|
||||||
|
fill: {
|
||||||
|
type: "solid",
|
||||||
|
color: {
|
||||||
|
r: 100,
|
||||||
|
g: 100,
|
||||||
|
b: 100,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "a",
|
||||||
|
name: "test 2",
|
||||||
|
opacity: 1,
|
||||||
|
rectangle: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
width: 2,
|
||||||
|
color: {
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
dash: [],
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
rotate: 30,
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "ellipse",
|
||||||
|
blendMode: "source-over",
|
||||||
|
ellipse: {
|
||||||
|
center: { x: 300, y: 300 },
|
||||||
|
radiusX: 30,
|
||||||
|
radiusY: 30,
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: "solid",
|
||||||
|
color: {
|
||||||
|
r: 150,
|
||||||
|
g: 0,
|
||||||
|
b: 100,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "a",
|
||||||
|
name: "test ellipse",
|
||||||
|
opacity: 0.5,
|
||||||
|
stroke: {
|
||||||
|
width: 5,
|
||||||
|
color: {
|
||||||
|
r: 0,
|
||||||
|
g: 255,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
dash: [],
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
rotate: 0,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
blendMode: "source-over",
|
||||||
|
text: "Hello world",
|
||||||
|
font: {
|
||||||
|
font: "serif",
|
||||||
|
color: {
|
||||||
|
r: 255,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
size: 72,
|
||||||
|
},
|
||||||
|
textStroke: {
|
||||||
|
color: {
|
||||||
|
r: 100,
|
||||||
|
g: 200,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
dash: [],
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
rectangle: {
|
||||||
|
x: 150,
|
||||||
|
y: 150,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: "solid",
|
||||||
|
color: {
|
||||||
|
r: 150,
|
||||||
|
g: 0,
|
||||||
|
b: 100,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "a",
|
||||||
|
name: "test ellipse",
|
||||||
|
opacity: 0.5,
|
||||||
|
stroke: {
|
||||||
|
width: 5,
|
||||||
|
color: {
|
||||||
|
r: 0,
|
||||||
|
g: 255,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
dash: [],
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
rotate: 30,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "image",
|
||||||
|
blendMode: "source-over",
|
||||||
|
source: image,
|
||||||
|
fill: {
|
||||||
|
type: "solid",
|
||||||
|
color: {
|
||||||
|
r: 100,
|
||||||
|
g: 100,
|
||||||
|
b: 100,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "a",
|
||||||
|
name: "test 2",
|
||||||
|
opacity: 1,
|
||||||
|
rectangle: {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
x: 300,
|
||||||
|
y: 300,
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
width: 2,
|
||||||
|
color: {
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
dash: [],
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
rotate: -15,
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: "test",
|
||||||
|
transform: {
|
||||||
|
rotate: 0,
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
id: "",
|
||||||
|
type: "node",
|
||||||
|
name: "",
|
||||||
|
children: [],
|
||||||
|
transform: {
|
||||||
|
rotate: 0,
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spotlight: [],
|
||||||
|
static: [
|
||||||
|
{
|
||||||
|
type: "rectangle",
|
||||||
|
blendMode: "source-over",
|
||||||
|
fill: {
|
||||||
|
type: "solid",
|
||||||
|
color: {
|
||||||
|
r: 255,
|
||||||
|
g: 255,
|
||||||
|
b: 255,
|
||||||
|
a: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "",
|
||||||
|
name: "",
|
||||||
|
opacity: 1,
|
||||||
|
rectangle: {
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
width: 2,
|
||||||
|
color: {
|
||||||
|
r: 255,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
dash: [],
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
rotate: 0,
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
skewX: 0,
|
||||||
|
skewY: 0,
|
||||||
|
translateX: 0,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.warn(renderer);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue