Animar elementos html al hacer scroll con Intersection Observer
La API Observador de Intersección provee una vía asíncrona para observar cambios en la intersección de un elemento con un elemento ancestro o con el viewport del documento de nivel superior.
→ Ir a la Página oficial de La API Observador
¿Qué podemos hacer con la API Intersection Observer?
- Implementar lazy loading: cargar imágenes y recursos sólo cuando el usuario está a punto de ocuparlos, mejorando así el performance de nuestra web.
- Mostrar una barra de progreso de lectura en una publicación.
- Reproducir/pausar un video dependiendo de si es visible para el usuario.
- Contar las veces que un usuario vio un elemento (útil para banners publicitarios).
- Scrolling infinito.
- Animaciones tipo scroll animations.
Implementación
Instanciar el observer
Lo primero que debemos hacer es instanciar el observer:
const observer = new IntersectionObserver(callback, options);
Miremos en detalle los 2 parámetros que recibe el constructor.
Callback
Cada vez que Intersection Observer es invocado , la función callback es invocada.
Esta función recibe una lista de objetos de tipo IntersectionObserverEntry y el observer.
function callback(entries, observer) { }
entries = Se puede observar mas de un elemento
A la hora de invocarlo
const observer = new IntersectionObserver(cualquier_nombre , options)
options
options de Intersection Observer , son las que definen cuando la función Callback se va a llamar.
El objeto options
pasado al constructor IntersectionObserver()
(en-US) le deja controlar las circunstancias bajo las cuales la función callback es invocada.
El parametro options recibe 3 opciones las unicas que recibe Intersection Observer:
root
rootMargin
threshold
const options = {
// root: document.querySelector('body'),
rootMargin: '0px 0px 0px 0px',
threshold: .5,
}
root
root va ser el elemento html que vamos a estar escuchando y donde vamos a estar esperando que se muestre o no se muestre , por defecto va a tomar el viewport es decir todo el area visible. Y si deseamos no lo configuramos.
El elemento que es usado como viewport para comprobar la visibilidad de elemento target. Debe ser ancestro de target. Por defecto es el viewport del navegador si no se especifica o si es null
.
rootMargin
Margen alrededor del elemento root. Puede tener valores similares a los de CSS margin
property, e.g. "10px 20px 30px 40px"
(top, right, bottom, left). Los valores pueden ser porcentajes. Este conjunto de valores sirve para aumentar o encoger cada lado del cuadro delimitador del elemento root antes de calcular las intersecciones. Por defecto son todos cero.
threshold
Es un número o un array de números que indican a que porcentaje de visibilidad del elemento target,
Aqui definimos en que nivel deseamos que se ejecute la función callback del observer.
Si usted quiere que se detecte cuando la visibilidad pasa la marca del 50%, debería usar un valor de 0.5.
Si quiere ejecutar la función callback cada vez que la visibilidad pase otro 25%, usted debería especificar el array [0, 0.25, 0.5, 0.75, 1].
El valor por defecto es 0 (lo que significa que tan pronto como un píxel sea visible, la función callback será ejecutada).
Un valor de 1 significa que el umbral no se considera hasta que todos los pixels sean visibles es decir se muestre completo el elemento.
Definir a quien vamos a Observar
Una vez que instanciamos el observer vamos a decirle a quien vamos a observar:
<body>
<video id="video" muted src=""></video>
</body>
Codigo Script
const $video = document.querySelector('#video')
const observer = new IntersectionObserver(callback, options)
//definimos a quien vamos a observar en este caso al elemento video
observer.observe($video)
Casos de Uso
Reproducir video automaticamente con Api Intersection Observer
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<video id="video" muted width="320"
src="https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"></video>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</body>
</html>
Codigo Script
const $video = document.querySelector('#video')
$video.addEventListener('play', ()=>{
console.log('se reproduce el video')
})
$video.addEventListener('pause', () => {
console.log('se pausa el video')
})
const options = {
// root: document.querySelector('body'),
rootMargin: '0px 0px 0px 0px',
threshold: .5,
}
function callback(entries, observer) {
console.log('se llamó al callback')
if (entries[0].isIntersecting) {
$video.play()
} else {
$video.pause()
}
}
const observer = new IntersectionObserver(callback, options)
observer.observe($video)
Ejemplo animación div deslizantes con Api Intersection Observer
Referencia:
Animaciones al Scrollear con Intersection Observer | Javascript
https://www.youtube.com/watch?v=cVsqA4NhDoI
Animaciones CSS lentas o con saltos cuando se usa la API de Intersection Observer
Codigo Html
<div id="caja_hosting_1" class="ctn_linux_porta">
</div>
<div id="caja_hosting_2" class="ctn_linux_porta">
</div>
<div id="caja_hosting_3" class="ctn_linux_porta">
</div>
<div id="caja_hosting_4" class="ctn_linux_porta">
</div>
Codigo Script
/* const $caja1= document.querySelector('#caja_hosting_1') */
const $caja1= document.getElementById ('caja_hosting_1')
const options = {
// root: document.querySelector('body'),
root: null,
rootMargin: '0px 0px 0px 0px',
//threshold:
//define en que momento se ejecuta la function callback
//El valor por defecto es 0 (lo que significa que tan pronto como un píxel sea visible)
//Un valor de 1 significa cuando muestre completo el elemento.
threshold: 0,
}
function callback(entradas,observador) {
entradas.forEach( (entradas) => {
/* if (entradas[0].isIntersecting) { */
// entradas.isIntersecting= si el elemento esta dentro del viewport
if (entradas.isIntersecting) {
/* console.log ('La imagen esta en el viewport') */
//console.log (entradas)
//console.log (observador)
entradas.target.classList.add('animate__animated','caja_animation_one');
//entradas.target.style.setProperty('--animate-duration', '0.5s');
} else {
entradas.target.classList.remove('animate__animated','caja_animation_one');
}
});
}
const observer = new IntersectionObserver(callback, options)
observer.observe($caja1)
/* ---------------------------------------------------- */
const observer4 = new IntersectionObserver(entries => {
// Loop over the entries
entries.forEach(entry => {
// If the element is visible
if (entry.isIntersecting) {
// Add the animation class
entry.target.classList.add('animate__animated','caja_animation_four', entry.isIntersecting);
} else {
entry.target.classList.remove('animate__animated','caja_animation_four');
}
});
});
observer4.observe(document.querySelector('#caja_hosting_4'));
/* ---------------------------------------------------- */
const observer2 = new IntersectionObserver(entries => {
// Loop over the entries
entries.forEach(entry => {
// If the element is visible
if (entry.isIntersecting) {
// Add the animation class
entry.target.classList.add('animate__animated','caja_animation_two');
//entradas.target.style.setProperty('visibility', 'visible');
//entradas.target.style.visibility = "visible";
} else {
entry.target.classList.remove('animate__animated','caja_animation_two');
}
});
});
observer2.observe(document.querySelector('#caja_hosting_2'));
/* ---------------------------------------------------- */
const observer3 = new IntersectionObserver(entries => {
// Loop over the entries
entries.forEach(entry => {
// If the element is visible
if (entry.isIntersecting) {
// Add the animation class
entry.target.classList.add('animate__animated','caja_animation_three');
//entradas.target.style.setProperty('visibility', 'visible');
//entradas.target.style.visibility = "visible";
} else {
entry.target.classList.remove('animate__animated','caja_animation_three');
}
});
});
observer3.observe(document.querySelector('#caja_hosting_3'));
Codigo css
/*----------Animacion de las cajas -----------------------------*/
.caja_animation_one {animation-name: fadeInLeft;}
/* ------------------------------- */
.caja_animation_four {animation-name: fadeInLeft;}
/* ------------------------------- */
.caja_animation_two {animation-name: fadeInLeft;}
.caja_animation_three { animation-name: fadeInLeft;}
.caja_animation_one , .caja_animation_two ,
.caja_animation_three , .caja_animation_four {
animation-delay: 0ms!important;
animation-duration: 1500ms!important;
}
Ejemplo animación div deslizantes con Api Intersection Observer con clases iguales
Codigo html
<div class="woocommerce columns-4">
<ul class="products columns-4">
<li class="product-category product first animate__animated caja_animation_one">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Puertas de Madera
</h2>
</a></li>
<li class="product-category product animate__animated caja_animation_one">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Ventanas de Madera
</h2>
</a></li>
<li class="product-category product animate__animated caja_animation_one">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Reposteros de madera
</h2>
</a></li>
<li class="product-category product last animate__animated caja_animation_one">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Muebles de Madera
</h2>
</a></li>
<li class="product-category product first">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Drywall
</h2>
</a></li>
<li class="product-category product">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Melamines
</h2>
</a></li>
<li class="product-category product">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Estructuras Metalicas
</h2>
/a></li>
<li class="product-category product last">
<a href="#"><img src="ruta_imagen" width="300" height="300">
<h2 class="woocommerce-loop-category__title">
Cromado y Zincado
</h2>
</a></li>
</ul>
</div>
Codigo Javascript
document.addEventListener("DOMContentLoaded", function(){
const $caja1= document.querySelectorAll('.product-category')
const options = {
// root: document.querySelector('body'),
root: null,
rootMargin: '0px 0px 0px 0px',
//threshold:
//define en que momento se ejecuta la function callback
//El valor por defecto es 0 (lo que significa que tan pronto como un píxel sea visible)
//Un valor de 1 significa cuando muestre completo el elemento.
threshold: 0,
}
function callback(entradas,observador) {
entradas.forEach( (entradas) => {
/* if (entradas[0].isIntersecting) { */
// entradas.isIntersecting= si el elemento esta dentro del viewport
if (entradas.isIntersecting) {
/* console.log ('La imagen esta en el viewport') */
//console.log (entradas)
//console.log (observador)
entradas.target.classList.add('animate__animated','caja_animation_one');
//entradas.target.style.setProperty('--animate-duration', '0.5s');
} else {
entradas.target.classList.remove('animate__animated','caja_animation_one');
}
});
}
const observer = new IntersectionObserver(callback, options)
// observer.observe($caja1)
// loop
$caja1.forEach( xvariable => {
observer.observe(xvariable);
});
});
Referencia
Error al visualizar la web y ver en la consola
Uncaught TypeError: Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'
Respuesta:
El elemento devuelve un valor nulo porque el script se ejecuta antes de que se analice el HTML. usar defer en la etiqueta de script para evitar este problema.
script src='script.js' defer>
script
o tambien podemos asegurarnos de que el script IntersectionObserver se ejecute después de que el DOM esté completamente cargado.
Cuando usamos WordPress nuestro script lo carga al final de la pagina , para evitar eso escribimos el siguiente codigo
document.addEventListener("DOMContentLoaded", function(){
// your instersectionobserver code here
});
.nombre_de_la_clase {animation-name: fadeInUp;}
Fuente:
→ FalconMasters = Animaciones al Scrollear con Intersection Observer | Javascript
: https://www.youtube.com/watch?v=cVsqA4NhDoI
→ Leonidas Esteban = Intersection Observer
https://www.youtube.com/watch?v=Mm9R1Z5B31s
→ Aprende a usar Intersection Observer API :
https://www.youtube.com/watch?v=CvXHedd3Z7w
→ Animaciones CSS lentas o con saltos cuando se usa la API de Intersection Observer
Domine la API Intersection Observer - Navegación de una sola página (Parte 1)
https://www.hweaver.com/intersection-observer-single-page-navigation/