Hola y gracias de antemano por leer mi pregunta.
OBJETIVO: configurar la imagen para que, una vez que se desplace a la vista, cambie sin problemas a una posición establecida, pero aún reaccione al :hover . Usando @keyframes y un poco de JavaScript, establecí la imagen en opacity: 0 y su opacidad final en opacity: .85 . Luego agregué un efecto de desplazamiento en CSS para que sea opacity: 1
El problema es que una vez que termina con su transición, desaparece, volviendo a su opacidad original, que es cero. Logré congelarlo en .85 con animation-fill-mode: forwards , en lugar de animation-fill-mode: none , pero luego no responderá a :hover
Y aquí hay un fragmento de prueba del problema en acción:
let observer_img = new IntersectionObserver(updates => { updates.forEach(update => { if (update.isIntersecting) { update.target.classList.add('shift_frame_center_img'); } else { update.target.classList.remove('shift_frame_center_img'); } }); }, { threshold: 0 }); [...document.querySelectorAll('.features-img-wrapper img')].forEach(element => observer_img.observe(element)); body { width: 100%; height: 100vh; background-color: lightgrey; } /* CHILD */ .features-img-wrapper img { width: 10rem; display: block; margin-left: auto; margin-right: auto; margin-top: 8rem; opacity: 0; transition: all .5s; } /* APPEND-CHILD */ .shift_frame_center_img { animation: center_img 1s 0.5s none; } /* CHILD ON HOVER */ .features-img-wrapper img:hover { opacity: 1; transform: scale(1.035); } /* KEYFRAMES */ @keyframes center_img { 0% { transform: translateY(20rem); } 100% { transform: translateY(0); opacity: .85; } } <body> <div class="features-img-wrapper"> <img src="https://synapse.it/wp-content/uploads/2020/12/test.png"> </div> </body>Si pudiera echarme una mano con esto, sería maravilloso, soy un poco principiante y ya he pasado algunas horas en esto, todos los comentarios son bienvenidos. Muchísimas gracias.
Para comprender por qué el efecto de desplazamiento no funcionaba con el animation-fill-mode: forwards , lea esta respuesta .
Puede solucionarlo agregando la propiedad !important a los estilos de desplazamiento:
.features-img-wrapper img:hover { opacity: 1 !important; transform: scale(1.035) !important; }El problema, en este caso, es que la transición no funcionará para hover.
Puede eliminar la animación por completo y agregar los estilos de estado final a la clase shift_frame_center_img .
Pero aún necesitaría usar la propiedad !important debido a la especificidad de CSS .
let observer_img = new IntersectionObserver(updates => { updates.forEach(update => { if (update.isIntersecting) { update.target.classList.add('shift_frame_center_img'); } else { update.target.classList.remove('shift_frame_center_img'); } }); }, { threshold: 0 }); [...document.querySelectorAll('.features-img-wrapper img')].forEach(element => observer_img.observe(element)); body { width: 100%; height: 100vh; background-color: lightgrey; } /* CHILD */ .features-img-wrapper img { width: 10rem; transform: translateY(20rem); display: block; margin-left: auto; margin-right: auto; margin-top: 8rem; opacity: 0; transition: all .5s; } /* APPEND-CHILD */ .shift_frame_center_img { transform: none !important; opacity: .85 !important; } /* CHILD ON HOVER */ .features-img-wrapper img:hover { opacity: 1 !important; transform: scale(1.035) !important; } <body> <div class="features-img-wrapper"> <img src="https://synapse.it/wp-content/uploads/2020/12/test.png"> </div> </body>Este fragmento elimina la necesidad de reenviar el modo de relleno configurando el img para que tenga opacidad 1 como su estado inicial, por lo que volverá a ese estado al final de la animación.
La animación en sí se modifica para tomar 1.5 segundos en lugar de 1 con el primer tercio simplemente configurando la opacidad de img en 0 para que no se pueda ver. Esto da el efecto de retardo.
let observer_img = new IntersectionObserver(updates => { updates.forEach(update => { if (update.isIntersecting) { update.target.classList.add('shift_frame_center_img'); } else { update.target.classList.remove('shift_frame_center_img'); } }); }, { threshold: 0 }); [...document.querySelectorAll('.features-img-wrapper img')].forEach(element => observer_img.observe(element)); body { width: 100%; height: 100vh; background-color: lightgrey; } /* CHILD */ .features-img-wrapper img { width: 10rem; display: block; margin-left: auto; margin-right: auto; margin-top: 8rem; opacity: 0; transition: all .5s; opacity: 1; } /* APPEND-CHILD */ .features-img-wrapper img { animation: center_img 1.5s 0s none; } /* CHILD ON HOVER */ .shift_frame_center_img:hover { opacity: 1; transform: translateY(0) scale(1.035); } /* KEYFRAMES */ @keyframes center_img { 0% { transform: translateY(20rem) scale(1); opacity: 0; } 33.33% { transform: translateY(20rem) scale(1); opacity: 0; } 100% { transform: translateY(0) scale(1); opacity: .85; } } <body> <div class="features-img-wrapper"> <img src="https://synapse.it/wp-content/uploads/2020/12/test.png"> </div> </body>Nota: como cada configuración de transformación restablecerá cualquier cosa que no esté incluida, tanto tranlateY como la escala se incluyen en cada configuración.
Fuera del sistema de fragmentos SO, era posible dejar intacta la configuración de la animación encadenando otra animación al frente que se ejecutaba durante 0,5 s y simplemente configuraba la imagen en opacidad: 0. Esto no funcionó en el sistema de fragmentos (entró en un bucle de parpadeo encendido y apagado), de ahí la introducción de una animación extendida.