Draaiknop voor de thermostaat Nieuw
Draaiknop zoals op een thermostaat: sleep rond de knop of gebruik pijltjes, Page Up/Down, Home en End; de boog vult mee en de stand staat in halve graden. Met snelkeuzes Thuis, Weg en Nacht en een statusregel die vertelt wat de ketel gaat doen. Gebouwd als role=slider met aria-valuetext, dus ook met schermlezer bruikbaar. Voor installateurs, energie-apps en slimme-huisdemo's; met andere schaal ook voor budget of volume.
Woonkamer, nu 18,9 °C
Verwarmen tot 20,5 °C, klaar rond 19:40
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.
<!-- Draaiknop voor de thermostaat · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="dkt"><div class="dkt__in">
<div class="dkt__dial">
<svg class="dkt__svg" viewBox="0 0 160 160" aria-hidden="true"><path class="dkt__trk" d="M33.9 126.1A65 65 0 1 1 126.1 126.1"/><path class="dkt__arc" d="M33.9 126.1A65 65 0 1 1 126.1 126.1"/><g class="dkt__ticks"></g></svg>
<div class="dkt__knob" role="slider" tabindex="0" aria-label="Gewenste temperatuur woonkamer" aria-valuemin="12" aria-valuemax="28" aria-valuenow="20.5" aria-valuetext="20,5 graden">
<span class="dkt__ind" aria-hidden="true"></span>
<span class="dkt__val" aria-hidden="true"><b>20,5</b><small>°C</small></span>
</div>
</div>
<div class="dkt__side">
<p class="dkt__room">Woonkamer, nu 18,9 °C</p>
<div class="dkt__pre" role="group" aria-label="Snelkeuze">
<button type="button" data-v="20.5" aria-pressed="true">Thuis</button><button type="button" data-v="17" aria-pressed="false">Weg</button><button type="button" data-v="16" aria-pressed="false">Nacht</button>
</div>
<p class="dkt__st" aria-live="polite">Verwarmen tot 20,5 °C, klaar rond 19:40</p>
</div>
</div></div>
</div>/* Draaiknop voor de thermostaat · NedDev · eigen werk. */
.dkt{position:absolute;inset:0;overflow:auto;display:flex;padding:12px;box-sizing:border-box;background:#f4f3f1;font-family:'Switzer',sans-serif;color:#0a0a0a}
.dkt *,.dkt *::before,.dkt *::after{box-sizing:border-box}
.dkt__in{margin:auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap;justify-content:center}
.dkt__dial{position:relative;width:170px;height:170px;flex:none;touch-action:none}
.dkt__svg{position:absolute;inset:0;width:100%;height:100%}
.dkt__trk,.dkt__arc{fill:none;stroke-width:8;stroke-linecap:round}
.dkt__trk{stroke:#dedbd5}
.dkt__arc{stroke:#F5480F;transition:stroke-dashoffset .12s linear}
.dkt__ticks line{stroke:#a3a09a;stroke-width:1.2}
.dkt__knob{position:absolute;left:30px;top:30px;width:110px;height:110px;border-radius:50%;background:#fff;box-shadow:0 1px 0 rgba(10,10,10,.06),0 6px 16px rgba(10,10,10,.12),inset 0 0 0 1px rgba(10,10,10,.1);cursor:grab;outline:none;display:grid;place-items:center}
.dkt__knob:active{cursor:grabbing}
.dkt__knob:focus-visible{box-shadow:0 0 0 3px #F5480F,0 6px 16px rgba(10,10,10,.12)}
.dkt__ind{position:absolute;inset:0;transform:rotate(var(--a,0deg));transition:transform .12s linear}
.dkt__ind::before{content:'';position:absolute;left:50%;top:8px;width:3px;height:14px;margin-left:-1.5px;background:#0a0a0a;border-radius:2px}
.dkt__val{display:flex;align-items:flex-start;gap:2px;pointer-events:none}
.dkt__val b{font-size:28px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.dkt__val small{font-size:13px;font-weight:600;margin-top:4px}
.dkt__side{max-width:170px}
.dkt__room{margin:0 0 8px;font-size:13px;color:#4a4a4a}
.dkt__pre{display:flex;gap:4px}
.dkt__pre button{height:32px;padding:0 10px;border:1px solid rgba(10,10,10,.25);border-radius:4px;background:#fff;font:600 13px 'Switzer',sans-serif;color:#0a0a0a;cursor:pointer;transition:border-color .15s,background-color .15s}
.dkt__pre button:hover{border-color:#0a0a0a}
.dkt__pre button[aria-pressed="true"]{background:#0a0a0a;border-color:#0a0a0a;color:#fff}
.dkt__pre button:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.dkt__st{margin:10px 0 0;font-size:13px;line-height:1.4}
@media (max-width:420px){.dkt__dial{width:124px;height:124px}.dkt__knob{left:22px;top:22px;width:80px;height:80px}.dkt__ind::before{top:6px;height:11px}.dkt__val b{font-size:21px}.dkt__in{gap:12px;flex-wrap:nowrap}.dkt__side{max-width:none;min-width:0}.dkt__pre button{padding:0 8px}}
@media (prefers-reduced-motion:reduce){.dkt *{transition:none!important}}// Draaiknop voor de thermostaat · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.dkt');if(!w)return;
var knob=w.querySelector('.dkt__knob'),ind=w.querySelector('.dkt__ind'),arc=w.querySelector('.dkt__arc'),valB=w.querySelector('.dkt__val b'),st=w.querySelector('.dkt__st'),dial=w.querySelector('.dkt__dial'),tk=w.querySelector('.dkt__ticks');
var MIN=12,MAX=28,STEP=.5,SW=270,v=20.5,NOW=18.9,tm;
var L=arc.getTotalLength?arc.getTotalLength():400;arc.style.strokeDasharray=L;
for(var i=0;i<=16;i++){var a=(-135+i*SW/16)*Math.PI/180,r1=76,r2=i%4===0?70:73,ln=document.createElementNS('http://www.w3.org/2000/svg','line');
ln.setAttribute('x1',80+r1*Math.sin(a));ln.setAttribute('y1',80-r1*Math.cos(a));ln.setAttribute('x2',80+r2*Math.sin(a));ln.setAttribute('y2',80-r2*Math.cos(a));tk.appendChild(ln);}
function nl(x){return String(x).replace('.',',');}
function set(nv,src){nv=Math.round(Math.max(MIN,Math.min(MAX,nv))/STEP)*STEP;if(nv===v&&src!=='init')return;v=nv;
var f=(v-MIN)/(MAX-MIN);ind.style.setProperty('--a',(-135+f*SW)+'deg');arc.style.strokeDashoffset=L*(1-f);
valB.textContent=nl(v.toFixed(1));knob.setAttribute('aria-valuenow',v);knob.setAttribute('aria-valuetext',nl(v.toFixed(1))+' graden');
w.querySelectorAll('.dkt__pre button').forEach(function(b){b.setAttribute('aria-pressed',String(+b.getAttribute('data-v')===v));});
clearTimeout(tm);tm=setTimeout(function(){var d=v-NOW;
if(d>.2){var m=Math.round(d*25),t=new Date(2026,9,9,19,15+m);st.textContent='Verwarmen tot '+nl(v.toFixed(1))+' \u00b0C, klaar rond '+t.getHours()+':'+String(t.getMinutes()).padStart(2,'0');}
else st.textContent='Ketel uit, het is warm genoeg ('+nl(NOW)+' \u00b0C)';},src==='init'?0:350);}
knob.addEventListener('keydown',function(e){var k=e.key,n=null;
if(k==='ArrowUp'||k==='ArrowRight')n=v+STEP;else if(k==='ArrowDown'||k==='ArrowLeft')n=v-STEP;else if(k==='PageUp')n=v+2;else if(k==='PageDown')n=v-2;else if(k==='Home')n=MIN;else if(k==='End')n=MAX;
if(n!==null){e.preventDefault();set(n);}});
var drag=false;
function fromPt(e){var r=dial.getBoundingClientRect(),x=e.clientX-r.left-r.width/2,y=e.clientY-r.top-r.height/2,ang=Math.atan2(x,-y)*180/Math.PI;
if(ang<-135&&ang>=-180)ang=-135;if(ang>135)ang=135;set(MIN+(ang+135)/SW*(MAX-MIN));}
dial.addEventListener('pointerdown',function(e){drag=true;try{dial.setPointerCapture(e.pointerId);}catch(_){}knob.focus();fromPt(e);e.preventDefault();});
dial.addEventListener('pointermove',function(e){if(drag)fromPt(e);});
dial.addEventListener('pointerup',function(){drag=false;});dial.addEventListener('pointercancel',function(){drag=false;});
w.querySelectorAll('.dkt__pre button').forEach(function(b){b.addEventListener('click',function(){set(+b.getAttribute('data-v'));});});
set(v,'init');
}
init(document.querySelector('.jouw-container'));
Meer uit formulieren & invoer
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 →