What’s on our mind?

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

index41
const PX_RATIO = window.devicePixelRatio

Math.map = (n, start, stop, start2, stop2) => {
    const newval = (n - start) / (stop - start) * (stop2 - start2) + start2
    return newval
}

Math.dist = (a, b) => {
    let dx = a.x - b.x,
        dy = a.y - b.y;
    return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
}

class Text {
    constructor(txt, args) {
        if (args === undefined) args = {}
        this.canvas = document.createElement("canvas")
        document.body.appendChild(this.canvas)
        this.canvas.style.visibility = "hidden"
        this.context = this.canvas.getContext("2d")
        this.center = {
            x: 0,
            y: 0
        }
        this.pos = {
            x: 0,
            y: 0
        }
        this.mouse = {
            x: window.innerWidth / 2,
            y: window.innerHeight / 2
        }
        this.fontSize = 100
        this.lineHeight = this.fontSize * 0.9
        this.font = this.fontSize + "px Oswald"
        this.fillStyle = args.color || "#fff"
        this.txt = txt.toUpperCase() || "Oswald"
        this.friction = 0.1
        this.addEvents()
        this.resize()
    }

    get texture() {
        return this.canvas
    }

    resize() {
        this.render()
        this.height = this.fontSize
        this.canvas.width = 650
        this.canvas.height = this.height
        this.center = {
            x: (window.innerWidth / 2) * PX_RATIO,
            y: (window.innerHeight / 2) * PX_RATIO
        }
        this.diagonal = Math.dist(this.center, {
            x: window.innerWidth * PX_RATIO,
            y: window.innerHeight * PX_RATIO
        })
    }

    addEvents() {
        document.addEventListener('mousemove', this.onMouseMove.bind(this), false)
        document.addEventListener('touchmove', this.onMouseMove.bind(this), false)
    }

    onMouseMove(e) {
        if (e.touches) e = e.touches[0]
        this.mouse.x = e.clientX * PX_RATIO
        this.mouse.y = e.clientY * PX_RATIO
    }

    update(args) {
        this.pos.x += (this.mouse.x - this.pos.x) * this.friction
        this.pos.y += (this.mouse.y - this.pos.y) * this.friction
        let dist = Math.dist(this.pos, this.center)
        this.dist = dist / this.diagonal
        this.wght = Math.map(dist, 0, this.diagonal, 700, 100)
    }

    render() {
        this.update()
        this.context.fillStyle = "#000"
        this.context.fillRect(0, 0, this.canvas.width, this.canvas.height)
        this.context.fillStyle = this.fillStyle
        this.context.font = this.font
        this.width = this.context.measureText(this.text).width
        this.context.fillText(this.txt, (420 - this.width) / 2, this.lineHeight)
        this.canvas.style.fontVariationSettings = "'wght' " + this.wght.toFixed(2)
    }
}
class VariableText {
    constructor(_text) {
        this.camera = new THREE.PerspectiveCamera(45, this.rect.width / this.rect.height, 1, 1000)
        this.camera.position.z = 10
        this.scene = new THREE.Scene()
        this.text = new Text(_text)
        this.texture = new THREE.CanvasTexture(this.text.texture)
        this.texture.minFilter = THREE.NearestFilter

        this.geometry = new THREE.BoxGeometry(4, 1, 4, 8, 5, 8)

        this.mesh = new THREE.Mesh(this.geometry, this.material)
        this.scene.add(this.mesh)

        this.mouse = {
            x: 1,
            y: 1
        }

        this.setRenderer()
        this.addEvents()
        this.animate()
    }

    get rect() {
        return document.body.getBoundingClientRect()
    }

    onMouseMove(e) {
        if (e.touches) e = e.touches[0]
        this.mouse = {
            x: (e.clientX - this.center.x) * 0.0035,
            y: (e.clientY - this.center.y) / 50
        }
    }

    addEvents() {
        window.addEventListener('resize', () => this.onWindowResize(), false)
        document.addEventListener('mousemove', (e) => this.onMouseMove(e), false)
        document.addEventListener('touchmove', (e) => this.onMouseMove(e), false)
    }

    get material() {
        return new THREE.ShaderMaterial({
            vertexShader: document.getElementById('vertexShader').textContent,
            fragmentShader: document.getElementById('fragmentShader').textContent,
            transparent: true,
            uniforms: {
                uTime: {
                    value: 0
                },
                mouse: {
                    value: 1.0
                },
                uTexture: {
                    value: this.texture
                }
            },
        })
    }

    setRenderer() {
        this.renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true
        })
        this.renderer.setPixelRatio(window.devicePixelRatio)
        this.renderer.setSize(this.rect.width, this.rect.height)
        document.body.appendChild(this.renderer.domElement)
        this.onWindowResize()

    }

    onWindowResize() {
        this.center = {
            x: this.rect.width / 2,
            y: this.rect.height / 2
        }
        this.text.resize()
        this.camera.aspect = this.rect.width / this.rect.height
        this.camera.updateProjectionMatrix()
        this.renderer.setSize(this.rect.width, this.rect.height)
    }

    animate() {
        requestAnimationFrame(() => this.animate())
        this.text.render()
        this.render()
    }

    render(delta) {
        let time = new Date().getTime() * 0.001
        this.mesh.material.uniforms.mouse.value = this.text.dist
        this.mesh.material.uniforms.uTime.value = Math.sin(time)
        this.texture.needsUpdate = true

        this.camera.position.x += (this.mouse.x - this.camera.position.x) * .05;
        this.mesh.rotation.y += (-this.mouse.x - this.mesh.rotation.y) * .05;

        this.camera.lookAt(this.scene.position)
        this.renderer.render(this.scene, this.camera)
    }

}

window.onload = () => {
    const _text = new VariableText("Webstoryboy")
}