Depth bands for hundreds of elements
Four hundred and fifty motes sit in three layers at three depths, and as the focal plane moves, whole layers soften and sharpen.
Each band is a static pool inside a container whose filter comes from a computed that calls focus() with steps.
init()places the motes once withrandom(seed)and nothing writes to them again, so the three container filters are the only styles that change per frame. The cost is three filters however many motes there are, and a mote's band is fixed when it is added.steps: 6rounds a band's blur to one of six levels, so its filter is re-rasterised only when the level changes.
It builds on Many particles, which places a static pool the same way.
The stylesheet needs .band { position: absolute; inset: 0; } and .mote { position: absolute; border-radius: 50%; background: #fff; }.
<div id="stage" data-composition-id="bands" data-start="0" data-duration="5" data-width="1280" data-height="720" data-fps="30">
<div class="layer" data-component="bands" data-start="0" data-duration="5">
<div class="band" data-bind-style="far"><div data-pool="far" data-key="id"><template><i class="mote" data-bind-style="style"></i></template></div></div>
<div class="band" data-bind-style="mid"><div data-pool="mid" data-key="id"><template><i class="mote" data-bind-style="style"></i></template></div></div>
<div class="band" data-bind-style="near"><div data-pool="near" data-key="id"><template><i class="mote" data-bind-style="style"></i></template></div></div>
</div>
</div>
const { scene, random, focus } = wildflower.kinetic;
const BANDS = { far: -900, mid: -450, near: 0 }; // one depth per band
const PER_BAND = 150;
scene('bands', {
pools: { far: {}, mid: {}, near: {} },
keyframes: {
plane: { at: [0.5, 4.5], to: [-900, 0], ease: 'power2.inOut' },
},
init() {
const rnd = random(11);
for (const name of Object.keys(BANDS)) {
const scale = 1 + BANDS[name] / 1800; // nearer bands have larger motes
for (let i = 0; i < PER_BAND; i++) {
const size = (2 + rnd() * 4) * scale;
this.pools[name].add({ id: i, style: { left: (rnd() * 1280).toFixed(1) + 'px', top: (rnd() * 720).toFixed(1) + 'px', width: size.toFixed(1) + 'px', height: size.toFixed(1) + 'px' } });
}
}
},
computed: {
far() { return { filter: 'blur(' + focus(BANDS.far, this.plane, { range: 300, steps: 6 }) + 'px)' }; },
mid() { return { filter: 'blur(' + focus(BANDS.mid, this.plane, { range: 300, steps: 6 }) + 'px)' }; },
near() { return { filter: 'blur(' + focus(BANDS.near, this.plane, { range: 300, steps: 6 }) + 'px)' }; },
},
});
html, body { margin: 0; background: #0a0b10; }
#stage { position: relative; overflow: hidden; background: #0b0f1a; }
.layer { position: absolute; inset: 0; }
.band { position: absolute; inset: 0; }
.mote { position: absolute; border-radius: 50%; background: #fff; }
[data-composition-id]:not([data-kinetic-ready]) { visibility: hidden; }
Depth bands for hundreds of elements
No demo uses this pattern yet.