JSFiddle - React, Tailwind, and code Playground

by jpeter06

HTML

<svg id="svg" viewBox="-10 -10 173 61">
<defs>
<path id = "arrowDef"
      d = "M -2.0286728,-0.44913063 0.01728976,-2.4950973 2.0632567,-0.44913063 H 1.2448717 V 2.0060262 h -2.4551595 v -2.45515683 z"
      stroke-width = "0px" />
</defs>
  <path class="infinity path" d="M153,20.89c0-45.88-74.95,0-74.95,0S.5,66.76.5,20.89s77.54,0,77.54,0S153,66.76,153,20.89Z" fill="none" stroke-width="12px" stroke="#fa0" vector-effect="non-scaling-stroke" marker="url(#diamond)"/>
  <g class="arrow" >
    <use xlink:href = "#arrowDef"  />
  </g>
  <text  x="38" class="small">click anywhere to start/stop</text>
  <marker id="diamond" markerWidth="12" markerHeight="12" refX="6" refY="6"
          markerUnits="userSpaceOnUse" orient="auto-start-reverse">
    <circle cx="6" cy="6" r="3"
            fill="transparent" stroke="black" stroke-width="1"/>
    <rect x="4" y="5" width="4" height="2" fill="green" stroke="context-stroke" stroke-width="2"/>
  </marker>
</svg>

CSS

body {
  height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  margin: 0;
}
svg {
  overflow: visible;
  width: 100%;
  height: 100vh;
}

rect {
  transform-origin: center;
  transform-box: fill-box;
}

.path{
  marker: url(#diamond);
}
.arrow{
  transform-origin: center;
  transform-box: fill-box;
  stroke-width:0px;
  stroke:red;
  fill:blue;
}

.small { font: italic 6px sans-serif;  }

JavaScript

// Query DOM Elements
const svg = document.getElementById("svg");
const w = 10;
const h = 5;
const step = 80;
const path = document.querySelector('.infinity');
const arrow = document.querySelector('.arrow');
const arrowList = [];
const totalLength = path.getTotalLength();

for(let i=0; i*step < totalLength; i++){
		let g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
		let aux = document.createElementNS('http://www.w3.org/2000/svg', 'use');
    aux.setAttributeNS('http://www.w3.org/1999/xlink', "xlink:href", "#arrowDef");  
    g.appendChild(aux);
    arrowList.push(g);
    svg.appendChild(g);
}
console.log(arrowList);

let pos = 0;
let anim = true;
//animate();
// Create an object that gsap can animate

function animateAll(){
  if(anim){
    pos++;
    const length = pos%totalLength;
    for(let i=0; i*step < totalLength; i++){
        animateArrow(arrowList[i], pos + i*step);
    }
  }
  requestAnimationFrame(animateAll);
}

function animateArrow(g,pos){
    const length = pos%totalLength;
    const point = path.getPointAtLength(length);
    const p1 = path.getPointAtLength(length + 5);
    const p2 = path.getPointAtLength(length - 5);

    var deg = Math.atan2(p1.y - p2.y, p1.x - p2.x) * (180 / Math.PI) + 90;
    g.setAttribute('transform', "translate(" + point.x + ',' + point.y +
                        ') rotate(' + deg + ')');
}


requestAnimationFrame(animateAll);

document.body.addEventListener('click', ()=>{
	anim = ! anim;
}, true); 

//OLD 1 arrow
/*
function animate(){
  if(anim){
    pos++;
    const length = pos%totalLength;
    const point = path.getPointAtLength(length);
    const p1 = path.getPointAtLength(length + 5);
    const p2 = path.getPointAtLength(length - 5);

    var deg = Math.atan2(p1.y - p2.y, p1.x - p2.x) * (180 / Math.PI) + 90;
    arrow.setAttribute('transform', "translate(" + point.x + ',' + point.y +
                        ') rotate(' + deg + ')');
  }
  requestAnimationFrame(animate);
}
*/