feat: added useContext and useContextAfter hooks (#63)

The `useContext` hooks give direct access the the context to allow for
transforming, clipping, and drawing to the canvas directly. The
`useContext` hook runs before the scene is rendered, and so is most
appropriate for altering the render through transforms and clipping. The
`useContextAfter` hook runs after the scene is rendered, and so is most
appropriate for altering the image data or drawing to the canvas.

Fixes #58
This commit is contained in:
Ross Esmond
2022-07-14 09:06:51 -05:00
committed by GitHub
parent bcfc661644
commit 352e131043
6 changed files with 74 additions and 1 deletions

View File

@@ -157,8 +157,12 @@ export class Project {
);
}
this.context.save();
this.previousScene?.render(this.context, this.canvas);
this.context.restore();
this.context.save();
this.currentScene.current?.render(this.context, this.canvas);
this.context.restore();
}
public reload(runners: SceneDescription[]) {

View File

@@ -64,6 +64,23 @@ export abstract class GeneratorScene<T>
}
private readonly thread = new ValueDispatcher<Thread>(null);
public get onBeforeRendered() {
return this.beforeRendered.subscribable;
}
protected readonly beforeRendered =
new EventDispatcher<CanvasRenderingContext2D>();
public get onAfterRendered() {
return this.afterRendered.subscribable;
}
protected readonly afterRendered =
new EventDispatcher<CanvasRenderingContext2D>();
public get onReset() {
return this.afterReset.subscribable;
}
private readonly afterReset = new EventDispatcher<void>();
private previousScene: Scene = null;
private runner: ThreadGenerator;
private state: SceneState = SceneState.Initial;
@@ -168,7 +185,7 @@ export abstract class GeneratorScene<T>
},
);
this.state = SceneState.Initial;
setScene(this);
this.afterReset.dispatch();
await this.next();
}

View File

@@ -125,7 +125,9 @@ export class KonvaScene
sceneCanvas.getContext()._context = context;
}
this.beforeRendered.dispatch(context);
this.view.drawScene(sceneCanvas);
this.afterRendered.dispatch(context);
}
public reset(previousScene: Scene = null) {

View File

@@ -108,6 +108,27 @@ export interface Scene<T = unknown> {
*/
get onRecalculated(): SubscribableEvent<void>;
/**
* Triggered before the scene is rendered with the Context2D.
*
* @event CanvasRenderingContext2D
*/
get onBeforeRendered(): SubscribableEvent<CanvasRenderingContext2D>;
/**
* Triggered after the scene is rendered with the Context2D.
*
* @event CanvasRenderingContext2D
*/
get onAfterRendered(): SubscribableEvent<CanvasRenderingContext2D>;
/**
* Triggered when the scene is reset.
*
* @event void
*/
get onReset(): SubscribableEvent<void>;
/**
* Render the scene onto a canvas.
*

View File

@@ -3,3 +3,4 @@ export * from './useAnimator';
export * from './useProject';
export * from './useRef';
export * from './useScene';
export * from './useContext';

28
src/utils/useContext.ts Normal file
View File

@@ -0,0 +1,28 @@
import {useScene} from './useScene';
import type {Scene} from '../scenes';
function subscribe(fn: (scene: Scene) => () => void) {
const scene = useScene();
if (scene == null) {
throw new Error('no scene found from which to use the context');
}
const unsubScene = fn(scene);
const unsubReset = scene.onReset.subscribe(unsubAll);
function unsubAll() {
unsubScene();
unsubReset();
}
return unsubAll;
}
export function useContext(
callback: (ctx: CanvasRenderingContext2D) => void,
): () => void {
return subscribe(scene => scene.onBeforeRendered.subscribe(callback));
}
export function useContextAfter(
callback: (ctx: CanvasRenderingContext2D) => void,
): () => void {
return subscribe(scene => scene.onAfterRendered.subscribe(callback));
}