It’s time to make your website come alive! In this milestone, you’ll animate your 3D dog with movement and gestures based on scroll position — and let users interact with it through fun buttons.
The model’s position and rotation will change depending on the section the user scrolls to. First, add this logic to your app.js
:
// Responsive position arrays
const getPositionArray = () => {
const isMobile = window.innerWidth <= 767;
const isTablet = window.innerWidth <= 1023 && window.innerWidth > 767;
if (isMobile) {
return [
{
id: "banner",
position: { x: 0, y: -20, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
animation: ["clip3"],
},
{
id: "intro",
position: { x: -10, y: -1, z: -3 },
rotation: { x: 0.3, y: -0.3, z: 0 },
animation: ["clip3"],
},
{
id: "description",
position: { x: 0, y: -1, z: -3 },
rotation: { x: 0, y: 0.3, z: 0 },
animation: ["clip3"],
},
{
id: "playground",
position: { x: 0, y: -15, z: -3 },
rotation: { x: 0, y: 0, z: 0 },
animation: ["clip4"],
},
];
} else if (isTablet) {
return [
{
id: "banner",
position: { x: 15, y: -20, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
animation: ["clip3"],
},
{
id: "intro",
position: { x: -15, y: -1, z: -4 },
rotation: { x: 0.4, y: -0.4, z: 0 },
animation: ["clip3"],
},
{
id: "description",
position: { x: -1, y: -1, z: -4 },
rotation: { x: 0, y: 0.4, z: 0 },
animation: ["clip3"],
},
{
id: "playground",
position: { x: 0, y: -18, z: -4 },
rotation: { x: 0, y: 0, z: 0 },
animation: ["clip4"],
},
];
} else {
return [
{
id: "banner",
position: { x: 20, y: -20, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
animation: ["clip3"],
},
{
id: "intro",
position: { x: -20, y: -1, z: -5 },
rotation: { x: 0.5, y: -0.5, z: 0 },
animation: ["clip3"],
},
{
id: "description",
position: { x: -1, y: -1, z: -5 },
rotation: { x: 0, y: 0.5, z: 0 },
animation: ["clip3"],
},
{
id: "playground",
position: { x: 0, y: 20, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
animation: ["clip4"],
},
];
}
};
// Model movement function
const modelMove = () => {
const sections = document.querySelectorAll(".section");
let currentSection;
sections.forEach((section) => {
const rect = section.getBoundingClientRect();
if (rect.top <= window.innerHeight / 3) {
currentSection = section.id;
}
});
const arrPositionModel = getPositionArray();
let position_active = arrPositionModel.findIndex(
(val) => val.id === currentSection
);
if (position_active >= 0) {
let target = arrPositionModel[position_active];
// Move model with GSAP
gsap.to(dog.position, {
x: target.position.x,
y: target.position.y,
z: target.position.z,
duration: 3,
ease: "power1.out",
});
gsap.to(dog.rotation, {
x: target.rotation.x,
y: target.rotation.y,
z: target.rotation.z,
duration: 3,
ease: "power1.out",
});
// Switch animation
if (
mixer &&
actions[target.animation] &&
currentAction !== actions[target.animation]
) {
if (currentAction) {
currentAction.fadeOut(0.5);
}
currentAction = actions[target.animation];
currentAction.reset().fadeIn(0.5).play();
}
}
};
window.addEventListener("scroll", () => {
if (dog) modelMove();
});
Let users click buttons to trigger custom animations. This is how you hook up the dog’s tricks:
// Button interactions
const changeAnimation = (animationName) => {
if (
mixer &&
actions[animationName] &&
currentAction !== actions[animationName]
) {
if (currentAction) {
currentAction.fadeOut(0.5);
}
currentAction = actions[animationName];
currentAction.reset().fadeIn(0.5).play();
// Auto-revert after 5 seconds
setTimeout(() => {
currentAction.fadeOut(0.5);
currentAction = actions["clip4"];
currentAction.reset().fadeIn(0.5).play();
}, 5000);
}
};
// Event listeners
document
.getElementById("bang")
.addEventListener("click", () => changeAnimation("clip0"));
document
.getElementById("paw")
.addEventListener("click", () => changeAnimation("clip2"));
document
.getElementById("roll")
.addEventListener("click", () => changeAnimation("clip1"));
document
.getElementById("sit")
.addEventListener("click", () => changeAnimation("clip3"));
Make your model responsive and interactive:
window.addEventListener("resize", () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
if (dog) modelMove();
});