@luma.gl/engine
Version:
3D Engine Components for luma.gl
100 lines (84 loc) • 3.15 kB
text/typescript
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import {luma, Adapter, Device} from '@luma.gl/core';
import {AnimationLoopTemplate} from './animation-loop-template';
import {AnimationLoop, AnimationLoopProps} from './animation-loop';
import type {AnimationProps} from './animation-props';
export type MakeAnimationLoopProps = Omit<
AnimationLoopProps,
'onCreateDevice' | 'onInitialize' | 'onRedraw' | 'onFinalize'
> & {
/** List of adapters to use when creating the device */
adapters?: Adapter[];
};
/**
* Instantiates an animation loop and initializes it with the template.
* @note The application needs to call `start()` on the returned animation loop to start the rendering loop.
*/
export function makeAnimationLoop(
AnimationLoopTemplateCtor: typeof AnimationLoopTemplate,
props?: MakeAnimationLoopProps
): AnimationLoop {
let renderLoop: AnimationLoopTemplate | null = null;
const device =
props?.device ||
luma.createDevice({id: 'animation-loop', adapters: props?.adapters, createCanvasContext: true});
// Create an animation loop;
const animationLoop = new AnimationLoop({
...props,
device,
async onInitialize(animationProps: AnimationProps): Promise<unknown> {
clearError(animationProps.animationLoop.device);
try {
// @ts-expect-error abstract to prevent instantiation
renderLoop = new AnimationLoopTemplateCtor(animationProps);
// Any async loading can be handled here
return await renderLoop?.onInitialize(animationProps);
} catch (error) {
console.error(error);
setError(animationProps.animationLoop.device, error as Error);
return null;
}
},
onRender: (animationProps: AnimationProps) => renderLoop?.onRender(animationProps),
onFinalize: (animationProps: AnimationProps) => renderLoop?.onFinalize(animationProps)
});
// @ts-expect-error Hack: adds info for the website to find
animationLoop.getInfo = () => {
// @ts-ignore
// eslint-disable-next-line no-invalid-this
return this.AnimationLoopTemplateCtor.info;
};
return animationLoop;
}
function setError(device: Device | null, error: Error): void {
if (!device) {
return;
}
const canvas = device.getDefaultCanvasContext().canvas;
if (canvas instanceof HTMLCanvasElement) {
canvas.style.overflow = 'visible';
let errorDiv = document.getElementById('animation-loop-error');
errorDiv?.remove();
errorDiv = document.createElement('h1');
errorDiv.id = 'animation-loop-error';
errorDiv.innerHTML = error.message;
errorDiv.style.position = 'absolute';
errorDiv.style.top = '10px'; // left: 50%; transform: translate(-50%, -50%);';
errorDiv.style.left = '10px';
errorDiv.style.color = 'black';
errorDiv.style.backgroundColor = 'red';
canvas.parentElement?.appendChild(errorDiv);
// canvas.style.position = 'absolute';
}
}
function clearError(device: Device | null): void {
if (!device) {
return;
}
const errorDiv = document.getElementById('animation-loop-error');
if (errorDiv) {
errorDiv.remove();
}
}