Skip to content

Commit

Permalink
Fix <Image> in Node.js
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Feb 1, 2024
1 parent a034fdf commit 84556fb
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 47 deletions.
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,11 @@ render(<App />, screen);
```tsx
import React from "react";
import { render } from "react-tela/render";
import { DOMMatrix, Image, Path2D, createCanvas } from "@napi-rs/canvas";
import config, { Canvas } from "@napi-rs/canvas";
import { App } from "./App";

const canvas = createCanvas(300, 150);
await render(<App />, canvas, {
Image,
Path2D,
DOMMatrix,
});
const canvas = new Canvas(300, 150);
await render(<App />, canvas, config);

const buffer = canvas.toBuffer("image/png");
// … do something with PNG `buffer`
Expand Down
2 changes: 1 addition & 1 deletion src/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class Group extends Entity {

constructor(opts: GroupProps, root: Root) {
super(opts);
this.subcanvas = root.createCanvas(
this.subcanvas = new root.Canvas(
this.calculatedWidth,
this.calculatedHeight,
);
Expand Down
55 changes: 26 additions & 29 deletions src/image.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Entity, EntityProps } from './entity';
import { Entity, type EntityProps } from './entity';
import type { Root } from './root';
import type { IImage, PercentageString } from './types';

export interface ImageProps extends Omit<EntityProps, 'width' | 'height'> {
Expand All @@ -12,24 +13,27 @@ export interface ImageProps extends Omit<EntityProps, 'width' | 'height'> {
}

export class Image extends Entity {
#root: Root;
#src: string;
image?: IImage;
#image?: IImage;
sx?: number;
sy?: number;
sw?: number;
sh?: number;

constructor(opts: ImageProps) {
constructor(opts: ImageProps, root: Root) {
super({
width: 0,
height: 0,
...opts,
});
this.#src = opts.src;
this.sx = opts.sx;
this.sy = opts.sy;
this.sw = opts.sw;
this.sh = opts.sh;
this.#root = root;
this.#src = opts.src;
this.loadImage();
}

get src() {
Expand All @@ -38,43 +42,36 @@ export class Image extends Entity {

set src(v: string) {
this.#src = v;
if (this.image) {
this.image.src = v;
this.loadImage();
}

async loadImage() {
const img = await this.#root.loadImage(this.#src);
this.#image = img;
if (this.width === 0) {
this.width = img.naturalWidth;
}
if (this.height === 0) {
this.height = img.naturalHeight;
}
this.root?.queueRender();
}

render(): void {
super.render();
let { image, root } = this;
if (!image) {
image = this.image = new root.Image();
image.onload = this.#onload.bind(this);
image.src = this.src;
}
let { root } = this;
const img = this.#image;
if (!img) return;
root.ctx.drawImage(
image,
img,
this.sx ?? 0,
this.sy ?? 0,
this.sw ?? image.naturalWidth,
this.sh ?? image.naturalHeight,
this.sw ?? img.naturalWidth,
this.sh ?? img.naturalHeight,
0,
0,
this.calculatedWidth,
this.calculatedHeight,
);
}

#onload() {
const { image, root } = this;
if (!image) return;
if (this.width === 0) {
this.width = image.naturalWidth;
}
if (this.height === 0) {
this.height = image.naturalHeight;
}
if (root) {
root.queueRender();
}
}
}
2 changes: 1 addition & 1 deletion src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const reconciler = ReactReconciler<
} else if (is('Arc', t)) {
return new Arc(t.props);
} else if (is('Image', t)) {
return new Image(t.props);
return new Image(t.props, root);
} else if (is('Path', t)) {
return new Path(t.props);
} else if (is('Rect', t)) {
Expand Down
25 changes: 16 additions & 9 deletions src/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import type {
} from './types';

export interface RootParams {
createCanvas?: (w: number, h: number) => ICanvas;
Canvas?: new (w: number, h: number) => ICanvas;
DOMMatrix?: new (init?: string | number[]) => IDOMMatrix;
Image?: new () => IImage;
Path2D?: new (path?: string) => IPath2D;
loadImage?: (src: string) => Promise<IImage>;
}

export class Root extends TelaEventTarget {
Expand All @@ -21,8 +21,8 @@ export class Root extends TelaEventTarget {
entities: Entity[];
renderCount: number;
renderQueued: boolean;
Canvas: new (w: number, h: number) => ICanvas;
DOMMatrix: new (init?: string | number[]) => IDOMMatrix;
Image: new () => IImage;
Path2D: new (path?: string) => IPath2D;

constructor(ctx: ICanvasRenderingContext2D, opts: RootParams = {}) {
Expand All @@ -33,10 +33,21 @@ export class Root extends TelaEventTarget {
this.render = this.render.bind(this);
this.renderCount = 0;
this.renderQueued = false;
this.Canvas = opts.Canvas || OffscreenCanvas;
this.DOMMatrix = opts.DOMMatrix || DOMMatrix;
this.Path2D = opts.Path2D || Path2D;
this.Image = opts.Image || Image;
if (opts.createCanvas) this.createCanvas = opts.createCanvas;
if (opts.loadImage) {
this.loadImage = opts.loadImage;
}
}

async loadImage(src: string): Promise<IImage> {
const img = new Image();
await new Promise(res => {
img.onload = res;
img.src = src;
});
return img;
}

then(r?: (value: Event) => void) {
Expand All @@ -45,10 +56,6 @@ export class Root extends TelaEventTarget {
}
}

createCanvas(width: number, height: number): ICanvas {
return new OffscreenCanvas(width, height);
}

clear() {
for (const e of this.entities) {
e._root = null;
Expand Down

0 comments on commit 84556fb

Please sign in to comment.