diff --git a/src/Project.ts b/src/Project.ts index 72624a43..1762363b 100644 --- a/src/Project.ts +++ b/src/Project.ts @@ -1,7 +1,6 @@ import {Stage, StageConfig} from 'konva/lib/Stage'; import {Rect} from 'konva/lib/shapes/Rect'; import {Layer} from 'konva/lib/Layer'; -import {Scene, SceneRunner} from './Scene'; import {Vector2d} from 'konva/lib/types'; import {Konva} from 'konva/lib/Global'; import { @@ -39,7 +38,6 @@ export class Project extends Stage { } private runner: Generator; - private scenes: Scene[] = []; public constructor( private runnerFactory: (project: Project) => Generator, @@ -67,22 +65,15 @@ export class Project extends Stage { fill: '#141414', }); - const backgroundLayer = new Layer(); + const backgroundLayer = new Layer({name: 'background'}); backgroundLayer.add(this.background); this.add(backgroundLayer); } - public createScene(runner: SceneRunner) { - const scene = new Scene(this, new Layer(), runner); - this.add(scene.layer); - this.scenes.push(scene); - - return scene; - } - public start() { - this.scenes.forEach(scene => scene.layer.destroy()); - this.scenes = []; + this.getLayers().forEach( + layer => layer.hasName('background') || layer.destroy(), + ); this.frame = 0; this.runner = this.threads((join, cancel) => { this.join = join; diff --git a/src/Scene.ts b/src/Scene.ts index d06debfc..ed8f4a01 100644 --- a/src/Scene.ts +++ b/src/Scene.ts @@ -2,17 +2,58 @@ import {Layer} from 'konva/lib/Layer'; import {Project} from './Project'; export interface SceneRunner { - (layer: Layer, project: Project): Generator; + (layer: Scene, project: Project): Generator; } -export class Scene { +export enum SceneState { + Pending, + Started, + Finished, + Disposed, +} + +export class Scene extends Layer { + private state: SceneState = SceneState.Pending; + public constructor( public readonly project: Project, - public readonly layer: Layer, private runner: SceneRunner, - ) {} + ) { + super(); + } - public run(): Generator { - return this.runner(this.layer, this.project); + public *run(): Generator { + this.project.add(this); + yield* this.runner(this, this.project); + this.state = SceneState.Finished; + // @ts-ignore + while (this.state !== SceneState.Disposed) { + yield; + } + } + + public activate() { + this.state = SceneState.Started; + } + + public *start(): Generator { + while (this.state === SceneState.Pending) { + yield; + } + } + + public *end(): Generator { + while (this.state !== SceneState.Finished) { + yield; + } + } + + public deactivate() { + this.state = SceneState.Finished; + } + + public dispose() { + this.state = SceneState.Disposed; + this.destroy(); } } diff --git a/src/animations/threads.ts b/src/animations/threads.ts index 9954f6d0..5d81d5a8 100644 --- a/src/animations/threads.ts +++ b/src/animations/threads.ts @@ -12,8 +12,9 @@ export interface ThreadsFactory { export function* threads(factory: ThreadsFactory): Generator { let runners: Generator[] = []; - let cancelled= new Set(); + let cancelled = new Set(); let values = new Map(); + let children = new Map(); const join = function* (...tasks: Generator[]): Generator { while (tasks.find(runner => runners.includes(runner))) { yield; @@ -28,26 +29,36 @@ export function* threads(factory: ThreadsFactory): Generator { const newRunners = []; for (let i = 0; i < runners.length; i++) { const runner = runners[i]; + const childTasks = children.get(runner) ?? []; + children.set(runner, childTasks); + if (cancelled.has(runner)) { cancelled.delete(runner); + children.delete(runner); + cancel(...childTasks); continue; } const result = runner.next(values.get(runner)); + values.delete(runner); + + if (result.done) { + cancelled.delete(runner); + children.delete(runner); + cancel(...childTasks); + continue; + } if (typeof result.value?.then === 'function') { - if (!result.done) { - runners.push(runner); - } values.set(runner, yield result.value); + runners.push(runner); } else if (result.value?.next) { - if (!result.done) { - runners.push(runner); - } values.set(runner, result.value); cancelled.delete(result.value); + childTasks.push(result.value); + runners.push(runner); runners.push(result.value); - } else if (!result.done) { + } else { newRunners.push(runner); } } diff --git a/src/components/LayoutText.ts b/src/components/LayoutText.ts index 8f73a456..c03275fc 100644 --- a/src/components/LayoutText.ts +++ b/src/components/LayoutText.ts @@ -146,7 +146,7 @@ export class LayoutText extends Text implements ILayoutNode { const toWidth = this.getLayoutSize({text, minWidth: 0}).width; this.overrideWidth = fromWidth; - yield* this.project.tween(3, value => { + yield* this.project.tween(0.3, value => { this.overrideWidth = value.easeInOutCubic(fromWidth, toWidth); this.setText(value.text(fromText, text, value.easeInOutCubic())); }); diff --git a/src/components/ThreeView.ts b/src/components/ThreeView.ts index a9e1550e..7020da10 100644 --- a/src/components/ThreeView.ts +++ b/src/components/ThreeView.ts @@ -19,26 +19,28 @@ interface Pool { dispose(object: T): void; } -class CanvasPool implements Pool { - private pool: HTMLCanvasElement[] = []; +class RendererPool implements Pool { + private pool: THREE.WebGLRenderer[] = []; - public borrow(): HTMLCanvasElement { + public borrow(): THREE.WebGLRenderer { if (this.pool.length) { return this.pool.pop(); } else { - return Util.createCanvasElement(); + return new THREE.WebGLRenderer({ + canvas: Util.createCanvasElement(), + antialias: true, + }); } } - public dispose(canvas: HTMLCanvasElement) { - this.pool.push(canvas); + public dispose(renderer: THREE.WebGLRenderer) { + this.pool.push(renderer); } } -const canvasPool3D = new CanvasPool(); +const rendererPool = new RendererPool(); export class ThreeView extends LayoutShape { - private readonly threeCanvas: HTMLCanvasElement; private readonly renderer: THREE.WebGLRenderer; private readonly context: WebGLRenderingContext; @@ -46,12 +48,7 @@ export class ThreeView extends LayoutShape { public constructor(config?: ThreeViewConfig) { super(config); - this.threeCanvas = canvasPool3D.borrow(); - - this.renderer = new THREE.WebGLRenderer({ - canvas: this.threeCanvas, - antialias: true, - }); + this.renderer = rendererPool.borrow(); this.context = this.renderer.getContext(); this.handleCanvasSizeChange(); @@ -133,8 +130,7 @@ export class ThreeView extends LayoutShape { } destroy(): this { - this.renderer.dispose(); - canvasPool3D.dispose(this.threeCanvas); + rendererPool.dispose(this.renderer); return super.destroy(); } @@ -192,7 +188,7 @@ export class ThreeView extends LayoutShape { ), ); context._context.drawImage( - this.threeCanvas, + this.renderer.domElement, 0, 0, size.width * scale, diff --git a/src/flow/all.ts b/src/flow/all.ts index de7b66ea..06a53544 100644 --- a/src/flow/all.ts +++ b/src/flow/all.ts @@ -1,3 +1,4 @@ +// TODO Support threading export function* all(...sequences: Generator[]): Generator { while (sequences.length > 0) { for (let i = sequences.length - 1; i >= 0; i--) { @@ -5,6 +6,9 @@ export function* all(...sequences: Generator[]): Generator { if (result.done) { sequences.splice(i, 1); } + if (result.value) { + console.warn('Unexpected value: ', result.value); + } } yield;