fantus/components/background.tsx

161 lines
4.9 KiB
TypeScript

import React, { useEffect } from 'react'
import { PerspectiveCamera, Scene, BufferGeometry, BufferAttribute, ShaderMaterial, Color, Points, WebGLRenderer } from 'three'
import '../styles/backgorund.styl'
const SEPARATION = 150, AMOUNTX = 50, AMOUNTY = 50, HEIGHT = 75
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() {
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++
}
}
const geometry = new BufferGeometry()
geometry.setAttribute('position', new BufferAttribute(positions, 3))
geometry.setAttribute('scale', new BufferAttribute(scales, 1))
const material = new ShaderMaterial({
uniforms: {
color: { value: new Color(0xffffff) },
},
vertexShader: document.getElementById('vertexshader')?.textContent || '',
fragmentShader: document.getElementById('fragmentshader')?.textContent || ''
})
particles = new Points(geometry, material)
scene.add(particles)
renderer = new WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
container.appendChild(renderer.domElement)
document.addEventListener('mousemove', onDocumentMouseMove, false)
document.addEventListener('touchstart', onDocumentTouchStart, false)
document.addEventListener('touchmove', onDocumentTouchMove, false)
window.addEventListener('resize', onWindowResize, false)
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2
windowHalfY = window.innerHeight / 2
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
function setMouse(x: number, y: number) {
mouseX = (x - windowHalfX) / windowHalfX
mouseY = -(y - windowHalfY) / windowHalfY
}
function onDocumentMouseMove(event: MouseEvent) {
setMouse(event.clientX, event.clientY)
}
function onDocumentTouchStart(event: TouchEvent) {
if (event.touches.length === 1) {
event.preventDefault()
setMouse(event.touches[0].pageX, event.touches[0].pageY)
}
}
function onDocumentTouchMove(event: TouchEvent) {
if (event.touches.length === 1) {
event.preventDefault()
setMouse(event.touches[0].pageX, event.touches[0].pageY)
}
}
function animate() {
requestAnimationFrame(animate)
render()
}
function render() {
// 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)
// @ts-ignore
positions[i + 1] = (dx * HEIGHT) + (dy * HEIGHT)
// @ts-ignore
scales[j] = (dx + 1) * 8 + (dy + 1) * 8
i += 3
j++
}
}
// @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)
}
const Background: React.FC = ({ children }) => {
useEffect(() => {
init()
animate()
}, [])
return <div id='bg'>
{children}
</div>
}
export default Background