JSFiddle - React, Tailwind, and code Playground
HTML
<svg id="wave" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 456 100" style="enable-background:new 0 0 456 100;" xml:space="preserve">
<style type="text/css">
.st69{fill:none;stroke:#000000;stroke-width:12;stroke-miterlimit:10;}
</style>
<circle cx="50" cy="50" r="4" fill="red" />
<circle cx="100" cy="50" r="4" fill="red" />
<circle cx="150" cy="50" r="4" fill="red" />
<circle cx="200" cy="50" r="4" fill="red" />
<circle cx="250" cy="50" r="4" fill="red" />
<circle cx="300" cy="50" r="4" fill="red" />
<circle cx="350" cy="50" r="4" fill="red" />
<circle cx="400" cy="50" r="4" fill="red" />
</svg>v
JavaScript
const NS = 'http://www.w3.org/2000/svg';
const svg = document.querySelector('svg');
const circles = [...document.querySelectorAll('svg circle')];
const lines = [];
for (let i = 0; i < circles.length - 1; i++) {
const c0 = circles[i];
const c1 = circles[i + 1];
const line = document.createElementNS(NS, 'line');
line.setAttribute('stroke-width', 1);
line.setAttribute('stroke', 'black');
line.setAttribute('x1', c0.getAttribute('cx'));
line.setAttribute('y1', c0.getAttribute('cy'));
line.setAttribute('x2', c1.getAttribute('cx'));
line.setAttribute('y2', c1.getAttribute('cy'));
lines.push(line);
svg.appendChild(line);
}
function anim() {
const now = performance.now();
circles.forEach((c, i) => {
c.setAttribute('cy', 50 + Math.sin(now / 1000 + i) * 25);
});
lines.forEach((c, i) => {
c.setAttribute('y1', 50 + Math.sin(now / 1000 + i) * 25);
c.setAttribute('y2', 50 + Math.sin(now / 1000 + i + 1) * 25);
});
}
setInterval(anim, 20);