Sneeuwbol met winteractie New
A snow globe with a bike repair shop inside: drag the globe back and forth or use the button, the flakes react to the globe's acceleration, swirl and settle on the ground. After enough shaking the promo code appears. For a seasonal winter promotion, such as a tyre or service week at a bike shop or garage. With reduced motion the snow stays still and the button shows the code right away.
Winterklaar bij De Spaak
Fietsenmaker in Groningen. Schud voor je winteractie.
How to use it
- Paste the HTML where the components should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Sneeuwbol met winteractie · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="fys">
<div class="fys__staan"><div class="fys__bol" aria-hidden="true"><canvas class="fys__c"></canvas></div><span class="fys__voet" aria-hidden="true"></span></div>
<div class="fys__tekst">
<h3 class="fys__h">Winterklaar bij De Spaak</h3>
<p class="fys__p">Fietsenmaker in Groningen. Schud voor je winteractie.</p>
<button class="fys__knop" type="button">Schud de bol</button>
<p class="fys__code" aria-live="polite"></p>
</div>
</div>
</div>/* Sneeuwbol met winteractie · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.fys{position:absolute;inset:0;display:flex;align-items:center;gap:14px;padding:12px 16px;background:#f4f3f1;color:#0a0a0a;font-family:'Switzer',sans-serif;text-align:left;overflow:hidden}
.fys *{box-sizing:border-box}
.fys__staan{flex:0 0 auto;display:flex;flex-direction:column;align-items:center}
.fys__bol{position:relative;width:150px;height:150px;border-radius:50%;overflow:hidden;cursor:grab;touch-action:none;will-change:transform;box-shadow:inset 0 0 0 3px #0a0a0a}
.fys__bol:active{cursor:grabbing}
.fys__c{display:block;width:100%;height:100%}
.fys__voet{display:block;width:70%;height:22px;margin-top:-10px;background:#0a0a0a;clip-path:polygon(8% 0,92% 0,100% 100%,0 100%);border-radius:0 0 4px 4px}
.fys__tekst{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:6px}
.fys__h{margin:0;font:700 18px/1.15 'Switzer',sans-serif;letter-spacing:-.015em}
.fys__p{margin:0;font:400 13px/1.4 'Switzer',sans-serif;color:#3d3b37}
.fys__knop{margin-top:2px;padding:9px 13px;border:0;border-radius:4px;background:#0a0a0a;color:#fff;font:600 13px/1 'Switzer',sans-serif;cursor:pointer;transition:background-color .16s,color .16s}
.fys__knop:hover{background:#F5480F;color:#0a0a0a}
.fys__knop:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.fys__code{margin:0;min-height:18px;font:600 13px/1.35 'Switzer',sans-serif}// Sneeuwbol met winteractie · © 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 el=root.querySelector('.fys');if(!el)return;
var RM=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var bol=el.querySelector('.fys__bol'),cv=el.querySelector('.fys__c'),ctx=cv.getContext('2d'),knop=el.querySelector('.fys__knop'),code=el.querySelector('.fys__code');
var D=140,R=70,dpr=1,F=[],NF=90,gx=0,gy=0,gvx=0,gvy=0,pgvx=0,pgvy=0,drag=false,sx=0,sy=0,raf=0,last=0,vis=true,energie=0,getoond=false,schud=[],stil=0;
function grond(x){return R*.52-Math.cos(x/R*1.3)*R*.06;}
function maat(){var r=el.getBoundingClientRect();if(!r.width)return false;D=Math.round(Math.max(110,Math.min(r.height*.66,r.width*.38)));R=D/2;bol.style.width=bol.style.height=D+'px';
dpr=Math.min(2,window.devicePixelRatio||1);cv.width=D*dpr;cv.height=D*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);return true;}
function vlok(i){var a=Math.random()*Math.PI*2,rr=Math.sqrt(Math.random())*(R-6);return{x:Math.cos(a)*rr,y:Math.sin(a)*rr*.8-R*.15,vx:0,vy:0,r:.9+Math.random()*1.5,lig:false};}
function vul(){F=[];for(var i=0;i<NF;i++)F.push(vlok(i));if(RM)F.forEach(function(f,i){if(i%3){f.x=(Math.random()*2-1)*(R*.8);f.y=grond(f.x)-Math.random()*2;f.lig=true;}});}
function teken(){ctx.clearRect(0,0,D,D);ctx.save();ctx.translate(R,R);ctx.fillStyle='#16181a';ctx.beginPath();ctx.arc(0,0,R,0,Math.PI*2);ctx.fill();
ctx.fillStyle='#2b2e31';ctx.beginPath();ctx.moveTo(-R,R);for(var x=-R;x<=R;x+=4)ctx.lineTo(x,grond(x));ctx.lineTo(R,R);ctx.closePath();ctx.fill();
var hw=R*.5,hb=grond(0),hh=R*.36;ctx.fillStyle='#F5480F';ctx.beginPath();ctx.moveTo(-hw/2,hb);ctx.lineTo(-hw/2,hb-hh);ctx.lineTo(0,hb-hh-R*.22);ctx.lineTo(hw/2,hb-hh);ctx.lineTo(hw/2,hb);ctx.closePath();ctx.fill();
ctx.fillStyle='#0a0a0a';ctx.fillRect(-hw*.12,hb-hh*.55,hw*.24,hh*.55);ctx.fillStyle='#f4f3f1';ctx.fillRect(hw*.2,hb-hh*.78,hw*.16,hh*.22);ctx.fillRect(-hw*.36,hb-hh*.78,hw*.16,hh*.22);
ctx.strokeStyle='#f4f3f1';ctx.lineWidth=1.4;var wy=hb-5,wx=-R*.52;ctx.beginPath();ctx.arc(wx,wy,5,0,Math.PI*2);ctx.moveTo(wx+19,wy);ctx.arc(wx+14,wy,5,0,Math.PI*2);ctx.moveTo(wx,wy);ctx.lineTo(wx+7,wy-7);ctx.lineTo(wx+14,wy);ctx.stroke();
ctx.fillStyle='#fff';for(var i=0;i<F.length;i++){var f=F[i];ctx.globalAlpha=f.lig?.95:.85;ctx.beginPath();ctx.arc(f.x,f.y,f.r,0,Math.PI*2);ctx.fill();}ctx.globalAlpha=1;ctx.restore();}
function stap(dt){var ax=(gvx-pgvx)/dt,ay=(gvy-pgvy)/dt;pgvx=gvx;pgvy=gvy;var am=Math.hypot(ax,ay);
if(!drag){gvx+=(-gx*160-gvx*9)*dt;gvy+=(-gy*160-gvy*9)*dt;gx+=gvx*dt;gy+=gvy*dt;}
if(schud.length&&performance.now()>=schud[0].t){var s=schud.shift();gvx+=s.v;gvy+=s.v*.25;}
if(am>900)energie+=am*dt*.001;if(energie>2.2&&!getoond){getoond=true;toon();}
var lim=R-4,dr=Math.exp(-2.4*dt);
for(var i=0;i<F.length;i++){var f=F[i];
if(f.lig){if(am>1400&&Math.random()<.5){f.lig=false;f.vx=-ax*.03*(.5+Math.random());f.vy=-ay*.03*(.5+Math.random())-Math.random()*60;}else continue;}
f.vx+=(-ax*.016+Math.sin(f.y*.07+i)*6)*dt;f.vy+=(-ay*.016+22)*dt;f.vx*=dr;f.vy*=dr;f.x+=f.vx*dt;f.y+=f.vy*dt;
var d=Math.hypot(f.x,f.y);if(d>lim){var nx=f.x/d,ny=f.y/d;f.x=nx*lim;f.y=ny*lim;var vn=f.vx*nx+f.vy*ny;if(vn>0){f.vx-=1.3*vn*nx;f.vy-=1.3*vn*ny;}}
var gy2=grond(f.x)-f.r*.6;if(f.y>gy2){f.y=gy2;if(Math.abs(f.vx)+Math.abs(f.vy)<40){f.lig=true;f.vx=f.vy=0;}else{f.vy=-Math.abs(f.vy)*.2;}}}
bol.style.transform='translate('+gx.toFixed(1)+'px,'+gy.toFixed(1)+'px) rotate('+(gx*.35).toFixed(2)+'deg)';}
function tick(t){if(!vis){raf=0;return;}var dt=Math.min(.033,(t-last)/1000)||.016;last=t;stap(dt);teken();
var rust=!drag&&!schud.length&&Math.abs(gx)+Math.abs(gy)<.2&&F.every(function(f){return f.lig;});if(rust){if(++stil>30){raf=0;return;}}else stil=0;raf=requestAnimationFrame(tick);}
function kick(){if(RM||raf)return;stil=0;last=performance.now();raf=requestAnimationFrame(tick);}
function toon(){code.textContent='Code winter15: 15% op winterbanden, t/m 31 januari.';}
bol.addEventListener('pointerdown',function(e){if(RM||e.button>0)return;drag=true;sx=e.clientX-gx;sy=e.clientY-gy;try{bol.setPointerCapture(e.pointerId);}catch(_){}kick();});
bol.addEventListener('pointermove',function(e){if(!drag)return;var nx=Math.max(-22,Math.min(22,e.clientX-sx)),ny=Math.max(-14,Math.min(14,e.clientY-sy)),dt=1/60;gvx=(nx-gx)/dt;gvy=(ny-gy)/dt;gx=nx;gy=ny;});
function los(){drag=false;}bol.addEventListener('pointerup',los);bol.addEventListener('pointercancel',los);
knop.addEventListener('click',function(){if(RM){toon();return;}var t=performance.now();schud=[];[0,130,260,390,520].forEach(function(d,i){schud.push({t:t+d,v:(i%2?-1:1)*900});});kick();});
if(maat()){vul();teken();kick();}
if('ResizeObserver' in window)new ResizeObserver(function(){var o=D;if(maat()&&o!==D){vul();teken();}}).observe(el);
if('IntersectionObserver' in window)new IntersectionObserver(function(es){vis=es[0].isIntersecting;if(vis)kick();}).observe(el);
}
init(document.querySelector('.jouw-container'));
More components
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →