How to set time according to svg length?

Viewed 102

I am learning javascript and react and here I am stuck in the challange that where I have to set time according to SVG image's length (something like progress bar).

For an ease and basic example, I am calculating total time of 24 hours in 1440 minutes and I have given passed time let passedMins = 1220

Now I need to fill the green line in svg circle according to the time elapse. Like every minute green line should add up more

import React, {
  useState,
  useEffect
} from 'react';

function Clock(props) {

  useEffect(() => {
    const circle = document.querySelector('circle');
    const circleLength = Math.floor(circle.getTotalLength());
    const perMinArea = circleLength / 1440;
    let passedMins = 1220;
    const fillArea = passedMins * perMinArea;
    circle.style.strokeDasharray = circleLength - fillArea;
    // circle.style.strokeDashoffset = circleLength -fillArea;

  }, [props.time])

  return (
    <div className = "clock-space" >
      <div className = "clock" >{props.time}</div>
      <svg width = "130" height = "130" className = "circle" viewBox = "0 0 130 130" fill = "none" xmlns = "http://www.w3.org/2000/svg">
        <circle cx="65" cy="65" r="61.5" stroke="lightgreen" strokeWidth="7"/>
      </svg>
    </div >
  );
}

export default Clock;

It looks like this:

Image of circle

It shows 3 lines where I want only one. Any suggestion to fix it?

2 Answers

You need to also pass the length of the circle to the circle.style.strokeDasharray. To do this, you need to get the length for the "minutes left" in terms of the circle's total length. Please see example below.

const circle = document.querySelector('circle');
const timeLeftLabel = document.getElementById('time-left');
const circleLength = Math.floor(circle.getTotalLength());
const maxMinutes = 1440;
let passedMins = 1220;
let minsLeft = maxMinutes - passedMins;

// get percentage of minutes left
let minsLeftPercentage = minsLeft / maxMinutes;

// x % of length where x  = minsLeftPercentage
let leftLength = minsLeftPercentage * circleLength;

//combine leftLength and circleLength into comma-separated string
circle.style.strokeDasharray = leftLength + ',' + circleLength;

//just simple implementation for the {props.time} 
timeLeftLabel.innerText = minsLeft + ' minutes left';
<div className="clock-space">
  <div className="clock" id="time-left"></div>
  <svg width="130" height="130" className="circle" viewBox="0 0 130 130" fill="none" xmlns="http://www.w3.org/2000/svg">
        <circle cx="65" cy="65" r="61.5" stroke="lightgreen" strokeWidth="7"/>
      </svg>
</div>

You could use a second circle (maybe with an id "mask") to cover the green stroke. The green one should have no dasharray. Then you would change the stroke-dashoffset for the mask. In my simple animate() test (without react) it ran from 0 to -390 (circlelength). The stroke color for #mask (here for testing white) should be the one from your background...

const circle = document.querySelector('#mask');
const maxMins = 1440;
...
  circle.style.strokeDasharray = maxMins;
  circle.style.strokeDashoffset = maxMins - circleLength + fillArea;
...
<circle cx="65" cy="65" r="61.5" stroke="lightgreen" strokeWidth="7"/>
<circle id="mask" cx="65" cy="65" r="61.5" stroke="white" strokeWidth="7"/>

Working example (here with "timelapse" and reset of passedMins):

    const circle = document.querySelector('#mask');
    const circleLength = Math.floor(circle.getTotalLength());
    const maxMins = 1440;
      const perMinArea = circleLength / maxMins;
    let passedMins = 0;
      circle.style.strokeDasharray = maxMins;
      circle.style.strokeDashoffset = maxMins - circleLength;
    
    setInterval( function() {
        passedMins = (passedMins == maxMins) ? 0 : passedMins + 3;
        
        const fillArea = passedMins * perMinArea;
        circle.style.strokeDashoffset = maxMins - circleLength + fillArea;
    }, 10);
    <div className="clock-space">
        <div className="clock" id="time-left"></div>
        <svg width="130" height="130" className="circle" viewBox="0 0 130 130" fill="none" xmlns="http://www.w3.org/2000/svg">
            <circle cx="65" cy="65" r="61.5" stroke="lightgreen" strokeWidth="7"/>
            <circle id="mask" cx="65" cy="65" r="61.5" stroke="white" strokeWidth="7"/>
        </svg>
    </div>

Related