Zwengel die door de jaren draait Nieuw
Twee tandwielen met een zwengel: draai met muis of vinger rond en de jaartallen van het bedrijf lopen mee, van de eerste draaibank tot nu; laat je los, dan draaien de wielen nog even door. Het tandwiel is een schuifregelaar, dus met de pijltjes (of Home en End) draai je ook. Voor de over-ons-pagina van een machinefabriek, smederij, horlogemaker of familiebedrijf met een lange geschiedenis.
1964
Sjoerd Hoekstra begint met twee draaibanken in een schuur in Leeuwarden.
Draai aan de zwengel of gebruik de pijltjes.
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.
<!-- Zwengel die door de jaren draait · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="zwg">
<svg class="zwg__wielen" viewBox="14 20 168 130" tabindex="0" role="slider" aria-label="Zwengel: draai door de geschiedenis" aria-valuemin="1964" aria-valuemax="2024" aria-valuenow="1964">
<g class="zwg__groot"><path class="zwg__tand-g" fill="#0a0a0a"/><circle cx="0" cy="0" r="9" fill="#f4f3f1"/><g class="zwg__gaten" fill="#f4f3f1"></g>
<rect x="-3" y="-38" width="6" height="38" rx="3" fill="#8a857e"/><circle class="zwg__knop" cx="0" cy="-38" r="8" fill="#F5480F"/></g>
<g class="zwg__klein"><path class="zwg__tand-k" fill="#F5480F"/><circle cx="0" cy="0" r="6" fill="#f4f3f1"/></g>
</svg>
<div class="zwg__info">
<p class="zwg__jaar">1964</p>
<p class="zwg__mijl" aria-live="polite">Sjoerd Hoekstra begint met twee draaibanken in een schuur in Leeuwarden.</p>
<p class="zwg__hint">Draai aan de zwengel of gebruik de pijltjes.</p>
</div>
</div>
</div>/* Zwengel die door de jaren draait · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.zwg{width:100%;height:100%;display:flex;align-items:center;justify-content:center;gap:14px;padding:12px 18px;box-sizing:border-box;background:#f4f3f1;font-family:Switzer,sans-serif}
.zwg__wielen{flex:0 0 auto;width:min(50%,240px);height:auto;cursor:grab;touch-action:none;outline:none;border-radius:8px}
.zwg__wielen:active{cursor:grabbing}
.zwg__wielen:focus-visible{box-shadow:0 0 0 2px #F5480F}
.zwg__info{flex:1 1 auto;min-width:0;max-width:200px}
.zwg__jaar{margin:0;font-size:40px;font-weight:700;line-height:1;color:#0a0a0a;font-variant-numeric:tabular-nums}
.zwg__mijl{margin:8px 0 0;font-size:13px;line-height:1.4;color:#0a0a0a;min-height:5.6em}
.zwg__mijl.is-nieuw{animation:zwg-in .35s ease-out}
@keyframes zwg-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.zwg__hint{margin:6px 0 0;font-size:12px;color:#55524d}
@media (max-width:420px){.zwg{gap:10px;padding:10px 12px}.zwg__jaar{font-size:32px}.zwg__mijl{font-size:12px}}
@media (prefers-reduced-motion:reduce){.zwg__mijl.is-nieuw{animation:none}}// Zwengel die door de jaren draait · © 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('.zwg'); if (!w) return;
const svg = w.querySelector('.zwg__wielen'), groot = w.querySelector('.zwg__groot'), klein = w.querySelector('.zwg__klein');
const jaar = w.querySelector('.zwg__jaar'), mijl = w.querySelector('.zwg__mijl');
const rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
const G = { x: 76, y: 88, r: 50, n: 18 }, K = { r: 30, n: 11 }, D = 7;
const hoek = -28 * Math.PI / 180, afst = G.r + K.r - D;
K.x = G.x + Math.cos(hoek) * afst; K.y = G.y + Math.sin(hoek) * afst;
function tanden(n, r){
const T = Math.PI * 2 / n; let s = '';
for (let i = 0; i < n; i++){
const a = i * T;
[[r - D, a], [r, a + 0.2 * T], [r, a + 0.5 * T], [r - D, a + 0.7 * T]].forEach((q, j) => {
s += (i === 0 && j === 0 ? 'M' : 'L') + (Math.cos(q[1]) * q[0]).toFixed(2) + ' ' + (Math.sin(q[1]) * q[0]).toFixed(2) + ' ';
});
}
return s + 'Z';
}
w.querySelector('.zwg__tand-g').setAttribute('d', tanden(G.n, G.r));
w.querySelector('.zwg__tand-k').setAttribute('d', tanden(K.n, K.r));
const gaten = w.querySelector('.zwg__gaten');
for (let i = 0; i < 5; i++){ const a = i * Math.PI * 2 / 5 + 0.6, c = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); c.setAttribute('cx', (Math.cos(a) * 25).toFixed(2)); c.setAttribute('cy', (Math.sin(a) * 25).toFixed(2)); c.setAttribute('r', '7'); gaten.appendChild(c); }
const MIJL = [
[1964, 'Sjoerd Hoekstra begint met twee draaibanken in een schuur in Leeuwarden.'],
[1978, 'Eerste eigen hal aan de Hemrik, met vijf man in de werkplaats.'],
[1992, 'De eerste CNC-freesbank komt binnen; zoon Rients neemt de planning over.'],
[2008, 'Onderdelen voor melkrobots, inmiddels een derde van al het werk.'],
[2024, 'Machinefabriek Hoekstra: 62 vakmensen, drie generaties, nog altijd in Leeuwarden.']
];
const MAX = Math.PI * 2 * 4;
let tot = 0, vel = 0, raf = 0, sleep = null, huidig = 0;
function teken(){
groot.setAttribute('transform', 'translate(' + G.x + ' ' + G.y + ') rotate(' + (tot * 180 / Math.PI).toFixed(2) + ')');
klein.setAttribute('transform', 'translate(' + K.x.toFixed(2) + ' ' + K.y.toFixed(2) + ') rotate(' + (-tot * G.n / K.n * 180 / Math.PI + 180 / K.n + 10).toFixed(2) + ')');
const j = Math.round(1964 + 60 * tot / MAX); jaar.textContent = j; svg.setAttribute('aria-valuenow', String(j));
let m = 0; MIJL.forEach((q, i) => { if (j >= q[0]) m = i; });
svg.setAttribute('aria-valuetext', j + ': ' + MIJL[m][1]);
if (m !== huidig) { huidig = m; mijl.textContent = MIJL[m][1]; mijl.classList.remove('is-nieuw'); void mijl.offsetWidth; mijl.classList.add('is-nieuw'); }
}
const zet = t => { tot = Math.max(0, Math.min(MAX, t)); teken(); };
function hoekVan(e){ const r = svg.getBoundingClientRect(), s = r.width / 168; return Math.atan2(e.clientY - (r.top + (G.y - 20) * s), e.clientX - (r.left + (G.x - 14) * s)); }
function rol(){
raf = 0; if (sleep || rm) return;
vel *= 0.94; zet(tot + vel);
if (Math.abs(vel) > 0.002 && tot > 0 && tot < MAX) raf = requestAnimationFrame(rol);
}
svg.addEventListener('pointerdown', e => {
if (e.button > 0) return; e.preventDefault(); cancelAnimationFrame(raf); raf = 0;
sleep = { id: e.pointerId, a: hoekVan(e) }; vel = 0; svg.setPointerCapture(e.pointerId);
});
svg.addEventListener('pointermove', e => {
if (!sleep || e.pointerId !== sleep.id) return;
const a = hoekVan(e); let d = a - sleep.a; if (d > Math.PI) d -= Math.PI * 2; if (d < -Math.PI) d += Math.PI * 2;
sleep.a = a; vel = vel * 0.5 + d * 0.5; zet(tot + d);
});
const eind = () => { if (!sleep) return; sleep = null; vel = Math.max(-0.3, Math.min(0.3, vel)); if (!raf) raf = requestAnimationFrame(rol); };
svg.addEventListener('pointerup', eind); svg.addEventListener('pointercancel', eind);
svg.addEventListener('keydown', e => {
const stap = { ArrowRight: 0.26, ArrowUp: 0.26, ArrowLeft: -0.26, ArrowDown: -0.26, PageUp: Math.PI / 2, PageDown: -Math.PI / 2 }[e.key];
if (stap !== undefined) { e.preventDefault(); zet(tot + stap); }
else if (e.key === 'Home') { e.preventDefault(); zet(0); }
else if (e.key === 'End') { e.preventDefault(); zet(MAX); }
});
teken();
}
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 →