CSS: Pause animation on timer

Viewed 427

I am implementing a time counter.

The objective is that user can start the timer, and when he feels like it, he can pause or continue the timer.

The difficulty I encountered was pausing the SVG animation.

I followed this article, however it does not work.

Any suggestions?

CODE

(function() {
    const start = document.querySelector( '.start' ),
      togglePause = document.querySelector( '.pause' ),
        seconds = 60;

    function format( seconds ) {
        var sec = Math.floor( seconds % 60 ),
            min = Math.floor( seconds / 60 );

            secdisp = sec;
            mindisp = min;

            if( min === 0 && sec === 0 ) {
                clearInterval( countdown );
            } else if ( secdisp === 0 ) {
                min--;
                sec = 0;
            }
    
          return mindisp + ':' + ( secdisp < 10 ? '0' + secdisp : secdisp );
    }

    function tickdown( seconds ) {
        var mod = Math.floor( seconds % 60 ),
            min = Math.floor( seconds / 60 ) - ( mod === 0 ? 1 : 0 ),
            sec = ( mod ? 60 - mod : 0 );

        var tick = () => {
            seconds--;
    
            document.getElementById('time').innerHTML = format( seconds );
        };
        tick();
        countdown = setInterval( tick, 1000 );
    }

    document.getElementById('time').innerHTML = format( seconds );

    start.addEventListener('click', () => {
        var gauge = document.querySelector( '.timer-gauge' ),
            frame = document.querySelector( 'html' );


        tickdown( seconds );
        gauge.style.transitionDuration = seconds + 's';
        gauge.classList.add('ticking');
    });
  
  togglePause.addEventListener( 'click', () => {
    var gauge = document.querySelector( '.timer-gauge' );
    const running = gauge.style.animationPlayState === 'running';
     gauge.style.animationPlayState = running ? 'paused' : 'running';  
    
  });
}());
* {
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
html {
    background: linear-gradient(125deg, #5967c3 0%,#76b8d6 100%);
    height: 100vh;
    width: 100vw;
    max-height: 100vh;
    max-width: 100vw;

}

html:after {
    content: " ";
    position: absolute;
    height: 100%;
    left: 0;
    top: 0;
    opacity: 0;
    transition: opacity .1s linear;
    width: 100%;
    z-index: 1;
    pointer-events: none;

}
html.started:after {
    background: linear-gradient(125deg, #52b670 0%,#84c86d 100%);
    opacity: 1;
}
body {
    margin: 0;
}
svg {
    position: absolute;
    width: 100%;
    height: 100%;
    padding: 2%;
    pointer-events: none;
    z-index: 2;
}
.main {
    max-width: 100%;

}
.chart-gauge {
    font-size: 1.5rem;
    text-anchor: middle;
    dominant-baseline: central;
    alignment-baseline: middle;
    stroke-linecap: round;
}
.timer-time {
    fill: rgba(255,255,255,.6);
}
.timer-backdrop {
    transform: rotate(-90deg);
    transform-origin: center;
    fill: none;
    stroke: rgba(255,255,255,.2);
    stroke-width: 5;
}
.timer-gauge {
    transform: rotate(-270deg) scaleX(-1);
    transform-origin: center;
    fill: none;
    stroke: rgba(255,255,255,.75);
    stroke-width: 5;
    animation: fill 1s;
    animation-iteration-count: 1;
    stroke-dashoffset: 0;
}
.ticking {
    stroke-dashoffset: 360;
    transition-timing-function: linear;
}

@keyframes fill {
  0%   { stroke-dashoffset: 360; }
  100% { stroke-dashoffset: 0; }
}
<div class="main">  
  <svg viewBox="0 0 120 120" class="chart-gauge" fill="none" stroke-width="2">
    <circle class="timer-backdrop" stroke-dasharray="360" r="57.32" cx="60" cy="60"></circle>
    <circle class="timer-gauge" stroke-dasharray="360" r="57.32" cx="60" cy="60"></circle>
    <text y="50%" x="50%" id="time" class="timer-time">1:00</text>
  </svg>

  <button class="start">Start</button>
</div>
<button class="pause">Toggle Pause</button>

CODE

Code change with the suggestion of this answer.

The problem now is that the animation got faster in the middle and slower at the beginning and at the end, so it will not hit the right spots on the "clock"

(function() {
    const start = document.querySelector( '.start' ),
      togglePause = document.querySelector( '.pause' ),
        seconds = 60;

    function format( seconds ) {
        var sec = Math.floor( seconds % 60 ),
            min = Math.floor( seconds / 60 );

            secdisp = sec;
            mindisp = min;

            if( min === 0 && sec === 0 ) {
                clearInterval( countdown );
            } else if ( secdisp === 0 ) {
                min--;
                sec = 0;
            }
    
          return mindisp + ':' + ( secdisp < 10 ? '0' + secdisp : secdisp );
    }

    function tickdown( seconds ) {
        var mod = Math.floor( seconds % 60 ),
            min = Math.floor( seconds / 60 ) - ( mod === 0 ? 1 : 0 ),
            sec = ( mod ? 60 - mod : 0 );

        var tick = () => {
            seconds--;
    
            document.getElementById('time').innerHTML = format( seconds );
        };
        tick();
        countdown = setInterval( tick, 1000 );
    }

    document.getElementById('time').innerHTML = format( seconds );

    start.addEventListener('click', () => {
        var gauge = document.querySelector( '.timer-gauge' ),
            frame = document.querySelector( 'html' );


        tickdown( seconds );
        gauge.style.animationDuration = seconds + 's';
        gauge.classList.add('ticking');
    });
  
  togglePause.addEventListener( 'click', () => {
    var gauge = document.querySelector( '.timer-gauge' );
    const running = gauge.style.animationPlayState === 'running';
     gauge.style.animationPlayState = running ? 'paused' : 'running';  
    
  });
}());
* {
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
html {
    background: linear-gradient(125deg, #5967c3 0%,#76b8d6 100%);
    height: 100vh;
    width: 100vw;
    max-height: 100vh;
    max-width: 100vw;

}

html:after {
    content: " ";
    position: absolute;
    height: 100%;
    left: 0;
    top: 0;
    opacity: 0;
    transition: opacity .1s linear;
    width: 100%;
    z-index: 1;
    pointer-events: none;

}
html.started:after {
    background: linear-gradient(125deg, #52b670 0%,#84c86d 100%);
    opacity: 1;
}
body {
    margin: 0;
}
svg {
    position: absolute;
    width: 100%;
    height: 100%;
    padding: 2%;
    pointer-events: none;
    z-index: 2;
}
.main {
    max-width: 100%;

}
.chart-gauge {
    font-size: 1.5rem;
    text-anchor: middle;
    dominant-baseline: central;
    alignment-baseline: middle;
    stroke-linecap: round;
}
.timer-time {
    fill: rgba(255,255,255,.6);
}
.timer-backdrop {
    transform: rotate(-90deg);
    transform-origin: center;
    fill: none;
    stroke: rgba(255,255,255,.2);
    stroke-width: 5;
}
.timer-gauge {
    transform: rotate(-270deg) scaleX(-1);
    transform-origin: center;
    fill: none;
    stroke: rgba(255,255,255,.75);
    stroke-width: 5;
    animation-name: initial-fill;
    animation-duration: 1s;
    animation-iteration-count: 1;
    stroke-dashoffset: 0;
}
.ticking {
animation-name: fill;
    stroke-dashoffset: 360;
    transition-timing-function: linear;
}

@keyframes initial-fill {
  0%   { stroke-dashoffset: 360; }
  100% { stroke-dashoffset: 0; }
}

@keyframes fill {
  0%   { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 360; }
}
<div class="main">  
<svg viewBox="0 0 120 120" class="chart-gauge" fill="none" stroke-width="2">
            <circle class="timer-backdrop" stroke-dasharray="360" r="57.32" cx="60" cy="60"></circle>
            <circle class="timer-gauge" stroke-dasharray="360" r="57.32" cx="60" cy="60"></circle>
            <text y="50%" x="50%" id="time" class="timer-time">1:00</text>
        </svg>

        <button class="start">Start</button>
</div>

        <button class="pause">Toggle Pause</button>
</div>

1 Answers

I haven't tested it; just digging up old knowledge.

With animation: fill 1s; shorthand notation
you are also messing with the animation-play-state setting.

Try using all separate properties: https://developer.mozilla.org/en-US/docs/Web/CSS/animation

and a animation-play-state: running (or paused) to start with

Update

If I change your CSS to:

    xanimation: fill 1s;
    animation-name: fill;
    animation-duration:60s;
    animation-play-state:paused;

Your toggle does something

So play-state toggle works

Now you have to make your CSS and SVG do what you want

Related