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; } } }