|
|
|
|
|
|
|
class Background {
|
|
constructor() {
|
|
|
|
this.scene = new THREE.Scene();
|
|
this.camera = new THREE.PerspectiveCamera(
|
|
75,
|
|
window.innerWidth / window.innerHeight,
|
|
0.1,
|
|
1000
|
|
);
|
|
|
|
|
|
this.renderer = new THREE.WebGLRenderer({
|
|
canvas: document.querySelector('#webgl-background'),
|
|
alpha: true,
|
|
antialias: true
|
|
});
|
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
|
this.renderer.setClearColor(0x0a0a0a, 1);
|
|
|
|
|
|
this.particles = [];
|
|
this.particleCount = 100;
|
|
this.particleGeometry = new THREE.BufferGeometry();
|
|
this.particleMaterial = new THREE.PointsMaterial({
|
|
size: 2,
|
|
color: 0xffffff,
|
|
transparent: true,
|
|
opacity: 0.5,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
|
|
|
|
this.camera.position.z = 100;
|
|
|
|
|
|
this.init();
|
|
|
|
|
|
this.bindEvents();
|
|
}
|
|
|
|
init() {
|
|
|
|
const positions = new Float32Array(this.particleCount * 3);
|
|
|
|
|
|
for (let i = 0; i < this.particleCount; i++) {
|
|
const i3 = i * 3;
|
|
positions[i3] = (Math.random() - 0.5) * window.innerWidth;
|
|
positions[i3 + 1] = (Math.random() - 0.5) * window.innerHeight;
|
|
positions[i3 + 2] = (Math.random() - 0.5) * 500;
|
|
|
|
|
|
this.particles.push({
|
|
velocity: (Math.random() - 0.5) * 0.2,
|
|
baseX: positions[i3],
|
|
baseY: positions[i3 + 1]
|
|
});
|
|
}
|
|
|
|
|
|
this.particleGeometry.setAttribute(
|
|
'position',
|
|
new THREE.BufferAttribute(positions, 3)
|
|
);
|
|
|
|
|
|
this.particleSystem = new THREE.Points(
|
|
this.particleGeometry,
|
|
this.particleMaterial
|
|
);
|
|
this.scene.add(this.particleSystem);
|
|
|
|
|
|
this.animate();
|
|
}
|
|
|
|
|
|
bindEvents() {
|
|
window.addEventListener('resize', () => {
|
|
|
|
this.camera.aspect = window.innerWidth / window.innerHeight;
|
|
this.camera.updateProjectionMatrix();
|
|
|
|
|
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
}
|
|
|
|
|
|
animate() {
|
|
requestAnimationFrame(() => this.animate());
|
|
|
|
const positions = this.particleGeometry.attributes.position.array;
|
|
const time = Date.now() * 0.0005;
|
|
|
|
|
|
for (let i = 0; i < this.particleCount; i++) {
|
|
const i3 = i * 3;
|
|
const particle = this.particles[i];
|
|
|
|
|
|
positions[i3] = particle.baseX + Math.sin(time + i) * 2;
|
|
positions[i3 + 1] = particle.baseY + Math.cos(time + i) * 2;
|
|
|
|
|
|
positions[i3 + 2] += particle.velocity;
|
|
|
|
|
|
if (Math.abs(positions[i3 + 2]) > 250) {
|
|
positions[i3 + 2] = -250;
|
|
}
|
|
}
|
|
|
|
|
|
this.particleGeometry.attributes.position.needsUpdate = true;
|
|
|
|
|
|
this.camera.position.x = Math.sin(time) * 10;
|
|
this.camera.position.y = Math.cos(time) * 10;
|
|
this.camera.lookAt(this.scene.position);
|
|
|
|
|
|
this.renderer.render(this.scene, this.camera);
|
|
}
|
|
|
|
|
|
addDramaticEffect(type) {
|
|
switch(type) {
|
|
case 'impostor_reveal':
|
|
|
|
this.particleMaterial.color.setHex(0xff0000);
|
|
setTimeout(() => {
|
|
this.particleMaterial.color.setHex(0xffffff);
|
|
}, 1000);
|
|
break;
|
|
|
|
case 'round_start':
|
|
|
|
const originalVelocities = this.particles.map(p => p.velocity);
|
|
this.particles.forEach(p => p.velocity *= 2);
|
|
setTimeout(() => {
|
|
this.particles.forEach((p, i) => p.velocity = originalVelocities[i]);
|
|
}, 2000);
|
|
break;
|
|
|
|
case 'voting':
|
|
|
|
const pulseAnimation = () => {
|
|
this.particleMaterial.size = 2 + Math.sin(Date.now() * 0.005) * 1;
|
|
};
|
|
const pulseInterval = setInterval(pulseAnimation, 16);
|
|
setTimeout(() => {
|
|
clearInterval(pulseInterval);
|
|
this.particleMaterial.size = 2;
|
|
}, 3000);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const background = new Background();
|
|
|
|
|
|
window.gameBackground = background;
|
|
});
|
|
|
|
|
|
export default Background; |