Zeepbellen die je wegblaast Nieuw
Zeepbellen stijgen langzaam op en wiebelen een beetje; beweeg je muis of vinger erdoor en ze waaien weg in jouw richting, tik er één aan en hij knapt. Met de knop "Blaas" krijgt iedereen een windvlaag, ook met het toetsenbord. Voor de hero van een kinderopvang, speeltuin, zwembad, kinderfeestjesbedrijf of wasserette. Canvas 2D; met minder beweging hangen de bellen stil.
Kinderopvang Het Speelkwartier
Zwolle · open dag woensdag 13 november
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.
<!-- Zeepbellen die je wegblaast · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="zpb">
<canvas class="zpb__cv" aria-hidden="true"></canvas>
<div class="zpb__txt"><p class="zpb__h">Kinderopvang Het Speelkwartier</p><p class="zpb__p">Zwolle · open dag woensdag 13 november</p></div>
<div class="zpb__acties"><a class="zpb__btn" href="#">Plan een rondleiding</a><button class="zpb__blaas" type="button">Blaas</button></div>
</div>
</div>/* Zeepbellen die je wegblaast · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.zpb{position:absolute;inset:0;background:#f4f3f1;font-family:Switzer,sans-serif;color:#0a0a0a;overflow:hidden}
.zpb__cv{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:pan-y}
.zpb__txt{position:absolute;left:16px;top:14px;right:16px;pointer-events:none}
.zpb__h{margin:0;font:600 18px/1.2 Switzer,sans-serif}
.zpb__p{margin:4px 0 0;font:400 14px/1.35 Switzer,sans-serif;color:#3a3a3a}
.zpb__acties{position:absolute;left:16px;bottom:14px;display:flex;gap:8px}
.zpb__btn{display:inline-flex;align-items:center;min-height:36px;padding:0 14px;border:0;border-radius:4px;font:600 14px/1 Switzer,sans-serif;cursor:pointer;text-decoration:none;background:#0a0a0a;color:#fff;transition:background-color .18s,color .18s}
.zpb__btn:hover{background:#F5480F;color:#0a0a0a}
.zpb__btn:focus-visible{outline:2px solid #F5480F;outline-offset:3px}
.zpb__blaas{display:inline-flex;align-items:center;min-height:36px;padding:0 14px;border:1px solid #0a0a0a;border-radius:4px;background:#fff;color:#0a0a0a;font:500 14px/1 Switzer,sans-serif;cursor:pointer}
.zpb__blaas:hover{border-color:#F5480F}.zpb__blaas:focus-visible{outline:2px solid #F5480F;outline-offset:3px}
@media (prefers-reduced-motion: reduce){.zpb__btn{transition:none}}// Zeepbellen die je wegblaast · © 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){
var reduce=!!(window.matchMedia&&matchMedia('(prefers-reduced-motion: reduce)').matches);
function fit(cv,ctx,cb){var dpr=Math.min(window.devicePixelRatio||1,2);function f(){var r=cv.getBoundingClientRect();var w=Math.max(1,Math.round(r.width)),h=Math.max(1,Math.round(r.height));if(cv.width===Math.round(w*dpr)&&cv.height===Math.round(h*dpr))return;cv.width=Math.round(w*dpr);cv.height=Math.round(h*dpr);if(ctx)ctx.setTransform(dpr,0,0,dpr,0,0);if(cb)cb(w,h,dpr);}if(window.ResizeObserver)new ResizeObserver(f).observe(cv);f();}
function loop(el,step){var on=false,raf=0,last=0;function fr(t){raf=requestAnimationFrame(fr);var dt=last?Math.min(.05,(t-last)/1000):.016;last=t;step(dt,t/1000);}function go(v){if(v&&!on){on=true;last=0;raf=requestAnimationFrame(fr);}else if(!v&&on){on=false;cancelAnimationFrame(raf);}}if(window.IntersectionObserver)new IntersectionObserver(function(es){go(es[0].isIntersecting);}).observe(el);else go(true);}
var w = root.querySelector('.zpb'), cv = w.querySelector('.zpb__cv'), ctx = cv.getContext('2d');
var W = 1, H = 1, bs = [], pops = [], t = 0, mx = -999, my = -999, pvx = 0, pvy = 0, lx = null, ly = null, lt = 0;
function mk(b, bottom) { b.r = 10 + Math.random() * 24; b.x = Math.random() * W; b.y = bottom ? H + b.r + Math.random() * 40 : Math.random() * H; b.vx = 0; b.vy = 0; b.ph = Math.random() * 6.283; b.up = 12 + Math.random() * 16; }
function draw() {
ctx.clearRect(0, 0, W, H);
for (var i = 0; i < bs.length; i++) {
var b = bs[i];
ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, 6.283); ctx.fillStyle = 'rgba(255,255,255,.42)'; ctx.fill();
ctx.lineWidth = 1.1; ctx.strokeStyle = 'rgba(10,10,10,.24)'; ctx.stroke();
ctx.beginPath(); ctx.arc(b.x, b.y, b.r * .74, 3.5, 4.6); ctx.strokeStyle = 'rgba(255,255,255,.95)'; ctx.lineWidth = 2; ctx.stroke();
ctx.beginPath(); ctx.arc(b.x, b.y, b.r * .86, .25, 1.35); ctx.strokeStyle = 'rgba(245,72,15,.6)'; ctx.lineWidth = 1.5; ctx.stroke();
}
for (var j = 0; j < pops.length; j++) {
var p = pops[j], k = p.a / .35, rr = p.r * (1 + k * .6);
ctx.strokeStyle = 'rgba(10,10,10,' + (.5 * (1 - k)).toFixed(3) + ')'; ctx.lineWidth = 1.2;
for (var q = 0; q < 8; q++) { var an = q * .785 + p.r; ctx.beginPath(); ctx.moveTo(p.x + Math.cos(an) * rr * .8, p.y + Math.sin(an) * rr * .8); ctx.lineTo(p.x + Math.cos(an) * rr, p.y + Math.sin(an) * rr); ctx.stroke(); }
}
}
fit(cv, ctx, function (a, b) {
W = a; H = b; var n = Math.max(8, Math.round(W * H / 6500)); bs = [];
for (var i = 0; i < n; i++) { var bb = {}; mk(bb); bs.push(bb); }
draw();
});
cv.addEventListener('pointermove', function (e) {
var r = cv.getBoundingClientRect(), x = e.clientX - r.left, y = e.clientY - r.top, tn = performance.now();
if (lx != null) { var d = Math.max(8, tn - lt) / 1000; pvx = pvx * .5 + (x - lx) / d * .5; pvy = pvy * .5 + (y - ly) / d * .5; }
lx = mx = x; ly = my = y; lt = tn;
});
cv.addEventListener('pointerleave', function () { mx = my = -999; lx = ly = null; pvx = pvy = 0; });
cv.addEventListener('pointerdown', function (e) {
var r = cv.getBoundingClientRect(), x = e.clientX - r.left, y = e.clientY - r.top;
for (var i = bs.length - 1; i >= 0; i--) { var b = bs[i]; if (Math.hypot(b.x - x, b.y - y) < b.r + 6) { pops.push({ x: b.x, y: b.y, r: b.r, a: 0 }); mk(b, true); if (reduce) draw(); break; } }
});
w.querySelector('.zpb__blaas').addEventListener('click', function () { if (reduce) return; for (var i = 0; i < bs.length; i++) { bs[i].vx += 140 + Math.random() * 120; bs[i].vy -= 30 + Math.random() * 40; } });
w.querySelector('.zpb__btn').addEventListener('click', function (e) { e.preventDefault(); });
loop(w, function (dt) {
if (reduce) return; t += dt; pvx *= Math.exp(-4 * dt); pvy *= Math.exp(-4 * dt);
for (var i = 0; i < bs.length; i++) {
var b = bs[i], d = Math.hypot(b.x - mx, b.y - my);
if (d < 120) { var f = (1 - d / 120) * 1.4 * dt; b.vx += pvx * f; b.vy += pvy * f; }
var k = Math.exp(-1.1 * dt); b.vx *= k; b.vy *= k;
b.x += (b.vx + Math.sin(t * 1.3 + b.ph) * 9) * dt; b.y += (b.vy - b.up) * dt;
if (b.y < -b.r - 4 || b.x < -b.r - 40 || b.x > W + b.r + 40) mk(b, true);
}
for (var j = pops.length - 1; j >= 0; j--) { pops[j].a += dt; if (pops[j].a > .35) pops.splice(j, 1); }
draw();
});
}
init(document.querySelector('.jouw-container'));
Meer uit achtergronden & webgl
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 →