From 66a5056288faad22e8572fe5caf3d4026defe547 Mon Sep 17 00:00:00 2001 From: Wouter lucas van Boesschoten Date: Tue, 21 May 2024 13:58:17 +0200 Subject: [PATCH] Unload textures if Canvas text is out of bounds --- examples/tests/viewport-events-canvas.ts | 192 ++++++++++++++++++ .../renderers/CanvasTextRenderer.ts | 19 +- 2 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 examples/tests/viewport-events-canvas.ts diff --git a/examples/tests/viewport-events-canvas.ts b/examples/tests/viewport-events-canvas.ts new file mode 100644 index 00000000..f12761dd --- /dev/null +++ b/examples/tests/viewport-events-canvas.ts @@ -0,0 +1,192 @@ +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: -300, + 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, + x: 0, + 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; + } + }; +} diff --git a/src/core/text-rendering/renderers/CanvasTextRenderer.ts b/src/core/text-rendering/renderers/CanvasTextRenderer.ts index f181ea51..5492e85b 100644 --- a/src/core/text-rendering/renderers/CanvasTextRenderer.ts +++ b/src/core/text-rendering/renderers/CanvasTextRenderer.ts @@ -359,6 +359,11 @@ export class CanvasTextRenderer extends TextRenderer { return; } + // if it's not renderable, don't render anything + if (state.isRenderable === false) { + return; + } + if (!state.renderInfo) { state.renderInfo = this.calculateRenderInfo(state); } @@ -681,9 +686,7 @@ export class CanvasTextRenderer extends TextRenderer { // Invalidate renderWindow because the renderInfo changed state.renderWindow = undefined; - - const renderInfo = state.lightning2TextRenderer.calculateRenderInfo(); - return renderInfo; + return state.renderInfo; } getAndCalculateVisibleWindow(state: CanvasTextRendererState): BoundWithValid { @@ -761,11 +764,9 @@ export class CanvasTextRenderer extends TextRenderer { const { stage } = this; const { canvasPages, textW = 0, textH = 0, renderWindow } = state; - if (!canvasPages || !renderWindow) return; const { x, y, scrollY, contain, width, height /*, debug*/ } = state.props; - const elementRect = { x: x, y: y, @@ -913,13 +914,15 @@ export class CanvasTextRenderer extends TextRenderer { ): void { super.setIsRenderable(state, renderable); // Set state object owner from any canvas page textures - state.canvasPages?.forEach((pageInfo) => { - pageInfo.texture?.setRenderableOwner(state, renderable); - }); + // If the state is not renderable, we don't want to keep the texture + if (renderable === false) { + this.destroyState(state); + } } override destroyState(state: CanvasTextRendererState): void { super.destroyState(state); + // Remove state object owner from any canvas page textures state.canvasPages?.forEach((pageInfo) => { const { texture } = pageInfo;