Complex Multiplication

by Josh Pullen

HTML

<img id="grab"...

CSS

canvas {
  border: 1px solid black;
  width: 100%;
}

img {
  display: none;
}

JavaScript

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");

document.body.appendChild(canvas);

canvas.width = 1280;
canvas.height = 720;

const lerp = (a, b, t = 0) => {
	if (Array.isArray(a) && Array.isArray(b)) {
  	let result = [];
    for (let i = 0; i < a.length; i++) {
    	result.push(lerp(a[i], b[i], t));
    }
    return result;
  }

	if (t < 0) t = 0;
  if (t > 1) t = 1;
  return a + (b - a) * t;
}

const sequence = (map, ease = (t) => t) => {
	const keyTimes = Object.keys(map).map(n => Number(n)).sort((a, b) => a - b);
	return (t) => {
  	const beforeTime = [...keyTimes].reverse().find(time => time <= t);
    const afterTime = keyTimes.find(time => time > t);
    
    if (beforeTime === undefined) return map[afterTime];
    if (afterTime === undefined) return map[beforeTime];
    
    return lerp(
    	map[beforeTime],
      map[afterTime],
      ease(lerp(0, 1, (t - beforeTime) / (afterTime - beforeTime)))
    );
  }
}

const ease = {
	sin: (t) => (1 - Math.cos(t * Math.PI)) / 2,
  out: (t) => 1 - (1 - t) ** 3,
}

function render(t) {
	ctx.fillStyle = "white";
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	const progress = sequence({
  	0: 0,
    2.5: 0,
    4: 1,
    8: 1,
    10: 0,
    15: 0,
    18: 1,
  })(t);
  
	const complexMode = lerp(0, 1, 0.5 * (t - 10));

  grid(0, complexMode, "#e4e4e4", "#bbb");
  grid(progress, complexMode, "#aaa", "#888");
  
  const grabbing = t > 2 && t < 4 || t > 13 && t < 18;
  
  onePoint(progress, complexMode, grabbing);
  negTwoPoint(progress, complexMode);
  
  renderMouse(
  	...sequence({
    	0: [850, 720],
      0.8: [850, 720],
      2: [700, 350],
      2.5: [700, 350],
      4: [840, 350],
      5: [840, 350],
      6: [900, 500],
      12: [900, 500],
      13: [685, 335],
      15: [685, 335],
      18: [825, 195],
      18.5: [825, 195],
      19.5: [1100, 100]
    }, ease.sin)(t),
    grabbing
  );
}

function getPoint(real, imaginary, progress, complexMode) {
	const...