JSFiddle - React, Tailwind, and code Playground

by Nikhil krishnan

HTML

<canvas></canvas>

CSS

html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}


/* HTML5 display-role reset for older browsers */

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
    display: block;
}

body {
    line-height: 1;
}

ol,
ul {
    list-style: none;
}

blockquote,
q {
    quotes: none;
}

blockquote:before,
blockquote:after,
q:before,
q:after {
    content: '';
    content: none;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
}

*,
*:before,
*:after {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

html,body{
    height: 100%;
}

JavaScript

var c = document.querySelector("canvas"),
    ctx = c.getContext("2d"),
    
    // the fixed point chain
    pointsFixed = [
      {x: 50, y: 50},
      {x: 100, y: 60},
      {x: 90, y: 90},
      {x: 120, y: 110},
      {x: 200, y: 80},
      {x: 250, y: 130}
    ],

    // for the IK chain - dragged by the *last* point
    points = [
      {x: 50, y: 50},
      {x: 100, y: 60},
      {x: 90, y: 90},
      {x: 120, y: 110},
      {x: 200, y: 80},
      {x: 250, y: 130}
	],
    
    min = 40, max = 70,
    isDown = false,
    
    // for animation
    isPlaying = false,
    t, step = 0.1;       // t = [0, 1]

// set canvas size
resize();
window.onresize = resize;

function resize() {
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  calc();
  render(points)
}

// handle mouse
c.onmousedown = function(e) {
  var pos = getXY(e), 
      p = points[points.length - 1];

  isDown = (pos.x > p.x - 7 && pos.x < p.x + 7 && pos.y > p.y - 7 && pos.y < p.y + 7);
};

window.onmousemove = function(e) {
  if (!isDown) return;
	
  points[points.length - 1] = getXY(e);    // override last point with mouse position

  // update chain and canvas
  calc();
  render(points);	
};

window.onmouseup = function() {
  if (isDown) {
    isDown = false;
    t = 0;                // reset t for new animation
    isPlaying = true;     // allow looping
    animate();            // start the animation
  }
};

// adjusted mouse position
function getXY(e) {
  var rect = c.getBoundingClientRect();
  return {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top
  }
}

// IK chain calculations
function calc() {
	
  var angle, i, p1, p2, dx, dy, distance;
	
  for(i = points.length - 1; i > 0; i--) {
    p1 = points[i];
    p2 = points[i-1];
    dx = p2.x - p1.x;
    dy = p2.y - p1.y;
    angle = Math.atan2(dy, dx);
    distance = Math.max(min, Math.min(max, Math.sqrt(dx*dx + dy*dy)));
		
    p2.x = p1.x + distance * Math.cos(angle);
    p2.y = p1.y + distance *...