01

anim.js typo

Replay

Replay

mode : letters
delay : 000
"Personne te regarde comme le handball"
anim duration : 400
step delay : 30
mode : words
delay : 300
"Personne te regarde comme le handball"
anim duration : 600
step delay : 60
mode : lines
delay : 600
"Personne te regarde
comme le handball"
anim duration : 800
step delay : 60
02

attributes

data-letter-animate = "true"
activates the animation
data-animate-mode= "letters", "words", "lines"
type of animation (by letters, words, or lines)
data-delay ="800"
global delay before the animation starts (in ms)
data-offset= "30"
offset for starting animation (in %)
data-step-delay= "60"
delay between each letter/word/line (in ms)
data-anim-duration= "800"
duration of animation (in ms)
03

code


<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.2/anime.min.js" defer></script>
  

javascipt

Copy

Copy


<script defer>
document.addEventListener('DOMContentLoaded', function() {
  var animatedElements = document.querySelectorAll('[data-letter-animate="true"]');

  animatedElements.forEach(function(element) {
    const mode            = element.getAttribute('data-animate-mode')     || 'letters';
    const delayAttr       = parseInt(element.getAttribute('data-delay'))          || 0;
    const stepDelayAttr   = parseInt(element.getAttribute('data-step-delay'))   || 50;
    const animDurationAttr= parseInt(element.getAttribute('data-anim-duration'))|| 1000;
    const offsetAttr      = parseFloat(element.getAttribute('data-offset'))        || 0;

    // Préparation du texte
    if (mode === 'letters' || mode === 'words') {
      element.innerHTML = element.innerHTML
        .replace(/(^|<\/?[^>]+>|\s+)([^\s<]+)/g, '$1<span class="letter-word">$2</span>');
      const wordSpans = element.querySelectorAll('.letter-word');
      wordSpans.forEach(function(wordSpan) {
        if (mode === 'letters') {
          wordSpan.innerHTML = wordSpan.textContent
            .replace(/\S/g, "<span class='letter'>$&</span>");
        } else {
          wordSpan.classList.add('word');
        }
      });
    } else {
      const lines = element.innerHTML.trim()
        .split('<br>');
      element.innerHTML = lines
        .map(line => `<span class="line">${line}</span>`)
        .join('');
    }

    // Détection au scroll
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const target = entry.target;
          let targets = target.querySelectorAll(
            mode === 'letters' ? '.letter' :
            mode === 'words'   ? '.word'   : '.line'
          );

          anime({
            targets: targets,
            translateY: [100, 0],
            opacity:   [0, 1],
            easing:    'easeInOutQuad',
            duration:  animDurationAttr,
            delay:     (el, i) => delayAttr + stepDelayAttr * i,
            begin:     () => target.style.opacity = '1'
          });

          observer.unobserve(target);
        }
      });
    }, {
      threshold:  0,
      rootMargin: `${-offsetAttr}% 0px ${-offsetAttr}% 0px`
    });

    observer.observe(element);
  });
});
</script>
  

<style>
  [data-letter-animate] { 
    opacity: 0; 
  }

  .letter, .word, .line {
    display: inline-block;
    transform: translateY(100px);
    opacity: 0;
  }

  .letter-word {
    white-space: nowrap;
  }

  .line {
    display: block;
    overflow: hidden;
  }
</style>
  

contact me

Contact for brand, web
or motion