360 graden draaien New
A product you drag around through a sequence of 36 shots (every 10 degrees), as webshops with a turntable photo set do. All frames sit in one sprite (6 x 6), so one request and instant switching. Drag with mouse or finger with momentum, arrow keys per 10 degrees, Home for the front, and a button for auto rotation (never starts by itself, off with reduced motion).
Mok Lekstroom
Steengoed, 0,3 liter
€ 18,50
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.
<!-- 360 graden draaien · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="d36"><div class="d36__in">
<div class="d36__view" tabindex="0" role="slider" aria-label="Mok Lekstroom, sleep of gebruik de pijltjes om te draaien" aria-valuemin="0" aria-valuemax="350" aria-valuenow="0" aria-valuetext="Voorkant, 0 graden">
<div class="d36__img" role="img" aria-label="Witte mok met oranje band en handvat"></div>
<span class="d36__hint" aria-hidden="true"><svg viewBox="0 0 40 16"><path d="M6 8h28M10 4L6 8l4 4M30 4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>Sleep om te draaien</span>
</div>
<div class="d36__info">
<b class="d36__ti">Mok Lekstroom</b>
<span class="d36__me">Steengoed, 0,3 liter</span>
<span class="d36__pr">€ 18,50</span>
<div class="d36__deg" aria-hidden="true"><i class="d36__dot"></i></div>
<button class="d36__auto" type="button" aria-pressed="false">Laat draaien</button>
</div>
</div></div>
</div>/* 360 graden draaien · NedDev · eigen werk. */
.d36{position:absolute;inset:0;background:#f4f3f1;color:#0a0a0a;font-family:'Switzer',sans-serif;text-align:left}
.d36__view{position:relative;display:grid;place-items:center;cursor:grab;touch-action:pan-y;outline:none;user-select:none;-webkit-user-select:none}
.d36__view:active{cursor:grabbing}
.d36__view:focus-visible{box-shadow:inset 0 0 0 2px #F5480F}
.d36__img{width:min(96%,calc(100cqh * 1.2));aspect-ratio:360/300;background:url("https://neddev.com/lab-media/draai-360-1.webp") 0 0/600% 600% no-repeat}
.d36{container-type:size}
.d36__in{position:absolute;inset:0;display:grid;grid-template-columns:1fr 132px}
.d36__hint{position:absolute;left:50%;bottom:10px;transform:translateX(-50%);display:flex;align-items:center;gap:6px;font:500 12px/1 'Switzer',sans-serif;color:#55524d;white-space:nowrap;transition:opacity .4s}
.d36__hint svg{width:30px;height:12px}
.d36.is-used .d36__hint{opacity:0}
.d36__info{display:flex;flex-direction:column;justify-content:center;gap:6px;padding:14px 14px 14px 0}
.d36__ti{font:600 16px/1.2 'Switzer',sans-serif;letter-spacing:-.01em}
.d36__me{font:400 13px/1.4 'Switzer',sans-serif;color:#55524d}
.d36__pr{font:600 14px/1.2 'Switzer',sans-serif;font-variant-numeric:tabular-nums}
.d36__deg{position:relative;width:40px;height:40px;margin:6px 0 4px;border:1.5px solid #0a0a0a;border-radius:50%}
.d36__dot{position:absolute;left:50%;top:50%;width:8px;height:8px;margin:-4px 0 0 -4px;border-radius:50%;background:#F5480F;transform:rotate(var(--a,0deg)) translateY(17px)}
.d36__auto{align-self:flex-start;min-height:32px;padding:0 12px;border:1.5px solid #0a0a0a;border-radius:4px;background:#fff;color:#0a0a0a;font:500 13px/1 'Switzer',sans-serif;cursor:pointer}
.d36__auto[aria-pressed="true"]{background:#0a0a0a;color:#fff}
.d36__auto:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
@container (max-width:360px){.d36__in{grid-template-columns:1fr 118px}.d36__info{padding-right:10px}.d36__ti{font-size:15px}}
@media (prefers-reduced-motion:reduce){.d36__hint{transition:none}.d36__auto{display:none}}// 360 graden draaien · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.d36');if(!w)return;
var view=w.querySelector('.d36__view'),img=w.querySelector('.d36__img'),dot=w.querySelector('.d36__dot'),auto=w.querySelector('.d36__auto');
var N=36,ang=0,vel=0,drag=false,lastX=0,lastT=0,raf=0,spin=false,rm=matchMedia('(prefers-reduced-motion: reduce)').matches;
var names=['Voorkant','Rechterkant','Achterkant','Linkerkant'];
function draw(){var a=((ang%360)+360)%360,fr=Math.round(a/10)%N,c=fr%6,r=Math.floor(fr/6);
img.style.backgroundPosition=(c*20)+'% '+(r*20)+'%';dot.style.setProperty('--a',(fr*10)+'deg');
var deg=fr*10;view.setAttribute('aria-valuenow',deg);view.setAttribute('aria-valuetext',names[Math.round(deg/90)%4]+', '+deg+' graden');}
function loop(t){raf=0;if(spin){ang+=0.6;}else if(!drag&&Math.abs(vel)>.05){ang+=vel;vel*=.93;}else if(!drag){vel=0;return draw();}
draw();raf=requestAnimationFrame(loop);}
function go(){if(!raf)raf=requestAnimationFrame(loop);}
function stopSpin(){if(spin){spin=false;auto.setAttribute('aria-pressed','false');auto.textContent='Laat draaien';}}
view.addEventListener('pointerdown',function(e){drag=true;stopSpin();w.classList.add('is-used');lastX=e.clientX;lastT=performance.now();vel=0;try{view.setPointerCapture(e.pointerId);}catch(_){}});
view.addEventListener('pointermove',function(e){if(!drag)return;var dx=e.clientX-lastX,now=performance.now();ang-=dx*0.9;vel=rm?0:-dx*0.9*Math.min(1,16/Math.max(1,now-lastT));lastX=e.clientX;lastT=now;draw();});
function up(){if(!drag)return;drag=false;if(!rm)go();}
view.addEventListener('pointerup',up);view.addEventListener('pointercancel',up);
view.addEventListener('keydown',function(e){var k=e.key;if(k==='ArrowRight'||k==='ArrowUp')ang+=10;else if(k==='ArrowLeft'||k==='ArrowDown')ang-=10;else if(k==='Home')ang=0;else if(k==='End')ang=180;else return;e.preventDefault();stopSpin();vel=0;w.classList.add('is-used');ang=Math.round(ang/10)*10;draw();});
auto.addEventListener('click',function(){spin=!spin;auto.setAttribute('aria-pressed',spin?'true':'false');auto.textContent=spin?'Stop draaien':'Laat draaien';if(spin){w.classList.add('is-used');go();}});
new IntersectionObserver(function(es){es.forEach(function(e){if(!e.isIntersecting)stopSpin();});}).observe(w);
draw();
}
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 →