| 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); |
| |
|
|
| 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) => { |
| |
| object.geometry && object.geometry.dispose(); |
| |
| object.texture && object.texture.dispose(); |
| |
| 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; |
| } |
| } |
| } |
|
|