File size: 2,702 Bytes
5b324f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import * as THREE from "three";
import { debounce } from "@/utils";

export class ThreeSceneBase {
	protected canvasEl: HTMLCanvasElement;
	protected containerEl: HTMLDivElement | undefined;
	protected renderer: THREE.WebGLRenderer;
	protected scene: THREE.Scene;
	protected camera: THREE.PerspectiveCamera;
	protected requestAnimationFrameId: number = -1;
	private resizeObserver: ResizeObserver | undefined;

	constructor(canvas: HTMLCanvasElement) {
		this.canvasEl = canvas;
		this.renderer = new THREE.WebGLRenderer({ canvas });
		this.renderer.setSize(canvas.clientWidth, canvas.clientHeight);
		this.renderer.setPixelRatio(window.devicePixelRatio);
		this.scene = new THREE.Scene();
		this.camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 1000);
		// this.renderLoop();

		const parentEl = canvas.parentElement;
		if (parentEl) {
			const tempDivEl = document.createElement("div");
			Object.assign(tempDivEl.style, {
				position: "relative",
				width: "100%",
				height: "100%",
			});
			Object.assign(canvas.style, {
				position: "absolute ",
				width: "100%",
				height: "100%",
				left: "0",
				top: "0",
			});
			tempDivEl.append(canvas);
			parentEl.append(tempDivEl);
			this.containerEl = tempDivEl;
			const callback: ResizeObserverCallback = (entries) => {
				if (entries.length === 0) return;
				const _parentEl = entries[0].target;
				if (_parentEl) {
					this.renderer.setSize(_parentEl.clientWidth, _parentEl.clientHeight);
					this.camera.aspect = _parentEl.clientWidth / _parentEl.clientHeight;
					this.camera.updateProjectionMatrix();
				}
			};
			const resizeObserver = new ResizeObserver(debounce(callback.bind(this), 100));
			resizeObserver.observe(tempDivEl, {});
			this.resizeObserver = resizeObserver;
		}
	}

	protected setLoadingMaskVisible(visible: boolean) {
		if (this.containerEl) {
			if (visible) this.containerEl.setAttribute("loading", "");
			else this.containerEl.removeAttribute("loading");
		}
	}

	protected render() {
		this.renderer.render(this.scene, this.camera);
	}

	protected destroy() {
		this.resizeObserver && this.resizeObserver.disconnect();
		cancelAnimationFrame(this.requestAnimationFrameId);
		this.scene.traverse((object) => {
			//@ts-ignore
			object.geometry && object.geometry.dispose();
			//@ts-ignore
			object.texture && object.texture.dispose();
			//@ts-ignore
			object.material && object.material.dispose();
		});
		this.scene.clear();
		this.renderer.dispose();
		this.renderer.forceContextLoss();
		let gl = this.renderer.domElement.getContext("webgl");
		if (gl) {
			const e = gl.getExtension("WEBGL_lose_context");
			e && e.loseContext();
			gl = null;
		}
	}
}