What’s on our mind?

Collection of articles, design, site, and resources made by designers and publisher @Menu View

* three.js index4.html
class Stage {
    constructor() {
        this.renderParam = {
            clearColor: 0x666666,
            width: window.innerWidth,
            height: window.innerHeight
        };

        this.cameraParam = {
            left: -1,
            right: 1,
            top: 1,
            bottom: 1,
            near: 0,
            far: -1
        };

        this.scene = null;
        this.camera = null;
        this.renderer = null;
        this.geometry = null;
        this.material = null;
        this.mesh = null;

        this.isInitialized = false;
    }

    init() {
        this._setScene();
        this._setRender();
        this._setCamera();

        this.isInitialized = true;
    }

    _setScene() {
        this.scene = new THREE.Scene();
    }

    _setRender() {
        this.renderer = new THREE.WebGLRenderer({
            canvas: document.getElementById("webgl-canvas"),
        });
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setClearColor(new THREE.Color(this.renderParam.clearColor));
        this.renderer.setSize(this.renderParam.width, this.renderParam.height);
    }

    _setCamera() {
        if (!this.isInitialized) {
            this.camera = new THREE.PerspectiveCamera(
                this.cameraParam.left,
                this.cameraParam.right,
                this.cameraParam.top,
                this.cameraParam.bottom,
                this.cameraParam.near,
                this.cameraParam.far
            );
        }

        const windowWidth = window.innerWidth;
        const windowHeight = window.innerHeight;

        this.camera.aspect = windowWidth / windowHeight;

        this.camera.updateProjectionMatrix();
        this.renderer.setSize(windowWidth, windowHeight);
    }

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

    onResize() {
        this._setCamera();
    }

    onRaf() {
        this._render();
    }
}

class Mesh {
    constructor(stage) {
        this.mouse = new THREE.Vector2(0.5, 0.5);
        this.targetRadius = 0.1;

        this.uniforms = {
            uAspect: {
                type: "f", value: window.innerWidth / window.innerHeight
            },
            uTime: {
                type: "f", value: 0.0
            },
            uMouse: {
                value: new THREE.Vector2(0.0, 0.0)
            },
            uRadius: {
                type: "f",
                value: this.targetRadius
            }
        };
        this.stage = stage;
        this.mesh = null;
    }

    init() {
        this._setMesh();
    }

    _setMesh() {
        const geometry = new THREE.PlaneGeometry(2, 2, 1, 1);
        const material = new THREE.RawShaderMaterial({
            vertexShader: document.getElementById("js-vertex-shader").textContent,
            fragmentShader: document.getElementById("js-fragment-shader").textContent,
            uniforms: this.uniforms,
            side: THREE.DoubleSide
        });
        this.mesh = new THREE.Mesh(geometry, material);
        this.stage.scene.add(this.mesh);
    }

    _render() {
        const sec = performance.now() / 1000;
        this.uniforms.uTime.value = sec;
        this.uniforms.uMouse.value.lerp(this.mouse, 0.25);
        this.uniforms.uRadius.value += (this.targetRadius - this.uniforms.uRadius.value) * 0.2;
    }

    onResize() {
        this.uniforms.uAspect.value = window.innerWidth / window.innerHeight;
    }

    onMouseMove(x, y) {
        this.mouse.x = x / window.innerWidth;
        this.mouse.y = 1.0 - (y / window.innerHeight);
    }
    onMouseEnter(x, y) {
        this.onMouseMove(x, y);
        this.targetRadius = 0.20;
    }
    onMouseLeave(x, y) {
        this.onMouseMove(x, y);
        this.targetRadius = 0.1;
    }

    onRaf() {
        this._render();
    }
}

(() => {
    const stage = new Stage();
    const mesh = new Mesh(stage);

    stage.init();
    mesh.init();

    window.addEventListener("resize", () => {
        stage.onResize();
        mesh.onResize();
    });

    window.addEventListener("mousemove", (e) => {
        mesh.onMouseMove(e.clientX, e.clientY);
    });

    document.querySelector("h1").addEventListener("mouseenter", (e) => {
        mesh.onMouseEnter(e.clientX, e.clientY);
    });

    document.querySelector("h1").addEventListener("mouseleave", (e) => {
        mesh.onMouseLeave(e.clientX, e.clientY);
    });

    const _raf = () => {
        window.requestAnimationFrame(() => {
            stage.onRaf();
            mesh.onRaf();

            _raf();
        });
    };

    _raf();
})();