So far, we've explored creating static 3D scenes. But the real magic of 3D on the web often comes alive when things move in response to the user. In this section, we'll delve into how to trigger animations based on user interactions, such as mouse clicks, hovers, or even keyboard presses. This transforms passive viewers into active participants.
The core idea is to link a user event to a change in an animation property. In Three.js, animations are often controlled by properties like rotation, position, scale, or even material properties like color. We'll use JavaScript event listeners to detect user actions and then update these properties over time.
Let's start with a simple example: making an object rotate when the user clicks on it. We'll need to set up an event listener on the renderer's DOM element.
const canvas = renderer.domElement;
canvas.addEventListener('click', onClick);
function onClick(event) {
// Determine if the click intersected with our object
// If it did, start an animation or toggle a state
}To determine if the click actually hit our 3D object, we can use a Raycaster. A Raycaster shoots a ray from a point in the scene (in this case, the mouse click's position) and checks for intersections with objects. If an intersection is found, we know the user clicked on that specific object.
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onClick(event) {
// Calculate mouse position in normalized device coordinates (-1 to +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
// Update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// Calculate objects intersecting the picking ray
const intersects = raycaster.intersectObjects(scene.children);
for (const intersect of intersects) {
if (intersect.object === myMesh) { // myMesh is the object we want to animate
// Trigger animation here!
isAnimating = !isAnimating; // For example, toggle an animation
break;
}
}
}Once we've detected a click on our target object, we can then trigger an animation. A common way to manage animations is to use a boolean flag that controls whether the animation logic in our requestAnimationFrame loop should run.
let isRotating = false;
function animate() {
requestAnimationFrame(animate);
if (isRotating) {
myMesh.rotation.y += 0.01;
}
renderer.render(scene, camera);
}Combining these pieces, our onClick function would now set isRotating to true or false based on the click, and our animate loop would handle the actual rotation when isRotating is true.
graph TD
A[User Clicks Canvas] --> B{Is Click on Target Object?}
B -- Yes --> C{Toggle Animation State}
C --> D[Animation Flag Set]
B -- No --> E[Do Nothing]
D --> F{In Animation Loop}
F -- Animation State True --> G[Rotate Object]
G --> H[Render Frame]
F -- Animation State False --> H
H --> I[Next Frame]
Beyond simple toggles, you can also create more nuanced animations. For instance, you could have an object grow in size on hover, or change color when a specific button is pressed. This involves more sophisticated control over the animation properties, potentially using animation libraries or custom easing functions to create smoother and more dynamic movements.
Remember to handle different interaction types. For hover effects, you'd use 'mousemove' or 'mouseover'/'mouseout' events. For keyboard input, you'd use 'keydown' and 'keyup' events. The fundamental principle remains the same: capture the event, determine the intent, and update your 3D scene accordingly.