diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 9a45d90..61c6780 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -316,6 +316,66 @@ export function readBlockConfig(block) { return config; } +/** + * adds animation on metadata level + */ +export function addAnimation() { + const addClass = (elem, className) => { + elem.classList.add(className); + }; + + const animationTypeMeta = getMetadata('animation'); + + const elementsToAnimate = document.querySelectorAll('h1, h2, h3, h4, h5, h6, a, img'); + + const applyAnimation = (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && entry.intersectionRatio >= 0.3) { + switch (animationTypeMeta) { + case 'up': + addClass(entry.target, 'animated-up'); + break; + case 'down': + addClass(entry.target, 'animated-down'); + break; + case 'left': + addClass(entry.target, 'animated-left'); + break; + case 'right': + addClass(entry.target, 'animated-right'); + break; + default: + break; + } + // eslint-disable-next-line no-use-before-define + observer.unobserve(entry.target); // Stop observing once animation is applied + } + }); + }; + + const observer = new IntersectionObserver(applyAnimation, { threshold: 0.3 }); + + elementsToAnimate.forEach((element) => { + observer.observe(element); + }); + + // Observe the body for changes + const bodyObserver = new MutationObserver(() => { + elementsToAnimate.forEach((element) => { + observer.observe(element); + }); + }); + + bodyObserver.observe(document.body, { childList: true, subtree: true }); + + // Observe the window for scrolling + window.addEventListener('scroll', () => { + elementsToAnimate.forEach((element) => { + observer.observe(element); + }); + }); +} + /** * Decorates all sections in a container element. * @param {Element} $main The container element @@ -650,6 +710,8 @@ function init() { window.addEventListener('error', (event) => { sampleRUM('error', { source: event.filename, target: event.lineno }); }); + + addAnimation(); } init(); diff --git a/styles/styles.css b/styles/styles.css index beb751c..958dff7 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -653,3 +653,99 @@ main .section.text-white { white-space: nowrap; width: 1px; } + +/* animations */ + +@keyframes fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes animated-up { + 0% { + opacity: 0; + transform: translateY(20px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes animated-down { + 0% { + opacity: 0; + transform: translateY(-20px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes animated-right { + 0% { + opacity: 0; + transform: translateX(-20px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes animated-left { + 0% { + opacity: 0; + transform: translateX(20px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +/* animations written on metadata level */ + +.animated-up { + animation: animated-up 0.5s ease-in-out forwards; +} + +.animated-down { + animation: animated-down 0.5s ease-in-out forwards; +} + +.animated-right { + animation: animated-right 0.5s ease-in-out forwards; +} + +.animated-left { + animation: animated-left 0.5s ease-in-out forwards; +} + +/* animations written on section-metadata level */ + + .section-animated-up { + animation: animated-up 0.5s ease-in-out forwards; +} + + .section-animated-down { + animation: animated-down 0.5s ease-in-out forwards; +} + + .section-animated-right { + animation: animated-right 0.5s ease-in-out forwards; +} + + .section-animated-left { + animation: animated-left 0.5s ease-in-out forwards; +}