skip to Main Content

I’m trying to make a text fade in fade out transition effect as such:

let change = document.querySelector("#change")
let transition_duration = 2000

let title = ["a plane!", "Superman!", "a bird!"]
let i = 0

setInterval(async() => {
  change.style.transitionDuration = (transition_duration / 2000) + 's'
  change.classList.add("fadeout")

  setTimeout(() => {
    change.classList.remove("fadeout")
    change.innerText = title[i];
    i = (i + 1) % title.length
  }, transition_duration / 2);

}, 3000)
#change {
  padding: 0 0.5rem;
  background-color: red;
  border-radius: 3px;
  color: white;
  transition: all;
}

.fadeout {
  color: transparent !important;
}
It's <span id="change">a bird!</span>

But as you can see the width does not transition when the text changes the length of the <span>
is there fix for this?

3

Answers


  1. The animation transition needs an explicit initial and ending value.
    width needs to have an explicit value.

    Login or Signup to reply.
  2. It’s not clear how you want to animate the width. Based on your code you expect it animated when the text is changed, not ahead, so the solution.

    Also you have an useless async.

    The width should be explicitly set and takes effects when you span is display:inline-block. To make it line up with the rest of the text you could use vertical-align:middle (it’s base by default).

    To avoid text overflowing when width changes to bigger you need overflow:hidden.
    To get a right width with paddings you could set box-sizing:border-box. Also set white-space:nowrap so in this case the text won’t break on a new line.

    To center the text during the animation place it in an inner span with margin: 0 -100%.

    To calc the next width you should set the span to inline and set the next title temporarily.

    let change=document.querySelector("#change")
    let transition_duration=2000
    
    let title=["a plane!", "Superman!", "a bird!" ]
    let i=0
    
    change.innerHTML = `<span>${change.textContent}</span>`;
    change.style.width = change.offsetWidth + 'px';
    
    setInterval(()=>{
    
      change.style.transitionDuration=(transition_duration/2000) + 's'
      change.classList.add("fadeout")
      const prevTitle = change.innerText;
      i=(i + 1)%title.length;
      change.style.display='inline';
      change.innerText=title[i];
      const widthTo = change.offsetWidth;
      change.style.display='inline-block';
      change.innerText=prevTitle;
     
      setTimeout(()=>{
         console.log(widthTo);
         change.style.width = widthTo + 'px';
         change.classList.remove("fadeout");
         change.innerHTML = `<span>${title[i]}</span>`;
         
      },transition_duration/2);
    
    },3000)
    #change{
      display:inline-block;
      box-sizing: border-box;
      vertical-align:middle;
      padding: 0 0.5rem;
      background-color:red;
      border-radius:3px;
      color:white;
      transition: all;
      overflow: hidden;
      text-align:center;
      white-space:nowrap;
    
    }
    #change span {
      margin: 0 -100%;
    }
    
    .fadeout{
      color:transparent !important;
    }
    It's <span id="change">a bird!</span>

    A solution with width animation during all the transition.

    Here we could actually use async to wait for the next animation frame. Also we cound use a css animation instead of a transition:

    const change=document.querySelector("#change")
    const duration=2000
    
    const title=["a plane!", "Superman!", "a bird!" ]
    let i=0
    
    
    change.style.transition = `width ${duration}ms, color ${duration/2}ms`;
    change.innerHTML = `<span>${change.textContent}</span>`;
    change.style.width = change.offsetWidth + 'px';
    
    setInterval(async ()=>{
    
      change.style.animation = '';
      const prevTitle = change.innerText;
      i=(i + 1)%title.length;
      change.style.display='inline';
      change.innerText=title[i];
      const widthTo = change.offsetWidth;
      change.style.display='inline-block';
      change.innerText=prevTitle;
      
      await new Promise(r=>requestAnimationFrame(r));
      console.log(widthTo);
      change.style.width = widthTo + 'px';
      change.style.animation =`fade ${duration}ms`;
    
      setTimeout(()=>{
         change.innerHTML = `<span>${title[i]}</span>`;
      },duration/2);
    
    },3000)
    #change{
      display:inline-block;
      box-sizing: border-box;
      vertical-align:middle;
      padding: 0 0.5rem;
      background-color:red;
      border-radius:3px;
      color:white;
      overflow: hidden;
      text-align:center;
      white-space:nowrap;
    
    }
    #change span {
      margin: 0 -100%;
    }
    
    @keyframes fade {
      0%,100% { color: white; }
      50% { color: transparent; }
    }
    It's <span id="change">a bird!</span>
    Login or Signup to reply.
  3. To achieve a smooth transition effect with the changing text length, you can utilize CSS keyframe animations instead of manipulating the transition-duration property. Here’s an updated version of your code that includes the width transition:

    const change = document.querySelector("#change");
        const title = ["a plane!", "Superman!", "a bird!"];
        let i = 0;
    
        setInterval(() => {
          change.classList.add("fadeout");
    
          setTimeout(() => {
            change.classList.remove("fadeout");
            change.innerText = title[i];
            i = (i + 1) % title.length;
          }, 500); 
    
        }, 2000); 
    #change {
      padding: 0 0.5rem;
      background-color: red;
      border-radius: 3px;
      color: white;
      animation: fade 4s linear infinite;
      opacity: 0; /* Add initial opacity */
    }
    
    @keyframes fade {
      0%, 20% { opacity: 0; } /* Start and end with opacity 0 */
      25%, 75% { opacity: 1; } /* Fully visible */
      80%, 100% { opacity: 0; } /* Fade out */
    }
    
    .fadeout {
      animation: none !important;
    }
    It's <span id="change">a bird!</span>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search