|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<title>3D Card Gallery</title> |
|
|
<style> |
|
|
body, html { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
overflow: hidden; |
|
|
background: #000; |
|
|
} |
|
|
canvas { |
|
|
display: block; |
|
|
} |
|
|
.loading { |
|
|
position: fixed; |
|
|
top: 50%; |
|
|
left: 50%; |
|
|
transform: translate(-50%, -50%); |
|
|
color: white; |
|
|
font-family: Arial, sans-serif; |
|
|
font-size: 24px; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="loading">Loading...</div> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js"></script> |
|
|
<script> |
|
|
let scene, camera, renderer, cards = []; |
|
|
const images = [ |
|
|
'https://picsum.photos/512/512?random=1', |
|
|
'https://picsum.photos/512/512?random=2', |
|
|
'https://picsum.photos/512/512?random=3', |
|
|
'https://picsum.photos/512/512?random=4', |
|
|
'https://picsum.photos/512/512?random=5', |
|
|
'https://picsum.photos/512/512?random=6' |
|
|
]; |
|
|
|
|
|
const textureLoader = new THREE.TextureLoader(); |
|
|
let loadedTextures = 0; |
|
|
|
|
|
function init() { |
|
|
scene = new THREE.Scene(); |
|
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
|
|
renderer = new THREE.WebGLRenderer({ antialias: true }); |
|
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
|
document.body.appendChild(renderer.domElement); |
|
|
|
|
|
camera.position.z = 10; |
|
|
|
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); |
|
|
scene.add(ambientLight); |
|
|
|
|
|
const pointLight = new THREE.PointLight(0xffffff, 1); |
|
|
pointLight.position.set(10, 10, 10); |
|
|
scene.add(pointLight); |
|
|
|
|
|
images.forEach((url, index) => { |
|
|
textureLoader.load(url, (texture) => { |
|
|
createCard(texture, index); |
|
|
loadedTextures++; |
|
|
if (loadedTextures === images.length) { |
|
|
document.querySelector('.loading').style.display = 'none'; |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.addEventListener('mousemove', onMouseMove); |
|
|
animate(); |
|
|
} |
|
|
|
|
|
function createCard(texture, index) { |
|
|
const geometry = new THREE.PlaneGeometry(3, 4); |
|
|
const material = new THREE.MeshPhongMaterial({ |
|
|
map: texture, |
|
|
side: THREE.DoubleSide |
|
|
}); |
|
|
const card = new THREE.Mesh(geometry, material); |
|
|
|
|
|
const angle = (index / images.length) * Math.PI * 2; |
|
|
const radius = 6; |
|
|
card.position.x = Math.cos(angle) * radius; |
|
|
card.position.z = Math.sin(angle) * radius; |
|
|
card.rotation.y = -angle; |
|
|
|
|
|
cards.push(card); |
|
|
scene.add(card); |
|
|
} |
|
|
|
|
|
function onMouseMove(event) { |
|
|
const mouseX = (event.clientX / window.innerWidth) * 2 - 1; |
|
|
const mouseY = (event.clientY / window.innerHeight) * 2 - 1; |
|
|
|
|
|
cards.forEach(card => { |
|
|
card.rotation.x = mouseY * 0.2; |
|
|
card.rotation.z = mouseX * 0.2; |
|
|
}); |
|
|
} |
|
|
|
|
|
function animate() { |
|
|
requestAnimationFrame(animate); |
|
|
|
|
|
cards.forEach((card, index) => { |
|
|
const time = Date.now() * 0.001; |
|
|
const angle = (index / images.length) * Math.PI * 2 + time * 0.5; |
|
|
const radius = 6; |
|
|
|
|
|
card.position.x = Math.cos(angle) * radius; |
|
|
card.position.z = Math.sin(angle) * radius; |
|
|
card.rotation.y = -angle; |
|
|
}); |
|
|
|
|
|
TWEEN.update(); |
|
|
renderer.render(scene, camera); |
|
|
} |
|
|
|
|
|
window.addEventListener('resize', () => { |
|
|
camera.aspect = window.innerWidth / window.innerHeight; |
|
|
camera.updateProjectionMatrix(); |
|
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
|
}); |
|
|
|
|
|
init(); |
|
|
</script> |
|
|
</body> |
|
|
</html> |