JSFiddle - React, Tailwind, and code Playground

by Christian Sonne

HTML

<canvas id="c"></canvas>
<canvas id="d"></canvas>
<div id="robot"></div>

CSS

* {
  margin: 0;
  padding: 0;
}

canvas {
  background: black;
  transform-origin: 0 0;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
}

#c {
  transform: translate(49px, 122px) scale(0.09);
}

#d {
  transform: translate(149px, 122px) scale(0.09);
}

#robot {
  position: absolute;
  top: 0;
  left: 0;
  background: url(https://i.imgur.com/6X40vYY.png) top left no-repeat;
  height: 605px;
  width: 806px;
  z-index: 1;
}

JavaScript

let S = c.height = c.width = d.height = d.width = 19 * 30;
let s = S / 2;
let ctx = c.getContext("2d");
let ctx2 = d.getContext("2d");
let nPixels = 32;
let D = S / nPixels;
let r = Math.floor(D / 3);

lidTargets = [];
lidTarget = null;
lid = 0;

let mouse = {x: 0, y: 0};

window.addEventListener("mousemove", function(e) {
	let angle = Math.atan2(s - e.clientY, s - e.clientX);
  let d = dist(e.clientX, e.clientY, s, s);
  d = clamp(d, 0, 2 * s / 3);
	mouse.x = s - Math.cos(angle) * d;
  mouse.y = s - Math.sin(angle) * d;
}, false);

window.addEventListener("click", function() {
	lidTargets.push(S);
  lidTargets.push(0);
}, false);

ctx.fillStyle = "white";

function clamp(v, ll, ul) {
	if (typeof ll === "undefined") ll = 0;
  if (typeof ul === "undefined") ul = 1;
	return Math.min(ul, Math.max(ll, v));
}

function dist(x1, y1, x2, y2) {
	return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
}

function grade(v, ul, ll) {
	return clamp(-v / (ll - ul) + 1 + ul / (ll - ul));
}

function draw() {
	if ((typeof lidTarget === "undefined" && lidTargets.length) || Math.abs(lidTarget - lid) <= 20) {
  	lidTarget = lidTargets.shift();
  }
  if (typeof lidTarget !== "undefined") {
  	lid += Math.sign(lidTarget - lid) * 65;
  }
	ctx.clearRect(0, 0, S, S);
  for (let y = 0; y < nPixels; y++) {
    let Y = (y + 0.5) * D;
  	for (let x = 0; x < nPixels; x++) {
      let X = (x + 0.5) * D;
      let d1 = dist(s, s, X, Y);
      let alpha1 = grade(d1, s - r, s);
      if (lid >= Y) {
    		ctx.globalAlpha = alpha1;
        ctx.save();
        ctx.fillStyle = "#76A2CC";
    	} else {
        let d2 = dist(mouse.x, mouse.y, X, Y);
        let alpha2 = grade(d2, s / 3, s / 3 - r);
        ctx.globalAlpha = Math.min(alpha1, alpha2);
      }
      ctx.beginPath();
      ctx.arc(X, Y, r, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();
    }
  }
  ctx2.clearRect(0, 0, S, S);
  ctx2.drawImage(c, 0, 0);
  window.requestAnimationFrame(draw);
}

draw();