Boom door de seizoenen Nieuw
Scroll van maart naar februari en een boom gaat door het jaar: bloesem, vol groen blad, verkleuren en blad dat valt, dan kale takken met sneeuw. Een kaartje noemt de maand en wat de hovenier dan doet. Voor de werkzaamhedenpagina of hero van een hovenier, kwekerij of tuincentrum. Eigen SVG en eigen scrollvak met toetsenbord; bij minder beweging vallen bladeren en sneeuw niet maar verschijnen en verdwijnen ze.
Maart
Lente Hagen snoeien, borders planten en het gazon bijzaaien.
Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- Boom door de seizoenen · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="bs">
<div class="bs__vak" tabindex="0" role="region" aria-label="Een boom door het jaar, van maart tot februari. Scroll in dit vak.">
<div class="bs__baan">
<div class="bs__beeld">
<svg class="bs__svg" viewBox="0 0 240 150" preserveAspectRatio="xMidYMax slice" aria-hidden="true">
<rect class="bs__grond" x="0" y="128" width="240" height="22" fill="#9cc06a"/>
<path d="M121 130C120 112 118 100 119 86M119 100C110 92 100 86 92 72M119 96C128 88 138 82 146 68M119 86C118 74 112 62 106 50M119 86C124 74 130 60 134 48M97 78C88 74 82 70 78 62M141 74C150 70 156 66 160 58" fill="none" stroke="#5b4636" stroke-width="4" stroke-linecap="round"/>
<path d="M115 130L127 130L123 100L118 100Z" fill="#5b4636"/>
<path class="bs__sneeuwtak" d="M92 71C100 84 110 91 119 98M146 67C138 81 128 88 119 95M106 49C112 60 117 72 119 84M134 47C130 59 124 72 119 84M78 61C82 69 88 73 97 77M160 57C156 65 150 69 141 73" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round" opacity="0"/>
<g class="bs__blad"></g>
<g class="bs__vlok"></g>
</svg>
<div class="bs__kaart" aria-live="polite"><p class="bs__maand">Maart</p><p class="bs__taak"><strong>Lente</strong> Hagen snoeien, borders planten en het gazon bijzaaien.</p></div>
</div>
</div>
</div>
<p class="bs__hint" aria-hidden="true">Scroll door het jaar</p>
</div>
</div>/* Boom door de seizoenen · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.bs{position:absolute;inset:0;container-type:size;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;background:#dfeaf0}
.bs__vak{position:absolute;inset:0;overflow-y:auto;scrollbar-width:none}
.bs__vak::-webkit-scrollbar{display:none}
.bs__vak:focus-visible{outline:2px solid #F5480F;outline-offset:-2px}
.bs__baan{height:600cqh}
.bs__beeld{position:sticky;top:0;height:100cqh;overflow:hidden;background:#dfeaf0}
.bs__svg{position:absolute;inset:0;width:100%;height:100%;display:block}
.bs__kaart{position:absolute;left:10px;top:10px;max-width:min(46%,210px);background:#fff;border-radius:4px;padding:7px 10px}
.bs__maand{margin:0;font-size:clamp(17px,5.4cqw,22px);font-weight:700;line-height:1.1}
.bs__taak{margin:3px 0 0;font-size:12px;line-height:1.35;color:#333}
.bs__taak strong{display:block;color:#b83a0b;font-weight:600}
.bs__hint{position:absolute;right:10px;bottom:8px;margin:0;font-size:12px;background:#fff;padding:3px 8px;border-radius:4px;pointer-events:none;transition:opacity .3s}
.bs.is-weg .bs__hint{opacity:0}// Boom door de seizoenen · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie
// root = het element waarin de HTML staat
function init(root){
const w = root.querySelector('.bs'); if (!w) return;
const vak = w.querySelector('.bs__vak'), beeld = w.querySelector('.bs__beeld'), grond = w.querySelector('.bs__grond'), tak = w.querySelector('.bs__sneeuwtak');
const gB = w.querySelector('.bs__blad'), gV = w.querySelector('.bs__vlok'), maand = w.querySelector('.bs__maand'), taak = w.querySelector('.bs__taak');
const rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
const NS = 'http://www.w3.org/2000/svg';
let seed = 7; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647;
const BL = ['#ffffff', '#f4c6cc'], GR = ['#6f9e3f', '#5d8a35', '#86b24f'], HF = ['#F5480F', '#e8a33d', '#c4512a', '#d9822b'];
const blad = [];
for (let i = 0; i < 52; i++) {
const a = rnd() * Math.PI * 2, r = Math.sqrt(rnd());
const x = 119 + Math.cos(a) * r * 50, y = 66 + Math.sin(a) * r * 30;
const c = document.createElementNS(NS, 'circle'); c.setAttribute('r', (5 + rnd() * 3).toFixed(1)); gB.appendChild(c);
blad.push({ c, x, y, bl: BL[i % 2], gr: GR[i % 3], hf: HF[i % 4], tg: .12 + rnd() * .12, th: .48 + rnd() * .1, val: .6 + rnd() * .14, gx: x + (rnd() - .5) * 60, gy: 131 + rnd() * 12 });
}
const vlok = [];
for (let i = 0; i < 16; i++) { const c = document.createElementNS(NS, 'circle'); c.setAttribute('r', 1.4); c.setAttribute('fill', '#fff'); gV.appendChild(c); vlok.push({ c, x: rnd() * 240, y: rnd() * 150 }); }
const hex = h => [1, 3, 5].map(i => parseInt(h.slice(i, i + 2), 16));
const mix = (a, b, t) => { const x = hex(a), y = hex(b); return 'rgb(' + x.map((v, i) => Math.round(v + (y[i] - v) * t)).join(',') + ')'; };
const cl = v => v < 0 ? 0 : v > 1 ? 1 : v;
const M = ['Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December', 'Januari', 'Februari'];
const SZ = [['Lente', 'Hagen snoeien, borders planten en het gazon bijzaaien.'], ['Zomer', 'Maaien, water geven en de vijver bijhouden.'], ['Herfst', 'Blad ruimen en bollen planten voor het voorjaar.'], ['Winter', 'Bomen snoeien en kappen nu de sap stilstaat.']];
const LU = ['#dfeaf0', '#cfe3f2', '#efe2d0', '#e3e7ea', '#e3e7ea'], GD = ['#9cc06a', '#7fae4e', '#a99b5a', '#fbfbfb', '#fbfbfb'];
let raf = 0, nu = -1;
function teken() {
raf = 0; const m = vak.scrollHeight - vak.clientHeight, p = m > 0 ? vak.scrollTop / m : 0;
const s = Math.min(3.999, p * 4), k = Math.floor(s), f = cl((s - k - .6) / .4);
beeld.style.background = mix(LU[k], LU[k + 1], f); grond.setAttribute('fill', mix(GD[k], GD[k + 1], f));
const winter = cl((p - .76) / .06);
tak.setAttribute('opacity', winter.toFixed(2));
blad.forEach(b => {
let kl, sc = 1, x = b.x, y = b.y, op = 1;
const g = cl((p - b.tg) / .025), h = cl((p - b.th) / .025);
sc = (.35 + .45 * cl(p / .1)) * (1 - g) + g;
kl = h > 0 ? mix(b.gr, b.hf, h) : g > 0 ? mix(b.bl, b.gr, g) : b.bl;
if (p > b.val) {
const t = cl((p - b.val) / .06);
if (rm) op = 1 - t; else { x = b.x + (b.gx - b.x) * t; y = b.y + (b.gy - b.y) * t * t; }
}
if (p > .78) op = Math.min(op, 1 - cl((p - .78) / .06));
b.c.setAttribute('cx', x.toFixed(1)); b.c.setAttribute('cy', y.toFixed(1)); b.c.setAttribute('fill', kl);
b.c.style.transform = 'scale(' + sc.toFixed(3) + ')'; b.c.style.transformOrigin = x.toFixed(1) + 'px ' + y.toFixed(1) + 'px';
b.c.setAttribute('opacity', op.toFixed(2));
});
vlok.forEach(v => { const y = rm ? v.y : (v.y + (p - .76) * 1400) % 150; v.c.setAttribute('cx', v.x.toFixed(1)); v.c.setAttribute('cy', y.toFixed(1)); v.c.setAttribute('opacity', winter.toFixed(2)); });
const mi = Math.min(11, Math.floor(p * 12));
if (mi !== nu) { nu = mi; const sz = SZ[Math.floor(mi / 3)]; maand.textContent = M[mi]; taak.innerHTML = '<strong>' + sz[0] + '</strong> ' + sz[1]; }
w.classList.toggle('is-weg', vak.scrollTop > 8);
}
vak.addEventListener('scroll', () => { if (!raf) raf = requestAnimationFrame(teken); }, { passive: true });
teken();
}
init(document.querySelector('.jouw-container'));
Meer uit scroll-effecten
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →