<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;
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('');
}
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>