Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offscreen canvas support #25

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18]
node-version: [18]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default [
'lines-around-comment': 'off',
'lines-between-class-members': 'off',
'max-len': 'off',
'max-lines': 'off',
'max-statements': 'off',
'multiline-comment-style': 'off',
'multiline-ternary': 'off',
Expand Down
4 changes: 2 additions & 2 deletions examples/checkers/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../bin/checkers",
"lib": ["dom", "es2015"],
"module": "node16"
"lib": ["dom", "es2020"],
"module": "es2020"
}
}
1 change: 1 addition & 0 deletions examples/offscreen/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
94 changes: 94 additions & 0 deletions examples/offscreen/bouncing-ball-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
CanvasElementBitmapSizeBinding,
createCanvasRenderingTarget2D,
Size,
} from 'fancy-canvas';
import { BouncingBallRenderer, Position } from './bouncing-ball-renderer.js';

type Canvas = CanvasElementBitmapSizeBinding<boolean>['canvas'];

export class BouncingBallModel {
private readonly _canvas: Canvas;
private readonly _color: string;
private _running: boolean = false;
private _size: Size;
private _mediaSize: Size;
private _renderer: BouncingBallRenderer;
private _position: Position = {
x: 10,
y: 10,
};
private _velocity: Position = {
x: 10,
y: 5,
};

// eslint-disable-next-line max-params
public constructor(
canvas: Canvas,
color: string,
size: Size,
mediaSize: Size,
) {
this._canvas = canvas;
this._color = color;
this._size = size;
this._mediaSize = mediaSize;
this._renderer = new BouncingBallRenderer(this._color);
this.start();
}

public updateSize(
size: Size,
mediaSize: Size,
isOffscreen?: boolean,
): void {
this._size = size;
this._mediaSize = mediaSize;
if (isOffscreen) {
(this._canvas as OffscreenCanvas).height = size.height;
(this._canvas as OffscreenCanvas).width = size.width;
}
}

public start(): void {
this._running = true;
this._animate();
}

public stop(): void {
this._running = false;
}

private _animate(): void {
const newPos: Position = {
x: this._position.x + this._velocity.x,
y: this._position.y + this._velocity.y,
};
if (newPos.x <= 10 || newPos.x >= this._mediaSize.width - 10) {
this._velocity.x *= -1;
newPos.x = Math.min(
Math.max(10, newPos.x),
this._mediaSize.width - 10,
);
}
if (newPos.y <= 10 || newPos.y >= this._mediaSize.height - 10) {
this._velocity.y *= -1;
newPos.y = Math.min(
Math.max(10, newPos.y),
this._mediaSize.height - 10,
);
}
const target = createCanvasRenderingTarget2D({
get2DContext: options => this._canvas.getContext('2d', options),
bitmapSize: this._size,
canvasElementClientSize: this._mediaSize,
});
this._renderer.updatePos(newPos);
this._renderer.draw(target);
this._position = newPos;
if (this._running) {
requestAnimationFrame(this._animate.bind(this));
}
}
}
45 changes: 45 additions & 0 deletions examples/offscreen/bouncing-ball-renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
CanvasRenderingTarget2D,
MediaCoordinatesRenderingScope,
} from 'fancy-canvas';

export interface Position {
x: number;
y: number;
}

export class BouncingBallRenderer {
private readonly _color: string;
private _position: Position | undefined;

public constructor(color: string) {
this._color = color;
}

public updatePos(position: Position) {
this._position = position;
}

public draw(target: CanvasRenderingTarget2D): void {
target.useMediaCoordinateSpace(
(scope: MediaCoordinatesRenderingScope) => {
scope.context.clearRect(
0,
0,
scope.mediaSize.width,
scope.mediaSize.height,
);
scope.context.beginPath();
scope.context.arc(
this._position?.x ?? 0,
this._position?.y ?? 0,
10,
0,
2 * Math.PI,
);
scope.context.fillStyle = this._color;
scope.context.fill();
},
);
}
}
40 changes: 40 additions & 0 deletions examples/offscreen/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto,
Ubuntu, sans-serif;
}

#header,
#content,
.actions {
max-width: 680px;
margin: 0 auto;
}

.actions {
display: flex;
flex-direction: row;
gap: 12px;
margin-block: 12px;
}

#header {
font-size: 36px;
width: 100%;
}

#content {
display: flex;
flex-direction: column;
gap: 24px;
}

.example h3 {
color: var(--example-color);
margin: 12px 0;
}

.example-canvas {
border: 2px solid var(--example-color);
height: 150px;
width: 100%;
}
41 changes: 41 additions & 0 deletions examples/offscreen/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>Fancy Canvas - Offscreen Demo</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div>
<h2 id="header">Offscreen demos</h2>
</div>
<div class="actions">
<button id="start-button">Start</button>
<button id="pause-button">Pause</button>
<button id="task-button">Long Task on Main Thread</button>
</div>
<div id="content">
<div class="example" style="--example-color: #f23645">
<h3>Offscreen Disabled</h3>
<canvas
id="offscreen-canvas-disabled"
class="example-canvas"
></canvas>
</div>
<div class="example" style="--example-color: #2962ff">
<h3>Offscreen Main Thread</h3>
<canvas
id="offscreen-canvas-main"
class="example-canvas"
></canvas>
</div>
<div class="example" style="--example-color: #089981">
<h3>Offscreen Worker Thread</h3>
<canvas
id="offscreen-canvas-worker"
class="example-canvas"
></canvas>
</div>
</div>
</body>
<script src="index.js"></script>
</html>
Loading
Loading