0

I've been trying to create a countdown that animates the text every time the counter changes. I can execute the change at the start but not sure how to clear the change and repeat it.

HTML & CSS

<!DOCTYPE html>
<html>
<head>
    <!--meta http-equiv="refresh" content="4; url=index.php"-->
    <style type="text/css">
        p
        {
            transition: 1s;
            font-size: 100%;
        }
    </style>
</head>
<body>

<p>A script on this page starts this clock:</p>
<p id="demo">Login in....</p>

<script>

    //various javascript here

</script>
</body>
</html>

This script changes the CSS once on the first count but not on count 2 or 1.

//Set the counter variable
    var counter= 4;

    //Set the timer to count the counter down
    var timer = setInterval(function() 
    {
        counter--;
        if(counter== 0) 
        {
            clearInterval(timer);
        } 
        else 
        {
            //Execute the CSS change function
            styleChange();
        }
    }, 1000);

    function styleChange()
    {
        document.getElementById("demo").innerHTML = (counter);
        document.getElementById("demo").style.fontSize = "500%";
        document.getElementById("demo").style.opacity = "0";
        // In CSS "Demo" is given a transition of 1s to smooth out the animation"
    }

The next script doesn't reset the style each time the counter but toggles between styles, not what I'm trying to do but at least it repeats unlike the first script. It just affects the style, if it worked I would add the timer.

    var myVar = setInterval(setStyle, 1000);

    function setStyle() {
      var x = document.getElementById("demo");
      x.style.fontSize = x.style.fontSize == "100%" ? "500%" : "100%";
    }

    function stopStyle() {
      clearInterval(myVar);
    }

I thought of trying to use a "for" to loop through the values of the counter, change the style on a value change then reset the value after but I couldn't get it to work. Visually the effect should look like this but played in reverse and not on acid-> https://www.youtube.com/watch?v=hsUKu9_Lr6U. I've borrowed heavily from w3schools pages about setInterval and clearInterval.

2
  • Please add the relevant HTML and CSS to your question so that we can replicate your issue and provide a working result. Commented May 5, 2020 at 18:11
  • Updated, sorry about that. SD Commented May 5, 2020 at 18:15

3 Answers 3

2

You don't need two separate intervals to make the text bigger and smaller.

Instead, you could use CSS transitions to animate the font-size from small to big and 2 nested Window.requestAnimationFrame() calls to time things properly, as shown below:

const counter = document.getElementById('counter');
let value = 11;

const intervalID = setInterval(() => {
  const nextValue = --value;
  
  if (nextValue === -1) {
    clearInterval(intervalID);
    
    return;
  }
  
  requestAnimationFrame(() => {
    // Update the value and remove the `big` class in the next frame, so that
    // the text becomes smaller again:
    counter.textContent = nextValue;
    counter.classList.remove('big');
  
    requestAnimationFrame(() => {
      // One more frame after that (so that the element has time to be re-rendered
      // with the smaller font-size, add the `big` class again:
      counter.classList.add('big');
    });
  });
  
}, 1000);
body {
  margin: 0;
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  font-family: monospace;
}

#counter.big {
  font-size: 500%;
  opacity: 0;
  transition: all linear 1s;
}
<div id="counter" class="big">10</div>

You could even use CSS custom properties to easily change the interval delay while keeping it in-sync with the CSS transition-duration:

const duration = 0.5; // In seconds
const counter = document.getElementById('counter');
let value = 11;

counter.style.setProperty('--transition-duration', `${ duration }s`);

const intervalID = setInterval(() => {
  const nextValue = --value;
  
  if (nextValue === -1) {
    clearInterval(intervalID);
    
    return;
  }
  
  requestAnimationFrame(() => {
    // Update the value and remove the `big` class in the next frame, so that
    // the text becomes smaller again:
    counter.textContent = nextValue;
    counter.classList.remove('big');
  
    requestAnimationFrame(() => {
      // One more frame after that (so that the element has time to be re-rendered
      // with the smaller font-size, add the `big` class again:
      counter.classList.add('big');
    });
  });
  
}, duration * 1000);
body {
  margin: 0;
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  font-family: monospace;
}

#counter.big {
  /* Default value: */
  --transition-duration: 1s;
  
  font-size: 500%;
  opacity: 0;
  transition: all linear var(--transition-duration);
}
<div id="counter" class="big"></div>

Sign up to request clarification or add additional context in comments.

4 Comments

@123samueld Happy to help! Would be great if you can accept the answer. 🙂
Do you think it would work to add "small" class with normal size and opacity and remove it while also adding the "big" class? Because atm adding opacity style to the "big" class makes the text opaque almost immediately. Also the text starts out super large where it should start normal on load.
Yes, you can add that to the CSS transition. Just updated the examples.
Awesome, thank you so much, you've saved me a literal headache. I've accepted your answer. SD :D
1

You can do that with another setInterval which will reduce the font size by 10px every 100ms I hope this will help


//ele is same as your demo element
var ele=document.getElementById('demo');
//function that reduces the size of text of ele by 10px every 100ms
function redSize(){
    var size=100;
    var small=setInterval(()=>{
    ele.style.fontSize=size+"px";size-=10;if(size==10) clearInterval(small); 
    },100)
}
function timer(count){
    var counter=count;
    var timer = setInterval(function(){
        if(counter== 0) 
        {
            clearInterval(timer);
        } 
        else 
        {
            ele.innerHTML=counter;
            redSize();
        }
        counter--;
    }, 1000);
}

timer(10);
<p id='demo'/>

1 Comment

That's interesting as well, thank you for an additional solution. ^_^
0

Here is a version using @keyframes to achieve the same goal.

.running {
  animation: growAndShow 1s linear infinite;
}

@keyframes growAndShow {
  0%,
  100% {
    font-size: 100%;
    opacity: 1;
  }
  50% {
    font-size: 500%;
    opacity: 0;
  }
}
<p>A script on this page starts this clock:</p>
<p id="demo">Login in....</p>
<script>

var counter = 4;
var element = document.getElementById("demo")
var text = element.innerText;
element.innerText = text + counter;
var myVar = setInterval(runTimer, 1000);
element.classList.toggle('running');
function runTimer() {
  
 element.innerText = text + --counter;
  if (counter <= 0) stopTimer();
}

function stopTimer() {
  clearTimeout(myVar);
  element.classList.toggle('running');
}
</script>

1 Comment

I'm not sure that works. It appears to bounce from large to small, like the way my second script does. The opacity is good but rather than resetting right back to normal size and opacity it reverts back using a transition, so it looks like it's bouncing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.