Home > Blockchain >  Simple appearing animation
Simple appearing animation

Time:03-01

Just make a very very simple animation:

p {
  animation: appear 1s linear 1;
  font-family:monospace;
}

@keyframes appear {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
<p>Thank you for any helps!</p>

It works, but I am just thinking is it possible to make the first letter appear first, after the first letter appearing, then the second, and finally the last letter (now the whole content of p will appear at the same time)

Does CSS have any selectors to select every characters in the element and is it possible to achieve the effect use CSS only?

I only know first-letter and last-letter selector in css, but not sure if css has selector for every letters

If not only with CSS, I would be also Ok with JS solution?

Appreciate for any helps provided~

CodePudding user response:

Here's a vanilla JS approach. First we extract the text, empty the element, then rebuild it one character at a time.

let p = document.querySelector('.anim');
// store value
let t = p.innerText.split(''),
  counter = 0
p.innerHTML = '';

let inter = setInterval(() => {
  if (counter == t.length) clearInterval(inter)
  else p.insertAdjacentHTML('beforeend', `<span>${t[counter  ]}</span>`);
}, 100)
p.anim span {
  animation: appear 1s linear 1;
}

@keyframes appear {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
<p class='anim'>Thank you for any helps!</p>

CodePudding user response:

If a JS-based solution is acceptable, then you can easily achieve this using a combination of DOM manipulation (wrapping each non-space character in a <span> element), and then sequentially toggling a class that runs the animation.

The sequential part can be achieved by simply looping through the generated <span> element, and then awaiting for a promise to be resolved.

In your question it is not clear how you want to be a delay to be calculated: I simply assume you want an arbitrary/customizable delay. In this case, a simple promise-based sleep function can get the job done:

function sleep(ms = 0) {
  return new Promise(r => setTimeout(r, ms));
}

async function fadeInCharacters(el) {
  // NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
  el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
  for (const span of el.querySelectorAll('span[data-animate]')) {
    span.classList.add('animate');
    await sleep(100);
  }
}

fadeInCharacters(document.querySelector('p'));
.animate {
  animation: appear 1s ease-in-out 1 forwards;
}

span {
  opacity: 0;
}

@keyframes appear {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
<p>Thank you for any help!</p>

If you want to wait for each character before fading in the next, then change the await function into something that hooks into the animationend event, although I am inclined to believe this is not what you intend. Just putting it out here for the sake of completeness:

function onAnimationEnd(el) {
  return new Promise(r => el.addEventListener('animationend', () => r()));
}

async function fadeInCharacters(el) {
  // NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
  el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
  for (const span of el.querySelectorAll('span[data-animate]')) {
    span.classList.add('animate');
    await onAnimationEnd(span);
  }
}

fadeInCharacters(document.querySelector('p'));
.animate {
  animation: appear 1s ease-in-out 1 forwards;
}

span {
  opacity: 0;
}

@keyframes appear {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
<p>Thank you for any help!</p>

  • Related