Animación de Landing con Intersection Observer

Animación con CSS aplicando la API de JavaScript Intersection Observer para iniciar la animación cuando el elemento a Animar entra dentro del Viewport
javascript,html,css
Slides usando Blend Mode y Document Fragment

He creado una Landing a la que he añadido animación mediante keyframes de CSS. La animación se inicia cuando el elemento entra dentro del viewport

En este Pen se puede ver funcionando. El inicio de la animación no es del todo fino por estar dentro de Codepen. Recomiendo descargar del GitHub (link más abajo) y probarlo

No voy a comentar todo el código, sólo algunas de las cosas que me parecen más interesantes

Intersection Observer

Cuando el DOM está cargado, se seleccionan todos los nodos que tengan el atributo [data-animation] para aplicarles IntersectionObserver

El valor que tengamos de dicho atributo será usado para añadirlo con clase y así iniciar la animación cuando el elemento sea visible un 70%. La animación la queremos aplicar sólo una vez y por eso aplicamos el unobserve sobre el elemento.

document.addEventListener('DOMContentLoaded', () => {
  //...

  const blocks = document.querySelectorAll("[data-animation]");

  const io = new IntersectionObserver(ioHandler, ioConfig);

  [].forEach.call(blocks, block => io.observe(block));

});

const ioHandler = (entries, self) => {
  for (let entry of entries) {
    const target = entry.target;

    if (entry.intersectionRatio > 0.7) {
      target.classList.add(target.getAttribute("data-animation"))
      self.unobserve(target);
    }
  }
}

const ioConfig = {
  threshold: 0.7
};

Animación en entrada de letras

La animación, tipo máquina de escribir, se realiza transformando mediante JS el contenido de un <div> para tener cada letra envuelta con un <span> al que se le aplica variable CSS para aplicar delays al keyframes

Para usarlo, sólo tenemos que añadir el atributo [data-split-word] allá donde queramos aplicar la animación


document.addEventListener('DOMContentLoaded', () => {

  let splits = document.querySelectorAll('[data-split-word]');

  splits.forEach(split => {
    let splitTextContent = split.textContent;

    split.innerHTML = '';
    split.appendChild(createFrameSlides(splitTextContent))
  })

  //...

});

const elFactory = (type, attributes, ...children) => {
  const el = document.createElement(type)

  for (key in attributes) {
    el.setAttribute(key, attributes[key])
  }

  children.forEach(child => {
    if (typeof child === 'string') el.appendChild(document.createTextNode(child))
    else el.appendChild(child)
  })

  return el
}

const createFrameSlides = chars => {
  const fragment = new DocumentFragment();
  chars = chars.split('');
  chars.forEach((char, index) => {
    const el = elFactory(
      'span',
      {
        'data-char': `${char}`,
        class: `char`,
        style: `--char-index:${index}`
      },
      `${char}`
    )
    fragment.appendChild(el);
  })

  return fragment;
}
[data-animation="kf-words"] {
  span {
    color: transparent;
  }
  &.kf-words {
    span {
      animation: words 200ms ease calc(var(--char-index) * 50ms) forwards;
    }
  }
}

@keyframes words {
	0% {
    color: transparent;
	}
	100% {
    color: #fff;
	}
}

Aplicar Delays a las animaciones que se deseen

Consiste en añadir variables CSS expresados en milisegundos, que luego será usado en el CSS

<ul class="nav__menu">
  <li data-animation="kf-left-to-right" style="--kf-delay: 150"><a href="#!">Works</a></li>
  <li data-animation="kf-left-to-right" style="--kf-delay: 300"><a href="#!">Features</a></li>
  <li data-animation="kf-left-to-right" style="--kf-delay: 450"><a href="#!">Security</a></li>
  <li data-animation="kf-left-to-right" style="--kf-delay: 600"><a href="#!">Contact</a></li>
</ul>

Quería que sólo fuera válido para pantallas superiores a 576px. Pero se podrían haber creado más variables CSS para cada viewport que interese

@media (min-width: 576px) {
  [data-animation][style*="--kf-delay"] {
    animation-delay: calc(var(--kf-delay) * 1ms);
  }
}

Código en mi GitHub