ezsepa-website/.history/hero-scene_20251102145927.js
2025-11-02 20:30:44 +01:00

303 lines
9.1 KiB
JavaScript

import * as THREE from 'three';
/**
* Create a 3D scene inspired by payment QR code illustration
* Features: Large phone/tablet with QR code, person figure, floating coins, and security shield
*/
export function initHeroScene() {
const container = document.getElementById('hero-3d-container');
if (!container) return;
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
50,
container.clientWidth / container.clientHeight,
0.1,
1000
);
camera.position.set(0, 0, 10);
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0xFF7A59, 1, 100);
pointLight.position.set(-5, 5, 5);
scene.add(pointLight);
// Create QR Code Cube (centerpiece)
const qrCodeTexture = createQRCodeTexture();
const qrCubeGeometry = new THREE.BoxGeometry(2, 2, 2);
const qrCubeMaterial = new THREE.MeshStandardMaterial({
map: qrCodeTexture,
metalness: 0.3,
roughness: 0.4,
});
const qrCube = new THREE.Mesh(qrCubeGeometry, qrCubeMaterial);
qrCube.position.set(0, 0, 0);
scene.add(qrCube);
// Create Euro coins
const coins = [];
const coinGeometry = new THREE.CylinderGeometry(0.3, 0.3, 0.1, 32);
const coinMaterial = new THREE.MeshStandardMaterial({
color: 0xFFD700,
metalness: 0.8,
roughness: 0.2,
});
for (let i = 0; i < 6; i++) {
const coin = new THREE.Mesh(coinGeometry, coinMaterial);
const angle = (i / 6) * Math.PI * 2;
const radius = 3;
coin.position.set(
Math.cos(angle) * radius,
Math.sin(angle * 2) * 1.5,
Math.sin(angle) * radius
);
coin.rotation.x = Math.PI / 2;
coins.push({ mesh: coin, angle, radius, offset: i });
scene.add(coin);
}
// Create shopping cart icon (simplified)
const cartGroup = new THREE.Group();
// Cart body
const cartBodyGeometry = new THREE.BoxGeometry(0.8, 0.6, 0.6);
const cartMaterial = new THREE.MeshStandardMaterial({
color: 0x2563EB,
metalness: 0.5,
roughness: 0.3,
});
const cartBody = new THREE.Mesh(cartBodyGeometry, cartMaterial);
cartGroup.add(cartBody);
// Cart wheels
const wheelGeometry = new THREE.CylinderGeometry(0.1, 0.1, 0.05, 16);
const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x1E293B });
const wheel1 = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheel1.position.set(-0.2, -0.4, 0.3);
wheel1.rotation.z = Math.PI / 2;
cartGroup.add(wheel1);
const wheel2 = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheel2.position.set(0.2, -0.4, 0.3);
wheel2.rotation.z = Math.PI / 2;
cartGroup.add(wheel2);
cartGroup.position.set(-3, -2, 2);
cartGroup.scale.set(1.5, 1.5, 1.5);
scene.add(cartGroup);
// Create smartphone (payment device)
const phoneGroup = new THREE.Group();
const phoneBodyGeometry = new THREE.BoxGeometry(0.6, 1, 0.1);
const phoneMaterial = new THREE.MeshStandardMaterial({
color: 0x1E293B,
metalness: 0.6,
roughness: 0.2,
});
const phoneBody = new THREE.Mesh(phoneBodyGeometry, phoneMaterial);
phoneGroup.add(phoneBody);
// Phone screen
const screenGeometry = new THREE.BoxGeometry(0.54, 0.9, 0.05);
const screenMaterial = new THREE.MeshStandardMaterial({
color: 0x4A90E2,
emissive: 0x2563EB,
emissiveIntensity: 0.3,
});
const screen = new THREE.Mesh(screenGeometry, screenMaterial);
screen.position.z = 0.06;
phoneGroup.add(screen);
phoneGroup.position.set(3, -1.5, 1);
phoneGroup.rotation.y = -0.3;
phoneGroup.scale.set(1.5, 1.5, 1.5);
scene.add(phoneGroup);
// Create bank icon
const bankGroup = new THREE.Group();
const bankBodyGeometry = new THREE.BoxGeometry(1.2, 0.8, 0.6);
const bankMaterial = new THREE.MeshStandardMaterial({
color: 0xFF7A59,
metalness: 0.3,
roughness: 0.5,
});
const bankBody = new THREE.Mesh(bankBodyGeometry, bankMaterial);
bankGroup.add(bankBody);
// Bank roof
const roofGeometry = new THREE.ConeGeometry(0.9, 0.4, 4);
const roofMaterial = new THREE.MeshStandardMaterial({
color: 0xE85A3D,
});
const roof = new THREE.Mesh(roofGeometry, roofMaterial);
roof.position.y = 0.6;
roof.rotation.y = Math.PI / 4;
bankGroup.add(roof);
bankGroup.position.set(2.5, 2, -1);
bankGroup.scale.set(0.8, 0.8, 0.8);
scene.add(bankGroup);
// Particle system for data flow
const particlesGeometry = new THREE.BufferGeometry();
const particlesCount = 50;
const positions = new Float32Array(particlesCount * 3);
for (let i = 0; i < particlesCount * 3; i++) {
positions[i] = (Math.random() - 0.5) * 10;
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const particlesMaterial = new THREE.PointsMaterial({
color: 0xFF7A59,
size: 0.05,
transparent: true,
opacity: 0.6,
});
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particles);
// Animation
let time = 0;
function animate() {
requestAnimationFrame(animate);
time += 0.01;
// Rotate QR cube
qrCube.rotation.y += 0.005;
qrCube.rotation.x = Math.sin(time) * 0.1;
qrCube.position.y = Math.sin(time * 1.5) * 0.2;
// Animate coins in orbit
coins.forEach((coinData, index) => {
const { mesh, angle, radius, offset } = coinData;
const newAngle = angle + time * 0.5;
mesh.position.x = Math.cos(newAngle) * radius;
mesh.position.z = Math.sin(newAngle) * radius;
mesh.position.y = Math.sin(time * 2 + offset) * 1.5;
mesh.rotation.y += 0.02;
});
// Animate cart (bobbing)
cartGroup.position.y = -2 + Math.sin(time * 2) * 0.1;
cartGroup.rotation.y = Math.sin(time * 0.5) * 0.1;
// Animate phone
phoneGroup.position.y = -1.5 + Math.sin(time * 1.5 + 1) * 0.15;
phoneGroup.rotation.z = Math.sin(time * 0.8) * 0.05;
// Animate bank
bankGroup.position.y = 2 + Math.sin(time * 1.2 + 2) * 0.1;
// Animate particles
particles.rotation.y += 0.001;
const positions = particles.geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
positions[i + 1] += Math.sin(time + i) * 0.002;
}
particles.geometry.attributes.position.needsUpdate = true;
renderer.render(scene, camera);
}
animate();
// Handle resize
function onWindowResize() {
if (!container) return;
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}
window.addEventListener('resize', onWindowResize);
// Cleanup function
return () => {
window.removeEventListener('resize', onWindowResize);
renderer.dispose();
container.removeChild(renderer.domElement);
};
}
/**
* Create a procedural QR code texture
*/
function createQRCodeTexture() {
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext('2d');
// White background
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, 512, 512);
// Draw QR code pattern
ctx.fillStyle = '#000000';
const moduleSize = 32;
const modules = 16;
// QR code pattern (simplified)
const pattern = [
[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1],
];
const offset = (512 - (pattern.length * moduleSize)) / 2;
pattern.forEach((row, i) => {
row.forEach((cell, j) => {
if (cell === 1) {
ctx.fillRect(
offset + j * moduleSize,
offset + i * moduleSize,
moduleSize,
moduleSize
);
}
});
});
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
}