Proefmuur met verfstreek Nieuw
Een foto van een lege woonkamer waarop de cursor of je vinger een brede kwaststreek in de gekozen kleur over de muur trekt; de verf ligt in de schaduw van de muur (multiply) en vervaagt na een paar tellen weer. Kies een kleur met de stalen (pijltjes werken ook) en "Hele muur" zet de kleur vast op de hele wand. Voor de hero van een schildersbedrijf of de kleurenpagina van een verfwinkel.
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.
<!-- Proefmuur met verfstreek · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="vpm">
<div class="vpm__kader"><div class="vpm__beeld">
<img class="vpm__foto" src="https://neddev.com/lab-media/verf-proefmuur-1.webp" alt="Lege woonkamer met een witte muur, een ladder en een verfbak" width="960" height="597" loading="lazy" draggable="false">
<div class="vpm__muur" aria-hidden="true"></div>
<canvas class="vpm__doek" aria-hidden="true"></canvas>
</div></div>
<div class="vpm__balk">
<div class="vpm__stalen" role="radiogroup" aria-label="Kies een verfkleur">
<button type="button" class="vpm__staal" role="radio" aria-checked="true" aria-label="Saliegroen" data-kleur="#8fa38a" data-naam="Saliegroen" style="--k:#8fa38a"></button>
<button type="button" class="vpm__staal" role="radio" aria-checked="false" tabindex="-1" aria-label="Okergeel" data-kleur="#d9a441" data-naam="Okergeel" style="--k:#d9a441"></button>
<button type="button" class="vpm__staal" role="radio" aria-checked="false" tabindex="-1" aria-label="Leisteen" data-kleur="#6b6f73" data-naam="Leisteen" style="--k:#6b6f73"></button>
<button type="button" class="vpm__staal" role="radio" aria-checked="false" tabindex="-1" aria-label="Oranjerood" data-kleur="#F5480F" data-naam="Oranjerood" style="--k:#F5480F"></button>
</div>
<span class="vpm__naam" aria-hidden="true">Saliegroen</span>
<button type="button" class="vpm__hele" aria-pressed="false">Hele muur</button>
</div>
</div>
</div>/* Proefmuur met verfstreek · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.vpm{position:relative;width:100%;height:100%;overflow:hidden;background:#fff;font-family:Switzer,sans-serif;display:flex;flex-direction:column}
.vpm__kader{position:relative;flex:1 1 auto;min-height:0;overflow:hidden;background:#f4f3f1}
.vpm__beeld{position:absolute;left:0;top:50%;width:100%;aspect-ratio:960/597;transform:translateY(-50%)}
.vpm__foto{position:absolute;inset:0;width:100%;height:100%;user-select:none;-webkit-user-drag:none}
.vpm__muur,.vpm__doek{position:absolute;inset:0;width:100%;height:100%;mix-blend-mode:multiply;clip-path:polygon(22.6% 18.6%,88.4% 1.6%,88.4% 71.2%,22.6% 63.6%)}
.vpm__muur{background:var(--muur,transparent);opacity:0;transition:opacity .6s ease,background-color .4s}
.vpm.is-vol .vpm__muur{opacity:.9}
.vpm__doek{touch-action:pan-y;cursor:crosshair}
.vpm__balk{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:7px 12px;background:#fff;border-top:1px solid #e4e0d9}
.vpm__hele{margin-left:auto}
.vpm__stalen{display:flex;gap:5px}
.vpm__staal{width:26px;height:26px;padding:0;border:2px solid #fff;border-radius:4px;background:var(--k);box-shadow:0 0 0 1px #c9c6c0;cursor:pointer;transition:transform .15s}
.vpm__staal[aria-checked="true"]{box-shadow:0 0 0 2px #0a0a0a;transform:translateY(-2px)}
.vpm__staal:focus-visible,.vpm__hele:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.vpm__naam{font-size:13px;color:#0a0a0a;min-width:72px}
.vpm__hele{font:inherit;font-size:13px;min-height:28px;padding:0 10px;border:1px solid #0a0a0a;border-radius:4px;background:#0a0a0a;color:#fff;cursor:pointer;transition:background-color .15s}
.vpm__hele:hover{background:#F5480F;border-color:#F5480F}
@media (max-width:420px){.vpm__naam{display:none}.vpm__balk{gap:6px}}
@media (prefers-reduced-motion:reduce){.vpm__muur,.vpm__staal{transition:none}}// Proefmuur met verfstreek · © 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('.vpm'); if (!w) return;
const cv = w.querySelector('.vpm__doek'), ctx = cv.getContext('2d');
const stalen = [...w.querySelectorAll('.vpm__staal')], naam = w.querySelector('.vpm__naam'), hele = w.querySelector('.vpm__hele');
const rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
let kleur = stalen[0].dataset.kleur, last = null, raf = 0, rust = 0, dpr = 1, W = 0, H = 0, gedemod = false;
function maat(){
const r = cv.getBoundingClientRect(); if (!r.width) return;
dpr = Math.min(2, window.devicePixelRatio || 1); W = r.width; H = r.height;
cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function streek(x, y){
const a = last || { x, y }; last = { x, y };
const s = Math.max(0.8, Math.min(W, H * 1.6) / 400);
ctx.globalCompositeOperation = 'source-over'; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = kleur;
ctx.globalAlpha = 0.8; ctx.lineWidth = 30 * s; ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(x, y); ctx.stroke();
ctx.globalAlpha = 0.35; ctx.lineWidth = 4 * s;
for (let i = -2; i <= 2; i++){ const o = i * 6 * s; ctx.beginPath(); ctx.moveTo(a.x, a.y + o); ctx.lineTo(x, y + o + (Math.random() - .5) * 2); ctx.stroke(); }
ctx.globalAlpha = 1; rust = 0; start();
}
function vervaag(){
raf = 0; if (rm) return;
ctx.globalCompositeOperation = 'destination-out'; ctx.fillStyle = 'rgba(0,0,0,.018)'; ctx.fillRect(0, 0, W, H);
ctx.globalCompositeOperation = 'source-over';
if (++rust < 420) raf = requestAnimationFrame(vervaag); else ctx.clearRect(0, 0, W, H);
}
function start(){ if (!rm && !raf) raf = requestAnimationFrame(vervaag); }
const pos = e => { const r = cv.getBoundingClientRect(); return { x: e.clientX - r.left, y: e.clientY - r.top }; };
let down = false;
cv.addEventListener('pointerdown', e => { down = true; last = null; const p = pos(e); streek(p.x, p.y); });
cv.addEventListener('pointermove', e => { if (e.pointerType !== 'mouse' && !down) return; const p = pos(e); streek(p.x, p.y); });
cv.addEventListener('pointerleave', () => { last = null; });
const stop = () => { down = false; last = null; };
cv.addEventListener('pointerup', stop); cv.addEventListener('pointercancel', stop);
function kies(i, focus){
stalen.forEach((s, j) => { s.setAttribute('aria-checked', String(i === j)); s.tabIndex = i === j ? 0 : -1; });
kleur = stalen[i].dataset.kleur; naam.textContent = stalen[i].dataset.naam;
if (w.classList.contains('is-vol')) w.style.setProperty('--muur', kleur);
if (focus) stalen[i].focus();
}
stalen.forEach((s, i) => {
s.addEventListener('click', () => kies(i));
s.addEventListener('keydown', e => {
const d = { ArrowRight:1, ArrowDown:1, ArrowLeft:-1, ArrowUp:-1 }[e.key]; if (!d) return;
e.preventDefault(); kies((i + d + stalen.length) % stalen.length, true);
});
});
hele.addEventListener('click', () => {
const op = !w.classList.contains('is-vol'); w.style.setProperty('--muur', kleur);
w.classList.toggle('is-vol', op); hele.setAttribute('aria-pressed', String(op)); hele.textContent = op ? 'Muur wit' : 'Hele muur';
});
function demo(){
if (gedemod || rm || !W) return; gedemod = true; last = null; const t0 = performance.now();
(function stap(t){
const k = Math.min(1, (t - t0) / 1100);
streek(W * (0.3 + 0.5 * k), H * (0.36 + 0.1 * Math.sin(k * Math.PI * 2.4)));
if (k < 1) requestAnimationFrame(stap); else last = null;
})(t0);
}
maat();
if ('ResizeObserver' in window) new ResizeObserver(maat).observe(cv);
if ('IntersectionObserver' in window) new IntersectionObserver(es => { if (es[0].isIntersecting) setTimeout(demo, 400); }, { threshold: 0.5 }).observe(w);
}
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 →