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

Fix Canvas2D fonts performance #253

Closed
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
1 change: 1 addition & 0 deletions examples/tests/text-baseline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ function generateBaselineTest(

const baselineNode = renderer.createTextNode({
...nodeProps,
parent: renderer.root,
});
const dimensions = await waitForLoadedDimensions(baselineNode);

Expand Down
139 changes: 139 additions & 0 deletions examples/tests/text-canvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import type { ExampleSettings } from '../common/ExampleSettings.js';

const Colors = {
Black: 0x000000ff,
Red: 0xff0000ff,
Green: 0x00ff00ff,
Blue: 0x0000ffff,
Magenta: 0xff00ffff,
Gray: 0x7f7f7fff,
White: 0xffffffff,
};

const randomIntBetween = (from: number, to: number) =>
Math.floor(Math.random() * (to - from + 1) + from);

/**
* Tests that Single-Channel Signed Distance Field (SSDF) fonts are rendered
* correctly.
*
* Text that is thinner than the certified snapshot may indicate that the
* SSDF font atlas texture was premultiplied before being uploaded to the GPU.
*
* @param settings
* @returns
*/
export default async function test(settings: ExampleSettings) {
const { renderer, testRoot } = settings;

// Set a smaller snapshot area
// testRoot.width = 200;
// testRoot.height = 200;
// testRoot.color = 0xffffffff;

const nodes: any[] = [];

const renderNode = (t: string) => {
const node = renderer.createTextNode({
x: Math.random() * 1900,
y: Math.random() * 1080,
text: 'CANVAS ' + t,
fontFamily: 'sans-serif',
parent: testRoot,
fontSize: 80,
});

nodes.push(node);

// pick random color from Colors
node.color =
Object.values(Colors)[
randomIntBetween(0, Object.keys(Colors).length - 1)
] || 0xff0000ff;
};

const spawn = (amount = 100) => {
for (let i = 0; i < amount; i++) {
renderNode(i.toString());
}
};

const despawn = (amount = 100) => {
for (let i = 0; i < amount; i++) {
const node = nodes.pop();
node.destroy();
}
};

const move = () => {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
node.x = randomIntBetween(0, 1600);
node.y = randomIntBetween(0, 880);
}
};

const newColor = () => {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
node.color =
Object.values(Colors)[
randomIntBetween(0, Object.keys(Colors).length - 1)
] || 0x000000ff;
}
};

let animating = false;
const animate = () => {
animating = !animating;

const animateNode = (node: any) => {
nodes.forEach((node) => {
node
.animate(
{
x: randomIntBetween(20, 1740),
y: randomIntBetween(20, 900),
rotation: Math.random() * Math.PI,
},
{
duration: 3000,
easing: 'ease-out',
// loop: true,
stopMethod: 'reverse',
},
)
.start();
});
};

const animateRun = () => {
if (animating) {
for (let i = 0; i < nodes.length; i++) {
animateNode(nodes[i]);
}
setTimeout(animateRun, 3050);
}
};

animateRun();
};

window.addEventListener('keydown', (event) => {
if (event.key === 'ArrowUp') {
spawn();
} else if (event.key === 'ArrowDown') {
despawn();
} else if (event.key === 'ArrowLeft') {
move();
} else if (event.key === 'ArrowRight') {
move();
} else if (event.key === '1') {
newColor();
} else if (event.key === ' ') {
animate();
}
});

spawn();
}
191 changes: 191 additions & 0 deletions examples/tests/viewport-events-canvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import type { IAnimationController } from '../../dist/exports/main-api.js';
import type { ExampleSettings } from '../common/ExampleSettings.js';
import test from './alpha-blending.js';

export default async function ({ renderer, testRoot }: ExampleSettings) {
const instructionText = renderer.createTextNode({
text: 'Press space to start animation, arrow keys to move, enter to reset',
fontSize: 30,
x: 10,
y: 960,
fontFamily: 'Ubuntu-ssdf',
parent: testRoot,
});

const redStatus = renderer.createTextNode({
text: 'Red Status: ',
fontSize: 30,
x: 10,
y: 50,
fontFamily: 'Ubuntu-ssdf',
parent: testRoot,
});

const blueStatus = renderer.createTextNode({
text: 'Blue Status: ',
fontSize: 30,
x: 10,
y: 10,
fontFamily: 'Ubuntu-ssdf',
parent: testRoot,
});

const boundaryRect = renderer.createNode({
x: 1920 / 2 - (1920 * 0.75) / 2,
y: 1080 / 2 - (1080 * 0.75) / 2,
width: 1440,
height: 810,
color: 0x000000ff,
clipping: true,
parent: testRoot,
});

const redText = renderer.createTextNode({
x: 500,
y: 305,
alpha: 1,
width: 200,
height: 200,
color: 0xff0000ff,
pivot: 0,
text: 'red',
fontSize: 80,
fontFamily: 'sans-serif',
parent: boundaryRect,
});

redText.on('outOfBounds', () => {
console.log('red text out of bounds');
redStatus.text = 'Red Status: text out of bounds';
redStatus.color = 0xff0000ff;
});

redText.on('inViewport', () => {
console.log('red text in view port');
redStatus.text = 'Red Status: text in view port';
redStatus.color = 0x00ff00ff;
});

redText.on('inBounds', () => {
console.log('red text inside render bounds');
redStatus.text = 'Red Status: text in bounds';
redStatus.color = 0xffff00ff;
});

const blueText = renderer.createTextNode({
x: 1920 / 2 - 200,
y: 100,
alpha: 1,
width: 200,
height: 200,
color: 0x0000ffff,
pivot: 0,
text: 'blue',
fontSize: 80,
fontFamily: 'sans-serif',
parent: testRoot,
});

blueText.on('outOfBounds', () => {
console.log('blue text ouf ot bounds');
blueStatus.text = 'Blue Status: blue text out of bounds';
blueStatus.color = 0xff0000ff;
});

blueText.on('inViewport', () => {
console.log('blue text in view port');
blueStatus.text = 'Blue Status: blue text in view port';
blueStatus.color = 0x00ff00ff;
});

blueText.on('inBounds', () => {
console.log('blue text inside render bounds');
blueStatus.text = 'Blue Status: blue text in bounds';
blueStatus.color = 0xffff00ff;
});

let runAnimation = false;
const animate = async () => {
redText
.animate(
{
x: -500,
},
{
duration: 4000,
},
)
.start();

await blueText
.animate(
{
x: -1200,
},
{
duration: 4000,
},
)
.start()
.waitUntilStopped();

redText.x = 1920 + 400;
blueText.x = 1920 + 400;

redText
.animate(
{
x: 520,
},
{
duration: 4000,
},
)
.start();

await blueText
.animate(
{
x: 1920 / 2 - 200,
},
{
duration: 4000,
},
)
.start()
.waitUntilStopped();

if (runAnimation) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
setTimeout(animate, 2000);
}
};

const moveModifier = 10;
window.onkeydown = (e) => {
if (e.key === ' ') {
runAnimation = !runAnimation;

if (runAnimation) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
animate();
}
}

if (e.key === 'ArrowRight') {
redText.x += moveModifier;
blueText.x += moveModifier;
}

if (e.key === 'ArrowLeft') {
redText.x -= moveModifier;
blueText.x -= moveModifier;
}

if (e.key === 'Enter') {
runAnimation = false;
redText.x = 520;
blueText.x = 1920 / 2 - 200;
}
};
}
Loading
Loading