fantus/components/background.tsx

168 lines
4.6 KiB
TypeScript
Raw Permalink Normal View History

2020-01-12 18:34:14 +01:00
import React, { useEffect } from 'react'
2021-02-25 11:52:09 +01:00
import {
PerspectiveCamera,
Scene,
BufferGeometry,
BufferAttribute,
ShaderMaterial,
Color,
Points,
WebGLRenderer,
} from 'three'
const SEPARATION = 150,
AMOUNTX = 50,
AMOUNTY = 50,
HEIGHT = 75
2020-01-12 18:34:14 +01:00
let container: HTMLElement | null
let camera: PerspectiveCamera
let scene: Scene
let renderer: WebGLRenderer
let count = 1
let particles: Points
let mouseX = 0
let mouseY = 0
let windowHalfX = window.innerWidth / 2
let windowHalfY = window.innerHeight / 2
function init() {
2021-02-25 11:52:09 +01:00
container = window.document.getElementById('bg') || document.createElement('div')
document.body.appendChild(container)
camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000000)
scene = new Scene()
const numParticles = AMOUNTX * AMOUNTY
const positions = new Float32Array(numParticles * 3)
const scales = new Float32Array(numParticles)
let i = 0,
j = 0
for (let ix = 0; ix < AMOUNTX; ix++) {
for (let iy = 0; iy < AMOUNTY; iy++) {
positions[i] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2 // x
positions[i + 1] = 0 // y
positions[i + 2] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2 // z
scales[j] = 1
i += 3
j++
2020-01-12 18:34:14 +01:00
}
2021-02-25 11:52:09 +01:00
}
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
const geometry = new BufferGeometry()
geometry.setAttribute('position', new BufferAttribute(positions, 3))
geometry.setAttribute('scale', new BufferAttribute(scales, 1))
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
const material = new ShaderMaterial({
uniforms: {
color: { value: new Color(0xffffff) },
},
vertexShader: document.getElementById('vertexshader')?.textContent || '',
fragmentShader: document.getElementById('fragmentshader')?.textContent || '',
})
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
particles = new Points(geometry, material)
scene.add(particles)
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
renderer = new WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
container.appendChild(renderer.domElement)
document.addEventListener('mousemove', onDocumentMouseMove, false)
document.addEventListener('touchstart', onDocumentTouchStart, false)
document.addEventListener('touchmove', onDocumentTouchMove, false)
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
window.addEventListener('resize', onWindowResize, false)
2020-01-12 18:34:14 +01:00
}
function onWindowResize() {
2021-02-25 11:52:09 +01:00
windowHalfX = window.innerWidth / 2
windowHalfY = window.innerHeight / 2
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
2020-01-12 18:34:14 +01:00
}
function setMouse(x: number, y: number) {
2021-02-25 11:52:09 +01:00
mouseX = (x - windowHalfX) / windowHalfX
mouseY = -(y - windowHalfY) / windowHalfY
2020-01-12 18:34:14 +01:00
}
function onDocumentMouseMove(event: MouseEvent) {
2021-02-25 11:52:09 +01:00
setMouse(event.clientX, event.clientY)
2020-01-12 18:34:14 +01:00
}
function onDocumentTouchStart(event: TouchEvent) {
2021-02-25 11:52:09 +01:00
if (event.touches.length === 1) {
event.preventDefault()
setMouse(event.touches[0].pageX, event.touches[0].pageY)
}
2020-01-12 18:34:14 +01:00
}
function onDocumentTouchMove(event: TouchEvent) {
2021-02-25 11:52:09 +01:00
if (event.touches.length === 1) {
event.preventDefault()
setMouse(event.touches[0].pageX, event.touches[0].pageY)
}
2020-01-12 18:34:14 +01:00
}
function animate() {
2021-02-25 11:52:09 +01:00
requestAnimationFrame(animate)
render()
2020-01-12 18:34:14 +01:00
}
function render() {
2021-02-25 11:52:09 +01:00
// Noence from 0.0 to 1.0
const noence = (Math.sin(count / 4) + 1) / 2
const noence_inv = 1 - noence
camera.position.x = -mouseX * 500
camera.position.y = 1000 - mouseY * 500
camera.position.z = 1000 - mouseY * 250
camera.lookAt(scene.position)
if (particles.geometry instanceof BufferGeometry) {
const positions = particles.geometry.attributes.position.array
const scales = particles.geometry.attributes.scale.array
let i = 0,
j = 0
for (let ix = 0; ix < AMOUNTX; ix++) {
for (let iy = 0; iy < AMOUNTY; iy++) {
const dx = Math.sin((ix + count) * 0.3)
const dy = Math.sin((iy + count) * 0.5)
2020-01-12 18:34:14 +01:00
// @ts-ignore
2021-02-25 11:52:09 +01:00
positions[i + 1] = dx * HEIGHT + dy * HEIGHT
// @ts-ignore
scales[j] = (dx + 1) * 8 + (dy + 1) * 8
i += 3
j++
}
2020-01-12 18:34:14 +01:00
}
2021-02-25 11:52:09 +01:00
// @ts-ignore
particles.material.uniforms.color.value.setRGB(1 - 0.5 * noence_inv, 0.2 + 0.8 * noence, 1)
if (particles.geometry.attributes.position instanceof BufferAttribute)
particles.geometry.attributes.position.needsUpdate = true
if (particles.geometry.attributes.scale instanceof BufferAttribute)
particles.geometry.attributes.scale.needsUpdate = true
}
renderer.render(scene, camera)
count += 0.05 + 0.01 * noence
2020-01-12 18:34:14 +01:00
}
const Background: React.FC = ({ children }) => {
2021-02-25 11:52:09 +01:00
useEffect(() => {
init()
animate()
}, [])
2020-01-12 18:34:14 +01:00
2021-02-25 11:52:09 +01:00
return <div id="bg">{children}</div>
2020-01-12 18:34:14 +01:00
}
2021-02-25 11:52:09 +01:00
export default Background