A camera push over layers
A title stands among three ridges of hills at different depths, and the camera pushes in and drifts right across them, as if held by hand.
camera() moves the whole world, depth() sets each layer back or forward, and shake() adds the hand, all on flat HTML with no three.js.
-
A camera push over layers
Four layers sit at different depths, and camera() pushes in and drifts across them with a light handheld shake().
depth(z)puts a layerzpx from the stage, farther away when negative, and scales it so a stage-sized layer at rest looks exactly as it does at 0. The camera's perspective then gives each layer its own parallax: as the camera pushes in, the near ridge grows fastest and the far one barely moves.camera(t, tracks, options)reads each key as a track att, here a dolly onzand a truck onx, and returns the style for the world, the one element that holds every layer.shake(t, 'handheld', { seed, amount })returns small offsets from noise, the same on every machine, and the camera'sshakeoption adds them to itsx,yandroll.amount: 0.6keeps the hand light.
<div id="stage" data-composition-id="camera" data-start="0" data-duration="6" data-width="1280" data-height="720" data-fps="30">
<div class="scene" data-component="camera" data-start="0" data-duration="6">
<div class="world" data-bind-style="world">
<div class="plane" data-bind-style="far"><div class="ridge far"></div></div>
<div class="plane" data-bind-style="mid"><div class="ridge mid"></div></div>
<div class="plane" data-bind-style="title"><h1 class="title">DEPTH</h1></div>
<div class="plane" data-bind-style="near"><div class="ridge near"></div></div>
</div>
</div>
</div>
const { scene, camera, depth, shake } = wildflower.kinetic;
// A slow push in while the camera drifts right. The near layer grows
// fastest and the far one barely moves: the parallax comes from the depths.
const CAM = {
z: { at: [0, 5], to: [0, 260], ease: 'power2.inOut' },
x: { at: [0, 6], to: [-90, 90], ease: 'sine.inOut' },
};
scene('camera', {
computed: {
world() {
return camera(this.local, CAM, { shake: shake(this.local, 'handheld', { seed: 3, amount: 0.6 }) });
},
far() { return depth(-1500); },
mid() { return depth(-600); },
title() { return depth(0); },
near() { return depth(300); },
},
});
The sky goes on the stage and never on the world.
The world takes no background, opacity, filter or clip-path: a background on it would sit in front of every layer behind it, and any of the others draws every layer flat.
/* The sky goes on the stage, never on the world: a background on the world
would sit in front of every layer behind it. */
#stage { position: relative; width: 1280px; height: 720px; overflow: hidden;
background: linear-gradient(#0b1030, #2a2160 50%, #b8577a 80%, #f0a35e); }
#stage:not([data-kinetic-ready]) { visibility: hidden; }
.scene, .world, .plane { position: absolute; inset: 0; }
.ridge { position: absolute; left: -30%; right: -30%; bottom: -10%; }
.far { height: 52%; background: #4a3478; clip-path: polygon(0 40%, 12% 22%, 26% 38%, 41% 12%, 55% 34%, 70% 18%, 84% 36%, 100% 20%, 100% 100%, 0 100%); }
.mid { height: 38%; background: #2b1d58; clip-path: polygon(0 30%, 18% 50%, 33% 22%, 50% 44%, 66% 20%, 82% 46%, 100% 28%, 100% 100%, 0 100%); }
.near { height: 24%; background: #120c2a; clip-path: polygon(0 20%, 15% 0, 30% 30%, 48% 10%, 64% 34%, 80% 6%, 100% 26%, 100% 100%, 0 100%); }
.title { position: absolute; left: 0; right: 0; top: 230px; margin: 0; text-align: center;
font: 800 150px/1 system-ui, sans-serif; letter-spacing: 0.12em; color: #fff4e6; text-shadow: 0 8px 40px rgba(30, 0, 50, 0.6); }
A three.js scene has a camera of its own, which A camera push and a fly-through moves through a formation of beads.