JSFiddle - React, Tailwind, and code Playground

by fgnass

HTML

<div id="container"></div>

CSS

html,
body {
  background: #252525;
}

JavaScript

var NUM_PARTICLES = ((ROWS = 20) * (COLS = 20)),
  THICKNESS = Math.pow(60, 2),
  SPACING = 20,
  MARGIN = 100,
  COLOR = 220,
  DRAG = 0.95,
  EASE = 0.25,

  canvas,
  mouse,
  list,
  ctx,
  dx, dy,
  mx, my,
  d, t, f,
  a, b,
  i, n,
  w, h,
  p, s,
  r, c;

function init() {

  container = document.getElementById('container');
  canvas = document.createElement('canvas');

  ctx = canvas.getContext('2d');

  list = [];

  w = canvas.width = COLS * SPACING + MARGIN * 2;
  h = canvas.height = ROWS * SPACING + MARGIN * 2;

  for (i = 0; i < NUM_PARTICLES; i++) {

    p = {
      vx: 0,
      vy: 0,
      x: 0,
      y: 0
    };
    p.x = p.ox = MARGIN + SPACING * (i % COLS);
    p.y = p.oy = MARGIN + SPACING * Math.floor(i / COLS);

    list[i] = p;
  }



  container.addEventListener('pointermove', function(e) {
    const bounds = container.getBoundingClientRect();
    mx = e.clientX - bounds.left;
    my = e.clientY - bounds.top;
  });

  container.appendChild(canvas);

  ctx.fillStyle = "#5B5B5B";
}

function step() {

  for (i = 0; i < NUM_PARTICLES; i++) {

    p = list[i];

    d = (dx = mx - p.x) * dx + (dy = my - p.y) * dy;
    f = -THICKNESS / d;

    if (d < THICKNESS) {
      t = Math.atan2(dy, dx);
      p.vx += f * Math.cos(t);
      p.vy += f * Math.sin(t);
    }

    p.x += (p.vx *= DRAG) + (p.ox - p.x) * EASE;
    p.y += (p.vy *= DRAG) + (p.oy - p.y) * EASE;

  }

  ctx.clearRect(0, 0, w, h);
  for (i = 0; i < NUM_PARTICLES; i++) {

    p = list[i];
    ctx.fillRect(p.x, p.y, 2, 2);
  }

  requestAnimationFrame(step);
}

init();
step();