← Blurr Motion form-physics-feedback-brutalist
Categorie forms Tier 2 Techniek #31 Deps matter-js

 

1. Mechanisme — kopieer 1-op-1, geen styling-keuzes
// Mechanisme: form-physics-feedback-brutalist
import Matter from 'https://esm.sh/matter-js@0.20.0';
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const form = document.querySelector('.physics-form');
const cv = document.querySelector('.physics-canvas');
const msg = document.querySelector('.physics-msg');
if(!form||!cv) return;
let engine, runner, render;
function startEngine(){
  const {Engine,Render,Runner,Bodies,Composite} = Matter;
  const W = cv.offsetWidth, H = cv.offsetHeight;
  cv.width = W; cv.height = H;
  engine = Engine.create();
  render = Render.create({canvas:cv, engine, options:{width:W,height:H,wireframes:false,background:'transparent'}});
  Composite.add(engine.world, [
    Bodies.rectangle(W/2,H+25,W,50,{isStatic:true}),
    Bodies.rectangle(-25,H/2,50,H,{isStatic:true}),
    Bodies.rectangle(W+25,H/2,50,H,{isStatic:true}),
  ]);
  Render.run(render);
  runner = Runner.create();
  Runner.run(runner, engine);
}
function spawn(color, count){
  if(!engine) return;
  const {Bodies,Composite} = Matter;
  const W = cv.offsetWidth;
  for(let i=0;i<count;i++){
    const x = 40 + Math.random()*(W-80);
    const size = 30 + Math.random()*40;
    Composite.add(engine.world, Bodies.rectangle(x,-50-i*60,size,size,{
      restitution:0.4, friction:0.1,
      render:{fillStyle:color, strokeStyle:'#0A0A0A', lineWidth:2}
    }));
  }
}
form.addEventListener('submit', e=>{
  e.preventDefault();
  const inputs = form.querySelectorAll('input,textarea');
  const empty = [...inputs].filter(i=>!i.value.trim());
  const valid = empty.length===0;
  if(reduce){
    msg.textContent = valid?'VERZONDEN':'VUL ALLES IN';
    msg.style.color = valid?'#0A0A0A':'#FF4A1C';
    msg.classList.add('show');
    return;
  }
  if(!engine) startEngine();
  if(valid){ spawn('#0A0A0A',8); msg.textContent='VERZONDEN'; msg.style.color='#0A0A0A'; }
  else { spawn('#FF4A1C',5); msg.textContent='VUL ALLES IN'; msg.style.color='#FF4A1C'; form.classList.add('shake'); setTimeout(()=>form.classList.remove('shake'),500); }
  msg.classList.add('show');
});
2. Skeleton — DOM + class-namen, mag herschikken
<!-- Skeleton: form-physics-feedback-brutalist -->
<div class="demo-block">
  <form class="physics-form">
    <label>Naam<input name="naam"/></label>
    <label>Email<input type="email" name="email"/></label>
    <label>Bericht<textarea name="bericht"></textarea></label>
    <button type="submit">Verstuur</button>
  </form>
  <canvas class="physics-canvas"></canvas>
  <p class="physics-msg"></p>
</div>
3. Styling-template — verplicht eigen invulling per merk
/* Styling: form-physics-feedback-brutalist */
:root {
  --block-bg: #F4F1EB;
  --block-fg: #0A0A0A;
  --block-accent: #FF4A1C;
}
.physics-form { border: 1px solid var(--block-fg); }
.physics-form label { font-family: 'Archivo', sans-serif; font-weight: 700; text-transform: uppercase; }
.physics-form button { background: var(--block-accent); color: var(--block-fg); }
.shake { animation: shake .4s cubic-bezier(.36,.07,.19,.97); }
@keyframes shake { 10%,90%{transform:translateX(-2px)} 30%,70%{transform:translateX(4px)} 50%{transform:translateX(-6px)} }