| 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.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(); |
| } |
| } |
| } |