PAD + React

Click handler

An event handler that reads where you clicked and leaves a ripple there.

onClickuseRef
Live

How it is built

PAD, on the server React, in the browser

<div id="example1"></div>

<script type="text/babel" src="{$padRoot}react/examples/click.js"></script>
// An event handler reads the click - where it landed in the element, through a ref - and
// leaves a ripple there. Each ripple is an entry of state that removes itself when its
// animation is over.

function ClickExample() {
  const area = React.useRef(null);
  const [ripples, setRipples] = React.useState([]);
  const [clicks, setClicks] = React.useState(0);

  const click = event => {
    const box = area.current.getBoundingClientRect();
    const ripple = { id: Date.now() + Math.random(), x: event.clientX - box.left, y: event.clientY - box.top, hue: Math.round(Math.random() * 360) };
    setRipples(now => [...now, ripple]);
    setClicks(clicks + 1);
  };

  const gone = id => setRipples(now => now.filter(ripple => ripple.id !== id));

  return (
    <div>
      <div ref={area} onClick={click} role="button" tabIndex={0} aria-label="Click anywhere"
           style={{ position: 'relative', height: 240, borderRadius: 16, overflow: 'hidden', cursor: 'crosshair', userSelect: 'none',
                    display: 'grid', placeItems: 'center', background: 'linear-gradient(135deg, var(--pad-soft), var(--react-soft))', border: '1px solid var(--line)' }}>
        <div style={{ textAlign: 'center', pointerEvents: 'none' }}>
          <div key={clicks} className="pop" style={{ fontSize: 56, fontWeight: 900 }}>{clicks}</div>
          <div className="muted">{clicks ? 'clicks - keep going' : 'click anywhere in here'}</div>
        </div>
        {ripples.map(ripple => (
          <span key={ripple.id} onAnimationEnd={() => gone(ripple.id)}
                style={{ position: 'absolute', left: ripple.x, top: ripple.y, width: 260, height: 260, borderRadius: '50%', pointerEvents: 'none',
                         background: `radial-gradient(circle, hsl(${ripple.hue} 90% 60% / .55), transparent 70%)`, animation: 'ripple .8s ease-out forwards' }} />
        ))}
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 12 }}>
        <span className="muted small">{ripples.length} ripples on screen</span>
        <button className="btn-sm btn-ghost" onClick={() => setClicks(0)} disabled={!clicks}>Reset</button>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('example1')).render(<ClickExample />);