Bloemen die opbloeien waar je klikt Nieuw
Waar je klikt of tikt groeit een steel uit de grond omhoog en bloeit een bloem open, elke keer met andere blaadjes en kleuren; na twaalf bloemen verwelkt de oudste. De knop "Plant een bloem" doet hetzelfde voor toetsenbordgebruikers, en bij minder beweging staan de bloemen er meteen. Voor de bedankpagina van een hovenier, bloemist of tuincentrum, of de lege staat van een wensenlijst.
Bedankt, je aanvraag is binnen
Tuinontwerp De Linde komt dinsdag 12 november langs in Bussum. Tik ergens om een bloem te planten.
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.
<!-- Bloemen die opbloeien waar je klikt · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="blk">
<svg class="blk__veld" aria-hidden="true"></svg>
<div class="blk__tekst">
<p class="blk__kop">Bedankt, je aanvraag is binnen</p>
<p class="blk__sub">Tuinontwerp De Linde komt dinsdag 12 november langs in Bussum. Tik ergens om een bloem te planten.</p>
<button type="button" class="blk__knop">Plant een bloem</button>
</div>
<p class="blk__sr" aria-live="polite"></p>
</div>
</div>/* Bloemen die opbloeien waar je klikt · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.blk{position:relative;width:100%;height:100%;overflow:hidden;background:#f4f3f1;font-family:Switzer,sans-serif;cursor:pointer}
.blk::after{content:"";position:absolute;left:0;right:0;bottom:0;height:8px;background:#0a0a0a}
.blk__veld{position:absolute;inset:0;width:100%;height:100%}
.blk__tekst{position:relative;z-index:1;width:min(62%,250px);margin:14px 0 0 16px;padding:10px 12px;background:rgba(244,243,241,.92);border-radius:4px;cursor:default}
.blk__kop{margin:0;font-size:16px;font-weight:600;line-height:1.2;color:#0a0a0a}
.blk__sub{margin:4px 0 8px;font-size:13px;line-height:1.35;color:#55524d}
.blk__knop{font:inherit;font-size:13px;min-height:30px;padding:0 12px;border:0;border-radius:4px;background:#0a0a0a;color:#fff;cursor:pointer;transition:background-color .15s}
.blk__knop:hover{background:#F5480F}
.blk__knop:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.blk__sr{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
@media (max-width:420px){.blk__tekst{margin:10px 0 0 10px;width:66%;padding:8px 10px}.blk__kop{font-size:15px}.blk__sub{font-size:12px}}// Bloemen die opbloeien waar je klikt · © 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('.blk'); if (!w) return;
const svg = w.querySelector('.blk__veld'), knop = w.querySelector('.blk__knop'), sr = w.querySelector('.blk__sr'), tekst = w.querySelector('.blk__tekst');
const rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
const NS = 'http://www.w3.org/2000/svg';
const SOORTEN = [
{ blad:'#F5480F', hart:'#0a0a0a' }, { blad:'#ffffff', hart:'#F5480F' },
{ blad:'#ffb08f', hart:'#0a0a0a' }, { blad:'#0a0a0a', hart:'#F5480F' }
];
const bloemen = []; let raf = 0;
const mk = (n, a) => { const e = document.createElementNS(NS, n); for (const k in a) e.setAttribute(k, a[k]); return e; };
const uit = t => { const c1 = 1.9, c3 = c1 + 1; return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2); };
const glad = t => 1 - Math.pow(1 - t, 3);
function plant(x, y, direct){
const H = svg.clientHeight, grond = H - 8;
y = Math.min(grond - 24, Math.max(22, y));
const soort = SOORTEN[Math.floor(Math.random() * SOORTEN.length)];
const n = 5 + Math.floor(Math.random() * 4), r = 9 + Math.random() * 6, bocht = (Math.random() - .5) * 40;
const g = mk('g', {});
const bx = x + bocht * 0.3;
const steel = mk('path', { d: 'M' + bx + ' ' + grond + ' Q' + (x + bocht) + ' ' + ((grond + y) / 2) + ' ' + x + ' ' + y, fill: 'none', stroke: '#0a0a0a', 'stroke-width': 2, 'stroke-linecap': 'round' });
g.appendChild(steel);
const my = (grond + y) / 2, mx = (bx + x + bocht) / 2 - bocht * 0.25;
const blad1 = mk('path', { d: 'M0 0 Q9 -9 18 -2 Q9 3 0 0 Z', fill: '#0a0a0a' });
const blad2 = mk('path', { d: 'M0 0 Q-9 -9 -16 -2 Q-8 3 0 0 Z', fill: '#0a0a0a' });
g.appendChild(blad1); g.appendChild(blad2);
const kop = mk('g', {});
const fase = Math.random() * 360;
const blaadjes = [];
for (let i = 0; i < n; i++){
const b = mk('ellipse', { cx: 0, cy: -r * 0.62, rx: r * 0.42, ry: r * 0.62, fill: soort.blad, stroke: soort.blad === '#ffffff' ? '#d6d3ce' : 'none', 'stroke-width': 1 });
const bg = mk('g', {}); bg.appendChild(b); kop.appendChild(bg); blaadjes.push({ el: bg, hoek: fase + i * 360 / n });
}
const hart = mk('circle', { cx: 0, cy: 0, r: r * 0.32, fill: soort.hart });
kop.appendChild(hart); g.appendChild(kop); svg.appendChild(g);
const len = steel.getTotalLength();
const f = { g, steel, blad1, blad2, kop, hart, blaadjes, len, x, y, mx, my, t0: performance.now(), r, weg: 0 };
bloemen.push(f);
if (bloemen.length > 12) { const oud = bloemen.find(b => !b.weg); if (oud) oud.weg = performance.now(); }
teken(f, direct || rm ? 99999 : 0);
if (!direct && !rm) start();
}
function teken(f, ms){
const k = Math.min(1, ms / 520);
f.steel.setAttribute('stroke-dasharray', f.len); f.steel.setAttribute('stroke-dashoffset', (f.len * (1 - glad(k))).toFixed(2));
const kb = Math.max(0, Math.min(1, (ms - 300) / 380)), sb = kb ? uit(kb) : 0.001;
f.blad1.setAttribute('transform', 'translate(' + f.mx + ' ' + f.my + ') rotate(-20) scale(' + sb.toFixed(3) + ')');
f.blad2.setAttribute('transform', 'translate(' + (f.mx - 1) + ' ' + (f.my + 10) + ') rotate(18) scale(' + sb.toFixed(3) + ')');
f.kop.setAttribute('transform', 'translate(' + f.x + ' ' + f.y + ')');
f.blaadjes.forEach((b, i) => {
const kk = Math.max(0, Math.min(1, (ms - 460 - i * 45) / 420)), s = kk ? uit(kk) : 0.001;
b.el.setAttribute('transform', 'rotate(' + (b.hoek - 40 * (1 - kk)).toFixed(1) + ') scale(' + s.toFixed(3) + ')');
});
const kh = Math.max(0, Math.min(1, (ms - 420) / 300));
f.hart.setAttribute('r', (f.r * 0.32 * (kh ? uit(kh) : 0.001)).toFixed(2));
return k >= 1 && ms > 460 + f.blaadjes.length * 45 + 420;
}
function tik(t){
raf = 0; let bezig = false;
for (let i = bloemen.length - 1; i >= 0; i--){
const f = bloemen[i];
if (f.weg){
const k = Math.min(1, (t - f.weg) / 600); f.g.setAttribute('opacity', (1 - k).toFixed(3));
f.g.setAttribute('transform', 'translate(0 ' + (k * 6).toFixed(1) + ')');
if (k >= 1) { f.g.remove(); bloemen.splice(i, 1); } else bezig = true;
}
if (!f.klaar) { f.klaar = teken(f, t - f.t0); if (!f.klaar) bezig = true; }
}
if (bezig) raf = requestAnimationFrame(tik);
}
function start(){ if (!raf) raf = requestAnimationFrame(tik); }
function opruim(){ if (rm) { while (bloemen.length > 12) { bloemen.shift().g.remove(); } } }
w.addEventListener('pointerdown', e => {
if (tekst.contains(e.target)) return;
const r = svg.getBoundingClientRect(); plant(e.clientX - r.left, e.clientY - r.top); opruim();
});
knop.addEventListener('click', () => {
const W = svg.clientWidth, H = svg.clientHeight;
plant(W * (0.62 + Math.random() * 0.33), H * (0.35 + Math.random() * 0.4)); opruim();
sr.textContent = 'Er staat een nieuwe bloem in de tuin.';
});
function eerste(){
const W = svg.clientWidth, H = svg.clientHeight; if (!W) return false;
plant(W * 0.72, H * 0.52, true); plant(W * 0.86, H * 0.36, true); plant(W * 0.93, H * 0.66, true);
bloemen.forEach(f => { f.klaar = true; });
return true;
}
if (!eerste() && 'ResizeObserver' in window) { const ro = new ResizeObserver(() => { if (eerste()) ro.disconnect(); }); ro.observe(svg); }
}
init(document.querySelector('.jouw-container'));
Meer uit componenten
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 →