542 lines
18 KiB
JavaScript
542 lines
18 KiB
JavaScript
|
console.clear();
|
||
|
let scene, camera, renderer, controls;
|
||
|
let canvas = document.querySelector("canvas");
|
||
|
let width = window.innerWidth;
|
||
|
let height = window.innerHeight;
|
||
|
let fov = 75;
|
||
|
let aspectRatio = width / height;
|
||
|
let near = 0.001;
|
||
|
let far = 100;
|
||
|
let mainGroup = new THREE.Group();
|
||
|
let colors = {
|
||
|
blue: 0x71b6f7,
|
||
|
brown: 0x744436,
|
||
|
brown2: 0xC88247,
|
||
|
red: 0xfd4d50,
|
||
|
green: 0xa4d740,
|
||
|
green2: 0x66b888,
|
||
|
green3: 0x2C9D3E,
|
||
|
house: 0xfce3ad,
|
||
|
purple: 0x6e5370,
|
||
|
gold: 0xFFF09C,
|
||
|
grey: 0xB7B398,
|
||
|
greyBrown: 0xB7B398 };
|
||
|
|
||
|
|
||
|
const setup = () => {
|
||
|
// scene
|
||
|
scene = new THREE.Scene();
|
||
|
scene.fog = new THREE.FogExp2(0x9ac2fe, 0.14);
|
||
|
// camera
|
||
|
camera = new THREE.PerspectiveCamera(fov, aspectRatio, near, far);
|
||
|
scene.add(camera);
|
||
|
// renderer
|
||
|
renderer = new THREE.WebGLRenderer({
|
||
|
antialias: true,
|
||
|
canvas: canvas });
|
||
|
|
||
|
renderer.setSize(width, height);
|
||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||
|
renderer.setClearColor(0x9ac2fe);
|
||
|
renderer.shadowMap.enabled = true;
|
||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||
|
renderer.shadowMapSoft = true;
|
||
|
// controls
|
||
|
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||
|
controls.autoRotate = true;
|
||
|
controls.autoRotateSpeed = 1.6;
|
||
|
camera.position.set(3.56, 2, 3.4);
|
||
|
// lights
|
||
|
let light = new THREE.HemisphereLight(0xffffff, 0x5c9cfe, 1.1);
|
||
|
scene.add(light);
|
||
|
//
|
||
|
let spot = new THREE.SpotLight(0xF6FAFD, 0.06);
|
||
|
spot.castShadow = true;
|
||
|
spot.shadow.camera.left = -1;
|
||
|
spot.shadow.camera.right = -1;
|
||
|
spot.shadow.camera.top = -1;
|
||
|
spot.shadow.camera.bottom = -1;
|
||
|
spot.shadow.camera.near = 1;
|
||
|
spot.shadow.camera.far = 100;
|
||
|
spot.shadow.mapSize.width = 2048;
|
||
|
spot.shadow.mapSize.height = 2048;
|
||
|
spot.shadow.camera.fov = 100;
|
||
|
spot.position.set(-3, 3, 0);
|
||
|
camera.add(spot);
|
||
|
// group for all other groups
|
||
|
mainGroup = new THREE.Group();
|
||
|
scene.add(mainGroup);
|
||
|
camera.lookAt(0, 0, 0);
|
||
|
};
|
||
|
|
||
|
// objects
|
||
|
let islandGroup = new THREE.Group();
|
||
|
const createIsland = () => {
|
||
|
// earth
|
||
|
let coneGeo = new THREE.ConeBufferGeometry(2, 3, 8);
|
||
|
let mat = new THREE.MeshLambertMaterial({
|
||
|
color: colors.brown });
|
||
|
|
||
|
let cone = new THREE.Mesh(coneGeo, mat);
|
||
|
cone.rotation.x = THREE.Math.degToRad(180);
|
||
|
cone.position.set(0, -1.75, 0);
|
||
|
islandGroup.add(cone);
|
||
|
// grass
|
||
|
let boxGeo = new THREE.BoxBufferGeometry();
|
||
|
let mat2 = new THREE.MeshLambertMaterial({
|
||
|
color: colors.green });
|
||
|
|
||
|
let grass = new THREE.Mesh(boxGeo, mat2);
|
||
|
grass.scale.set(4, 0.5, 4);
|
||
|
grass.receiveShadow = true;
|
||
|
islandGroup.add(grass);
|
||
|
// water
|
||
|
let mat3 = new THREE.MeshLambertMaterial({
|
||
|
color: colors.blue });
|
||
|
|
||
|
let water = new THREE.Mesh(boxGeo, mat3);
|
||
|
water.receiveShadow = true;
|
||
|
islandGroup.add(water);
|
||
|
water.scale.set(0.5, 0.5, 4);
|
||
|
water.position.set(0.75, 0.005, 0.005);
|
||
|
mainGroup.add(islandGroup);
|
||
|
};
|
||
|
|
||
|
// waterfall particles
|
||
|
let dropCount = 100;
|
||
|
let drops = [];
|
||
|
// water details
|
||
|
let detailCount = 24;
|
||
|
let dets = [];
|
||
|
let detailCount2 = 8;
|
||
|
let dets2 = [];
|
||
|
|
||
|
// creating particles function
|
||
|
let particleGeo, particleMat, particle;
|
||
|
const createParticles = (color, particleAmount, particleArray, scaleX, scaleY, scaleZ, posX, posX2, posY, posY2, posZ, posZ2, opacity, rotX, rotY, rotZ) => {
|
||
|
particleGeo = new THREE.BoxBufferGeometry();
|
||
|
particleMat = new THREE.MeshLambertMaterial({
|
||
|
color: color,
|
||
|
transparent: true });
|
||
|
|
||
|
for (let i = 0; i < particleAmount; i++) {
|
||
|
particle = new THREE.Mesh(particleGeo, particleMat);
|
||
|
islandGroup.add(particle);
|
||
|
particleArray.push(particle);
|
||
|
particle.scale.set(scaleX, scaleY, scaleZ);
|
||
|
particle.position.set(THREE.Math.randFloat(posX, posX2), THREE.Math.randFloat(posY, posY2), THREE.Math.randFloat(posZ, posZ2));
|
||
|
particle.material.opacity = opacity;
|
||
|
particle.rotation.set(rotX, THREE.Math.degToRad(rotY), rotZ);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// function to create various box like shapes of the house and the mailbox
|
||
|
const createBoxShape = (x, y, z, xPos, yPos, zPos, color, rShadow, cShadow, group) => {
|
||
|
let geo = new THREE.BoxBufferGeometry(x, y, z);
|
||
|
let mat = new THREE.MeshLambertMaterial({ color: color });
|
||
|
let mesh = new THREE.Mesh(geo, mat);
|
||
|
mesh.position.set(xPos, yPos, zPos);
|
||
|
mesh.receiveShadow = rShadow;
|
||
|
mesh.castShadow = cShadow;
|
||
|
group.add(mesh);
|
||
|
};
|
||
|
|
||
|
let treeGroup = new THREE.Group();
|
||
|
const createTree = (trunkX, trunkY, trunkZ, leavesX, leavesY, leavesZ) => {
|
||
|
// trunk
|
||
|
let geo = new THREE.CylinderBufferGeometry(0.1, 0.1, 1, 10);
|
||
|
let mat = new THREE.MeshLambertMaterial({
|
||
|
color: colors.brown });
|
||
|
|
||
|
let trunk = new THREE.Mesh(geo, mat);
|
||
|
treeGroup.add(trunk);
|
||
|
trunk.position.set(trunkX, trunkY, trunkZ);
|
||
|
trunk.castShadow = true;
|
||
|
trunk.receiveShadow = true;
|
||
|
// leaves
|
||
|
let geo2 = new THREE.SphereBufferGeometry(0.25, 12, 12);
|
||
|
let mat2 = new THREE.MeshLambertMaterial({
|
||
|
color: colors.green2 });
|
||
|
|
||
|
let treeLeaves = new THREE.Mesh(geo2, mat2);
|
||
|
treeLeaves.position.set(leavesX, leavesY, leavesZ);
|
||
|
treeLeaves.castShadow = true;
|
||
|
treeLeaves.receiveShadow = true;
|
||
|
treeGroup.add(treeLeaves);
|
||
|
mainGroup.add(treeGroup);
|
||
|
};
|
||
|
|
||
|
const createBush = (x, y, z) => {
|
||
|
let geo = new THREE.SphereBufferGeometry(0.15, 8, 8);
|
||
|
let mat = new THREE.MeshLambertMaterial({ color: colors.green3 });
|
||
|
let bush = new THREE.Mesh(geo, mat);
|
||
|
bush.position.set(x, y, z);
|
||
|
bush.receiveShadow = true;
|
||
|
treeGroup.add(bush);
|
||
|
};
|
||
|
|
||
|
// house
|
||
|
let houseGroup = new THREE.Group();
|
||
|
|
||
|
// roof window function
|
||
|
const createRoofWindow = (x, y, z, color, radB, radT, height, segments) => {
|
||
|
let geo = new THREE.CylinderBufferGeometry(radB, radT, height, segments);
|
||
|
let mat = new THREE.MeshLambertMaterial({ color: color });
|
||
|
let window = new THREE.Mesh(geo, mat);
|
||
|
window.rotation.z = THREE.Math.degToRad(90);
|
||
|
window.position.set(x, y, z);
|
||
|
houseGroup.add(window);
|
||
|
};
|
||
|
|
||
|
const createHouse = () => {
|
||
|
let boxGeo = new THREE.BoxBufferGeometry();
|
||
|
// house
|
||
|
let houseMat = new THREE.MeshLambertMaterial({ color: colors.house });
|
||
|
let house = new THREE.Mesh(boxGeo, houseMat);
|
||
|
house.position.set(-1, 0.75, 1);
|
||
|
house.scale.set(1.2, 1, 1.5);
|
||
|
house.receiveShadow = true;
|
||
|
house.castShadow = true;
|
||
|
houseGroup.add(house);
|
||
|
// roof
|
||
|
let roofGeo = new THREE.ConeBufferGeometry(1.1, 0.7, 4);
|
||
|
let roofMat = new THREE.MeshLambertMaterial({ color: colors.red });
|
||
|
let roof = new THREE.Mesh(roofGeo, roofMat);
|
||
|
roof.position.set(-1, 1.6, 1);
|
||
|
roof.rotation.y = THREE.Math.degToRad(45);
|
||
|
roof.castShadow = true;
|
||
|
roof.receiveShadow = true;
|
||
|
houseGroup.add(roof);
|
||
|
// roof chimney
|
||
|
let chimneyMat = new THREE.MeshLambertMaterial({ color: colors.house });
|
||
|
let chimney = new THREE.Mesh(boxGeo, chimneyMat);
|
||
|
chimney.position.set(-1, 1.6, 0.6);
|
||
|
chimney.scale.set(0.2, 0.3, 0.2);
|
||
|
chimney.receiveShadow = true;
|
||
|
houseGroup.add(chimney);
|
||
|
// door
|
||
|
let doorMat = new THREE.MeshLambertMaterial({ color: colors.purple });
|
||
|
let door = new THREE.Mesh(boxGeo, doorMat);
|
||
|
door.scale.set(0.2, 0.5, 0.35);
|
||
|
door.position.set(-0.49, 0.545, 1);
|
||
|
houseGroup.add(door);
|
||
|
// doorknob
|
||
|
let knobG = new THREE.SphereBufferGeometry(0.025, 8, 8);
|
||
|
let knobMat = new THREE.MeshLambertMaterial({ color: colors.gold });
|
||
|
let knob = new THREE.Mesh(knobG, knobMat);
|
||
|
houseGroup.add(knob);
|
||
|
knob.position.set(-0.39, 0.5, 1.14);
|
||
|
// doorstep
|
||
|
createBoxShape(0.05, 0.05, 0.35, -0.38, 0.27, 1, colors.grey, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.05, 0.35, -0.34, 0.25, 1, colors.grey, false, false, houseGroup);
|
||
|
// windows
|
||
|
createRoofWindow(-0.65, 1.6, 1, colors.blue, 0.1, 0.1, 0.2, 12);
|
||
|
createRoofWindow(-0.67, 1.60, 1, colors.red, 0.12, 0.12, 0.22, 12);
|
||
|
// roof window bars
|
||
|
createBoxShape(0.02, 0.24, 0.025, -0.55, 1.6, 1, colors.red, false, false, houseGroup);
|
||
|
createBoxShape(0.02, 0.02, 0.2, -0.55, 1.62, 1, colors.red, false, false, houseGroup);
|
||
|
// left window
|
||
|
createBoxShape(0.05, 0.3, 0.3, -0.41, 0.65, 1.45, colors.blue, false, false, houseGroup);
|
||
|
// vertical bars
|
||
|
createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 1.45, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 1.60, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 1.30, colors.brown2, false, false, houseGroup);
|
||
|
// horizontal bars
|
||
|
createBoxShape(0.05, 0.025, 0.325, -0.40, 0.8, 1.45, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.025, 0.325, -0.40, 0.65, 1.45, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.025, 0.325, -0.40, 0.5, 1.45, colors.brown2, false, false, houseGroup);
|
||
|
// right window
|
||
|
createBoxShape(0.05, 0.3, 0.3, -0.41, 0.65, 0.55, colors.blue, false, false, houseGroup);
|
||
|
// vertical bars
|
||
|
createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 0.55, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 0.70, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 0.40, colors.brown2, false, false, houseGroup);
|
||
|
// horizontal bars
|
||
|
createBoxShape(0.05, 0.025, 0.325, -0.40, 0.8, 0.55, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.025, 0.325, -0.40, 0.65, 0.55, colors.brown2, false, false, houseGroup);
|
||
|
createBoxShape(0.05, 0.025, 0.325, -0.40, 0.5, 0.55, colors.brown2, false, false, houseGroup);
|
||
|
mainGroup.add(houseGroup);
|
||
|
};
|
||
|
|
||
|
// chimney smoke
|
||
|
const puffCount = 2;
|
||
|
let puffs = [];
|
||
|
let puff;
|
||
|
const createPuffs = () => {
|
||
|
for (let i = 0; i < puffCount; i++) {
|
||
|
const geo = new THREE.SphereBufferGeometry(0.1, 6, 6);
|
||
|
const mat = new THREE.MeshLambertMaterial({ color: 0xffffff, transparent: true });
|
||
|
puff = new THREE.Mesh(geo, mat);
|
||
|
puffs.push(puff);
|
||
|
houseGroup.add(puff);
|
||
|
puff.position.set(-1, 1.74, 0.6);
|
||
|
puff.scale.set(0, 0, 0);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// mailbox
|
||
|
let mailBoxGroup = new THREE.Group();
|
||
|
|
||
|
const createMailBoxRoof = () => {
|
||
|
let mailRoofG = new THREE.ConeBufferGeometry(0.16, 0.1, 4);
|
||
|
let mailRoofMat = new THREE.MeshLambertMaterial({ color: colors.brown2 });
|
||
|
let mailRoofMesh = new THREE.Mesh(mailRoofG, mailRoofMat);
|
||
|
mailRoofMesh.position.set(0, 0.34, 0);
|
||
|
mailRoofMesh.rotation.y = THREE.Math.degToRad(45);
|
||
|
mailRoofMesh.receiveShadow = true;
|
||
|
mailRoofMesh.castShadow = true;
|
||
|
mailBoxGroup.add(mailRoofMesh);
|
||
|
};
|
||
|
|
||
|
const createMailbox = () => {
|
||
|
createMailBoxRoof();
|
||
|
// pole
|
||
|
createBoxShape(0.05, 0.3, 0.05, 0, 0, 0, colors.brown2, true, true, mailBoxGroup);
|
||
|
// box
|
||
|
createBoxShape(0.2, 0.2, 0.2, 0, 0.2, 0, colors.brown2, true, true, mailBoxGroup);
|
||
|
// hole
|
||
|
createBoxShape(0.05, 0.05, 0.16, 0.085, 0.22, 0, 0x634326, false, false, mailBoxGroup);
|
||
|
mainGroup.add(mailBoxGroup);
|
||
|
mailBoxGroup.position.set(1.5, 0.4, 1.5);
|
||
|
};
|
||
|
|
||
|
// Bunny
|
||
|
let pivot1 = new THREE.Group();
|
||
|
let pivot2 = new THREE.Group();
|
||
|
let pivot3 = new THREE.Group();
|
||
|
let bunnyGroup1 = new THREE.Group();
|
||
|
let bunnyGroup2 = new THREE.Group();
|
||
|
let bunnyGroup3 = new THREE.Group();
|
||
|
const createBunnyShape = (group, x, y, z, color, xPos, yPos, zPos) => {
|
||
|
let geo = new THREE.BoxBufferGeometry(x, y, z);
|
||
|
let mat = new THREE.MeshLambertMaterial({ color: color, transparent: true });
|
||
|
let mesh = new THREE.Mesh(geo, mat);
|
||
|
mesh.position.set(xPos, yPos, zPos);
|
||
|
mesh.receiveShadow = true;
|
||
|
mesh.castShadow = true;
|
||
|
group.add(mesh);
|
||
|
};
|
||
|
|
||
|
const bunnyColors = [0xFFFFFF, 0xEAEAEA, 0xCFAF8E];
|
||
|
const createBunny = (group, pivotPositionX, pivotPositionY, pivotPositionZ, pivot) => {
|
||
|
bunnyColor = bunnyColors[Math.round(Math.random() * 2)];
|
||
|
// head
|
||
|
createBunnyShape(group, 0.1, 0.1, 0.1, bunnyColor, 0, 0, 0.2);
|
||
|
// ears
|
||
|
createBunnyShape(group, 0.025, 0.14, 0.025, bunnyColor, 0.025, 0.05, 0.23);
|
||
|
createBunnyShape(group, 0.025, 0.14, 0.025, bunnyColor, -0.025, 0.05, 0.23);
|
||
|
// eyes
|
||
|
createBunnyShape(group, 0.02, 0.02, 0.02, "black", 0.025, 0.02, 0.25);
|
||
|
createBunnyShape(group, 0.02, 0.02, 0.02, "black", -0.025, 0.02, 0.25);
|
||
|
mainGroup.add(group);
|
||
|
// https://stackoverflow.com/questions/28848863/threejs-how-to-rotate-around-objects-own-center-instead-of-world-center
|
||
|
let box = new THREE.Box3().setFromObject(group);
|
||
|
box.getCenter(group.position); // this re-sets the mesh position
|
||
|
group.position.multiplyScalar(-1);
|
||
|
pivot.add(group);
|
||
|
pivot.position.set(pivotPositionX, pivotPositionY, pivotPositionZ);
|
||
|
mainGroup.add(pivot);
|
||
|
|
||
|
};
|
||
|
|
||
|
// GSAP ANIMATIONS
|
||
|
// make island floaty
|
||
|
const animateIsland = () => {
|
||
|
gsap.to(mainGroup.position, { y: '+=0.065', repeat: -1, yoyo: true, ease: "sine.in", duration: 2, yoyoEase: "sine.inOut" });
|
||
|
};
|
||
|
// animate single waterfall particle
|
||
|
const animateDrop = drop => {
|
||
|
const tl = gsap.timeline({
|
||
|
onStart: () => {
|
||
|
gsap.set(drop.position, { y: gsap.utils.random(-0.17, 0, 0.01) });
|
||
|
gsap.set(drop.scale, { x: 0.1, y: 0.1, z: 0.1 });
|
||
|
},
|
||
|
onComplete: animateDrop,
|
||
|
onCompleteParams: [drop] });
|
||
|
|
||
|
|
||
|
tl.to(drop.position, {
|
||
|
y: "-=1",
|
||
|
ease: "linear",
|
||
|
delay: gsap.utils.random(0, 2, 0.2),
|
||
|
duration: 1,
|
||
|
onStart: () => {
|
||
|
gsap.to(drop.scale, { x: 0, y: 0, z: 0, delay: 0.14, duration: 0.86 });
|
||
|
} });
|
||
|
|
||
|
return tl;
|
||
|
};
|
||
|
// animate single water detail
|
||
|
animateDet = det => {
|
||
|
const tl = gsap.timeline(
|
||
|
{ defaults: { duration: 1, ease: "sine.in" },
|
||
|
onStart: () => {
|
||
|
gsap.set(det.position, { x: gsap.utils.random(0.60, 0.92), z: gsap.utils.random(-1.8, 1.8) });
|
||
|
gsap.set(det.rotation, { y: 0, z: 0 });
|
||
|
gsap.set(det.material, { opacity: 0 });
|
||
|
},
|
||
|
onComplete: animateDet,
|
||
|
onCompleteParams: [det] });
|
||
|
|
||
|
tl.to(det.material, { keyframes: [{ opacity: 0.7 }, { opacity: 0 }] }, 'in').
|
||
|
to(det.position, { keyframes: [{ z: "+=0.025" }, { z: "-=0.025" }] }, 'in').
|
||
|
to(det.rotation, { keyframes: [{ y: "-=0.2" }, { z: "+=0.2" }] }, 'in');
|
||
|
|
||
|
return tl;
|
||
|
};
|
||
|
|
||
|
animateDet2 = det => {
|
||
|
const tl = gsap.timeline(
|
||
|
{ defaults: { duration: 1, ease: "sine.in" },
|
||
|
onStart: () => {
|
||
|
gsap.set(det.position, { x: gsap.utils.random(0.60, 0.92), y: gsap.utils.random(-0.18, 0.20) });
|
||
|
gsap.set(det.rotation, { y: 0, z: 0 });
|
||
|
gsap.set(det.material, { opacity: 0 });
|
||
|
},
|
||
|
onComplete: animateDet2,
|
||
|
onCompleteParams: [det] });
|
||
|
|
||
|
tl.to(det.material, { keyframes: [{ opacity: 0.7 }, { opacity: 0 }] }, 'in').
|
||
|
to(det.position, { keyframes: [{ y: "-=0.025" }, { y: "+=0.025" }] }, 'in').
|
||
|
to(det.rotation, { keyframes: [{ y: "-=0.2" }, { z: "+=0.2" }] }, 'in');
|
||
|
|
||
|
return tl;
|
||
|
};
|
||
|
|
||
|
|
||
|
// animate single puff
|
||
|
const animatePuff = puff => {
|
||
|
const tl = gsap.timeline({ onComplete: animatePuff, onCompleteParams: [puff], onStart: () => {
|
||
|
gsap.set(puff.material, { opacity: 1 });
|
||
|
gsap.set(puff.scale, { x: 0, y: 0, z: 0 });
|
||
|
gsap.set(puff.position, { y: 1.74 });
|
||
|
} });
|
||
|
tl.to(puff.position, { y: '+=0.6', duration: 2, delay: 0.6, ease: "sine.inOut", onStart: () => {
|
||
|
gsap.to(puff.scale, { keyframes: [{ x: 1, y: 1.4, z: 1 }, { z: 1.4, duration: 0.24 }, { y: 0.8, delay: -0.44, duration: 0.24 }, { x: 1, y: 1 }] });
|
||
|
gsap.to(puff.material, { opacity: 0, duration: 1.32, delay: 0.68 });
|
||
|
} });
|
||
|
};
|
||
|
// function to loop over particles
|
||
|
const animateParticles = (fn, array) => {
|
||
|
for (let i = 0; i < array.length; i++) {
|
||
|
fn(array[i]);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// animate the bunny
|
||
|
// eye blink
|
||
|
const animateBunnyEyes = (group, delay) => {
|
||
|
const tl = gsap.timeline({ repeat: -1, repeatDelay: 1, defaults: { duration: 0.2 }, delay: delay });
|
||
|
const eyes = [group.children[3].material, group.children[4].material];
|
||
|
tl.to(eyes, { keyframes: [{ opacity: 0 }, { opacity: 1 }] }, 'blink');
|
||
|
return tl;
|
||
|
};
|
||
|
|
||
|
// hop and move
|
||
|
const animateBunny = (pivot, delay) => {
|
||
|
const tl = gsap.timeline({ repeat: -1, defaults: { duration: 0.32 }, delay: delay });
|
||
|
tl.to(pivot.position, { keyframes: [{ y: '+=0.1', z: '+=0.1' }, { y: '-=0.1' }, { y: '+=0.1', z: '+=0.1' }, { y: '-=0.1' }] }).
|
||
|
to(pivot.rotation, { y: 3, duration: 0.6, delay: 0.16 }).
|
||
|
to(pivot.position, { keyframes: [{ y: '+=0.1', z: '-=0.1' }, { y: '-=0.1' }, { y: '+=0.1', z: '-=0.1' }, { y: '-=0.1' }] }).
|
||
|
to(pivot.rotation, { y: 0, duration: 0.6, delay: 0.16 });
|
||
|
return tl;
|
||
|
};
|
||
|
|
||
|
// render
|
||
|
render = () => {
|
||
|
|
||
|
controls.update();
|
||
|
requestAnimationFrame(render);
|
||
|
renderer.render(scene, camera);
|
||
|
};
|
||
|
|
||
|
// resize
|
||
|
const resizeHandler = () => {
|
||
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||
|
camera.updateProjectionMatrix();
|
||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
|
};
|
||
|
|
||
|
window.addEventListener("resize", () => {
|
||
|
resizeHandler();
|
||
|
});
|
||
|
|
||
|
window.addEventListener('load', () => {
|
||
|
setup();
|
||
|
createIsland();
|
||
|
animateIsland();
|
||
|
// waterfall particles creation
|
||
|
createParticles(
|
||
|
colors.blue,
|
||
|
dropCount,
|
||
|
drops,
|
||
|
0.1, 0.1, 0.1,
|
||
|
0.56, 0.95, 0, -0.19, 1.95, 1.95,
|
||
|
1,
|
||
|
0, 0, 0);
|
||
|
|
||
|
// animate waterfall particles
|
||
|
animateParticles(animateDrop, drops);
|
||
|
// water details creation
|
||
|
createParticles(
|
||
|
"white",
|
||
|
detailCount,
|
||
|
dets,
|
||
|
0.025, 0.025, 0.025,
|
||
|
0.60, 0.92, 0.25, 0.26, 1.8, -1.8,
|
||
|
0,
|
||
|
0, 0, 0);
|
||
|
|
||
|
createParticles(
|
||
|
"white",
|
||
|
detailCount2,
|
||
|
dets2,
|
||
|
0.025, 0.025, 0.025,
|
||
|
0.60, 0.92, -0.2, 0.23, 2, 2,
|
||
|
0,
|
||
|
0, 0, 0);
|
||
|
|
||
|
// animate water details
|
||
|
animateParticles(animateDet, dets);
|
||
|
animateParticles(animateDet2, dets2);
|
||
|
// trees
|
||
|
// trees next to house
|
||
|
createTree(0, 0.75, -0.1, 0, 1.2, -0.1);
|
||
|
createTree(-1.50, 0.75, -0.1, -1.50, 1.2, -0.1);
|
||
|
createTree(-0.75, 0.75, -0.5, -0.75, 1.2, -0.5);
|
||
|
createTree(0, 0.75, -1, 0, 1.2, -1);
|
||
|
createTree(-1.50, 0.75, -1, -1.50, 1.2, -1);
|
||
|
createTree(-0.75, 0.75, -1.5, -0.75, 1.2, -1.5);
|
||
|
// other trees
|
||
|
createTree(1.5, 0.75, -1.5, 1.5, 1.2, -1.5);
|
||
|
createTree(1.5, 0.75, -0.5, 1.5, 1.2, -0.5);
|
||
|
createTree(1.5, 0.75, 0.5, 1.5, 1.2, 0.5);
|
||
|
// bushes next to house
|
||
|
createBush(-0.7, 0.28, -0.1);
|
||
|
createBush(-0.7, 0.28, -1);
|
||
|
createBush(-1.55, 0.28, -0.6);
|
||
|
createBush(-1.35, 0.28, -1.5);
|
||
|
//
|
||
|
createBush(1.5, 0.28, 1);
|
||
|
createBush(1.5, 0.28, 0);
|
||
|
createBush(1.5, 0.28, -1);
|
||
|
// house
|
||
|
createHouse();
|
||
|
// chimney smoke
|
||
|
createPuffs();
|
||
|
// animate the smoke
|
||
|
animateParticles(animatePuff, puffs);
|
||
|
// mailbox
|
||
|
createMailbox();
|
||
|
// bunnies
|
||
|
createBunny(bunnyGroup1, 0, 0.33, 0.2, pivot1);
|
||
|
createBunny(bunnyGroup2, -1, 0.33, -1, pivot2);
|
||
|
createBunny(bunnyGroup3, -0.2, 0.33, -1.4, pivot3);
|
||
|
animateBunny(pivot1, 0);
|
||
|
animateBunny(pivot2, gsap.utils.random(0, 3, 0.4));
|
||
|
animateBunny(pivot3, gsap.utils.random(0, 3, 0.4));
|
||
|
animateBunnyEyes(bunnyGroup1, 0);
|
||
|
animateBunnyEyes(bunnyGroup2, gsap.utils.random(0, 3, 0.4));
|
||
|
animateBunnyEyes(bunnyGroup3, gsap.utils.random(0, 3, 0.4));
|
||
|
render();
|
||
|
});
|