<div class="container">
  <span class="label">Wind (m/s) </span>
  <div class="progressContainer">
    <svg class="progress" viewBox="0 0 400 100">
      <path
            class="drop"
            style="fill:none;stroke:#d0d0d0;stroke-width:8;stroke-linecap:round;"
            d="m 20,80 c 0,0 60,-0.001 90,-0.001 30,0 60,0.001 90,0.001 30,0 60,-0.001 90,-0.001 30,0 90,0.001 90,0.001" />
      <path
            class="range"
            style="fill:none;stroke:#3cc124;stroke-width:8;stroke-linecap:round;"
            d="m 20,80 c 0,0 60,-0.001 90,-0.001 30,0 60,0.001 90,0.001 30,0 60,-0.001 90,-0.001 30,0 90,0.001 90,0.001" />
    </svg>
  </div>
</div>
<div class="container">
  <span class="label">Current (mA)</span>
  <div class="progressContainer">
    <svg class="progress" viewBox="0 0 400 100">
      <path
            class="drop drop2"
            style="fill:none;stroke:#d0d0d0;stroke-width:8;stroke-linecap:round;"
            d="m 20,80 c 0,0 60,-0.001 90,-0.001 30,0 60,0.001 90,0.001 30,0 60,-0.001 90,-0.001 30,0 90,0.001 90,0.001" />
      <path
            class="range range2"
            style="fill:none;stroke:#6397e4;stroke-width:8;stroke-linecap:round;"
            d="m 20,80 c 0,0 60,-0.001 90,-0.001 30,0 60,0.001 90,0.001 30,0 60,-0.001 90,-0.001 30,0 90,0.001 90,0.001" />
    </svg>
  </div>
</div>
@import url('https://rsms.me/inter/inter-ui.css');
body {
  align-items: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin: 0 30px;
  height: 100vh;
  font-family: "Inter UI";
  font-size: 1.6em;
}
.container {
  align-items: center;
  display: flex;
  justify-content: center;
  width: 100%;
}
.label {
  min-width: 165px;
}
.progressContainer {
  margin-left: 30px;
  min-width: 100px;
  display: flex;
  justify-content: flex-end;
}
.progress {
  height: 100px;
  min-width: 400px;
  transform: translateY(-30px);
  transform-origin: 100% 50%;
  width: 400px;
}
const array2d = (array) => {
  return array.reduce((accumulated, pair, i) => `${accumulated} ${i === 1 ? 'c ' : ''}${pair.join(',')}`
                      , 'm');
}

const d2Array = (d) => {
  const pairs = d.split(' ').filter(v => v.includes(','));
  return pairs.map(pair => pair.split(',').map(v => parseFloat(v), 10));
}

const duration = 2000, transformDuration = 600,
      progresses = Array.from(document.querySelectorAll('.progress')),
      ranges = Array.from(document.querySelectorAll('.range')),
      drops = Array.from(document.querySelectorAll('.drop')),
      from = d2Array(ranges[0].getAttribute('d'));
toD = "m 340,12.475269 c -20.72434,0 -37.52473,16.800394 -37.52473,37.524731 0,20.724337 16.80039,37.524731 37.52473,37.524731 20.72433,0 37.52473,-16.800394 37.52473,-37.524731 0,-20.724337 -16.8004,-37.524731 -37.52473,-37.524731"
to = d2Array(toD);
let start = null, transformStart = null;
let reverse = false, transformReverse = null;
const animationObjects = [
  { selector: '.range', duration: 2000, reverse: false },
  { selector: '.range2', duration: 1500, reverse: false },
];
animationObjects.forEach((ao) => {
  ao.object = document.querySelector(ao.selector);
})

const interpolate = (fromValue, toValue, quota) => {
  return fromValue + (toValue - fromValue) * quota;
};

const interpolatePair = (pairFrom, pairTo, quota) => {
  return pairTo.map(((toValue, i) => interpolate(pairFrom[i], toValue, quota)))
};

const cubic = (t) => t * t * t;

const animateRange = (obj, quota) => {
  const totalLength = drops[0].getTotalLength();
  length = quota * totalLength;
  obj.style.strokeDasharray = `${length} ${totalLength * 1.1}`
};

const animateTransition = (quota) => {
  const current = to.map((pairTo, i) => interpolatePair(from[i], pairTo, quota))
  const d = array2d(current)
  ranges.forEach((range) => {
    range.setAttribute('d', d);
  });
  drops.forEach((drop) => {
    drop.setAttribute('d', d);
  });
  progresses.forEach((progress) => {
    progress.style.transform = `translateY(${(1 - quota) * -30}px) scale(${1 - quota * 0.2})`;
  });
}

function step(timestamp) {
  // if (!start) start = timestamp;
  // const progress = timestamp - start;
  // let quota = progress / duration;
  // if (reverse) quota = 1 - quota;
  // animateRange(quota);
  // if (progress < duration) {
  // } else {
  //   animateRange(reverse ? 0 : 1);
  //   start = timestamp;
  //   //animateTransition(1);
  //   reverse = !reverse;
  // }
  animationObjects.forEach((ao) => {
    if (!ao.start) ao.start = timestamp;
    const progress = timestamp - ao.start;
    let quota = progress / ao.duration;
    if (ao.reverse) quota = 1 - quota;
    animateRange(ao.object, quota);
    if (progress < ao.duration) {
    } else {
      animateRange(ao.object, ao.reverse ? 0 : 1);
      ao.start = timestamp;
      ao.reverse = !ao.reverse;
    }
  });

  if (transformStart) {
    const transformProgress = timestamp - transformStart;
    let transformQuota = transformProgress / transformDuration;
    if (transformReverse) transformQuota = 1 - transformQuota;
    animateTransition(cubic(transformQuota));
    if (transformProgress < transformDuration) {
    } else {
      animateTransition(transformReverse ? 0 : 1);
      transformStart = null;
    }
  }

  window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);

let bigScreen = true;
window.addEventListener('resize', () => {
  if (window.innerWidth > 767 && !bigScreen) {
    transformStart = performance.now();
    transformReverse = true;
    bigScreen = true;
  } else if (window.innerWidth <= 767 && bigScreen) {
    transformStart = performance.now();
    transformReverse = false;
    bigScreen = false;
  }
})

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.