JSFiddle - React, Tailwind, and code Playground

by schrodingers

HTML

<canvas id="c"></canvas>

CSS

body {
  display: flex;
  justify-content: center;
  overflow: hidden;
}

canvas {
  margin: 0 50%;
  /* width: 100%; */
  /* height: 100%; */
}

JavaScript

/*Локсодромия*/
var circles = [{
  color: '#21a5ad',
  size: 84,
  angle: Math.PI / 3
}, {
  color: '#ffad10',
  size: 92,
  angle: -Math.PI / 3
}, {
  color: '#ef4239',
  size: 100,
  angle: 0
}];
var segmentsPerCircle = 200;
var speed = .9;

function twistEasing(t) {
  return (t < .5) ? 2 * t * t : 1 - 2 * (t = 1 - t) * t;
}
var c = document.getElementById('c');
c.width = window.innerWidth;
c.height = window.innerHeight;
var ctx = c.getContext('2d');
Math.PI2 = 2 * Math.PI;

function rotateX(p, a) {
  var d = Math.sqrt(p[2] * p[2] + p[1] * p[1]),
    na = Math.atan2(p[1], p[2]) + a;
  return [p[0], d * Math.sin(na), d * Math.cos(na)];
}

function rotateY(p, a) {
  var d = Math.sqrt(p[2] * p[2] + p[0] * p[0]),
    na = Math.atan2(p[2], p[0]) + a;
  return [d * Math.cos(na), p[1], d * Math.sin(na)];
}

function rotateZ(p, a) {
  var d = Math.sqrt(p[1] * p[1] + p[0] * p[0]),
    na = Math.atan2(p[1], p[0]) + a;
  return [d * Math.cos(na), d * Math.sin(na), p[2]];
}

function resize() {
  c.width = c.offsetWidth;
  c.height = c.offsetHeight;
  ctx.translate(c.width * .5, c.height * .5);
  ctx.lineWidth = 5;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
}
window.addEventListener('resize', resize);
resize();



var angleOffset = 0;
var angleOffsetGoal = 0;
c.addEventListener('mousemove', function(e) {
  angleOffsetGoal = Math.PI2 * (e.clientX / c.width - .5);
});
c.addEventListener('mouseout', function(e) {
  angleOffsetGoal = 0;
});


function loxo(radius, angle, segments) {
  var r = [];
  for (var i = 0; i < segments; i++) {
    var a = Math.PI2 * i / segments,
      c = Math.cos(a),
      s = Math.sin(a);
    var ax = Math.PI * .5;
    ax -= (c + 1) * .5 * angle;
    r.push([radius * c, radius * s * Math.sin(ax), radius * s * Math.cos(ax)]);
  }
  return r;
}

function loop() {
  requestAnimationFrame(loop);
  angleOffset += (angleOffsetGoal - angleOffset) * .1;
  ctx.clearRect(-c.width * .5, -c.height * .5, c.width, c.height);
  var t = Date.now() * 1e-3 *...