@equinor/esv-intersection
Version:
Intersection component package with testing and automatic documentation.
188 lines (155 loc) • 4.74 kB
text/typescript
import {
autoDetectRenderer,
AutoDetectOptions,
Container,
ContainerChild,
Renderer,
RendererType,
} from 'pixi.js';
import { Layer, LayerOptions } from './Layer';
import {
OnMountEvent,
OnRescaleEvent,
OnResizeEvent,
OnUnmountEvent,
} from '../../interfaces';
import { DEFAULT_LAYER_HEIGHT, DEFAULT_LAYER_WIDTH } from '../../constants';
// PixiRenderApplication has many similarities with PIXI.Application,
// but an important distinction is that it does not run the TickerPlugin.
// We only want to re-render on data changes
// The plugin we are trying to avoid:
// https://github.com/pixijs/pixijs/blob/dev/packages/ticker/src/TickerPlugin.ts
export class PixiRenderApplication {
stage: Container | undefined;
renderer: Renderer<HTMLCanvasElement> | undefined;
async init(pixiRenderOptions?: Partial<AutoDetectOptions>) {
const options = {
width: DEFAULT_LAYER_WIDTH,
height: DEFAULT_LAYER_HEIGHT,
antialias: true,
backgroundAlpha: 0,
clearBeforeRender: true,
// autoResize: true,
preserveDrawingBuffer: true,
...pixiRenderOptions,
};
this.renderer = await autoDetectRenderer(options);
this.stage = new Container();
}
get canvas() {
return this.renderer?.canvas;
}
render() {
if (this.stage != null) {
this.renderer?.render(this.stage);
}
}
}
export abstract class PixiLayer<T> extends Layer<T> {
private pixiViewContainer: HTMLElement | undefined;
private ctx: PixiRenderApplication;
private container: Container;
constructor(
ctx: PixiRenderApplication,
id?: string,
options?: LayerOptions<T>,
) {
super(id, options);
this.ctx = ctx;
this.container = new Container();
this.ctx.stage?.addChild(this.container);
}
render(): void {
this.ctx.render();
}
addChild(child: ContainerChild) {
this.container.addChild(child);
}
clearLayer() {
const children = this.container.removeChildren();
children.forEach(child => {
child.destroy();
});
}
override onMount(event: OnMountEvent) {
super.onMount(event);
this.pixiViewContainer =
this.element?.querySelector('#webgl-layer') ?? undefined;
if (!this.pixiViewContainer) {
this.pixiViewContainer = document.createElement('div');
this.pixiViewContainer.setAttribute('id', `${this.id}`);
this.pixiViewContainer.setAttribute('class', 'webgl-layer');
if (this.ctx.canvas != null) {
this.pixiViewContainer.appendChild(this.ctx.canvas);
}
this.element?.appendChild(this.pixiViewContainer);
this.updateStyle();
}
}
override onUnmount(event?: OnUnmountEvent) {
super.onUnmount(event);
this.clearLayer();
this.ctx.stage?.removeChild(this.container);
this.container.destroy();
this.pixiViewContainer?.remove();
this.pixiViewContainer = undefined;
}
override onResize(event: OnResizeEvent): void {
super.onResize(event);
this.ctx.renderer?.resize(event.width, event.height);
}
override onRescale(event: OnRescaleEvent): void {
super.onRescale(event);
const flippedX = event.xBounds[0] > event.xBounds[1];
const flippedY = event.yBounds[0] > event.yBounds[1];
this.setContainerPosition(event.xScale(0), event.yScale(0));
this.setContainerScale(
event.xRatio * (flippedX ? -1 : 1),
event.yRatio * (flippedY ? -1 : 1),
);
}
protected setContainerPosition(x?: number, y?: number) {
this.container.position.set(x, y);
}
protected setContainerScale(x?: number, y?: number) {
this.container.scale.set(x, y);
}
updateStyle(visible?: boolean): void {
const isVisible = visible || this.isVisible;
const interactive = this.interactive ? 'auto' : 'none';
this.container.visible = isVisible;
const styles = [
['position', 'absolute'],
['pointer-events', `${interactive}`],
['z-index', `${this.order}`],
['opacity', `${this.opacity}`],
]
.map(pair => pair.join(':'))
.join(';');
this.pixiViewContainer?.setAttribute('style', styles);
}
override setVisibility(visible: boolean, layerId?: string): void {
super.setVisibility(visible, layerId);
if (this.pixiViewContainer) {
this.updateStyle(visible);
}
}
onOpacityChanged(_opacity: number): void {
if (this.pixiViewContainer) {
this.updateStyle();
}
}
onOrderChanged(_order: number): void {
if (this.pixiViewContainer) {
this.updateStyle();
}
}
onInteractivityChanged(_interactive: boolean): void {
if (this.pixiViewContainer) {
this.updateStyle();
}
}
renderType(): RendererType | undefined {
return this.ctx.renderer?.type;
}
}