File size: 3,191 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
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)
        // 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();
        }
    }
}