I want to make a text animation like this website: https://www.viraly.com.br/ They have 3 keyframes for two types of animation Left to Right and Right to Left RTL start with 164vh LTR start with -200vh
When those texts are visible the animation starts base on scroll
You can check the final result here: https://hartech.vercel.app/ But I want the same text animation as this website https://www.viraly.com.br/
Can someone provide me some tutorial or help me with the code to achieve that?
This is what I have tried on NextJs component:
export function HartechAnimated() {
if (typeof window !== "undefined") {
const animateElementRTL = document.querySelectorAll('.animateRTL');
const animateElementLTR = document.querySelectorAll('.animateLTR');
function isScrolledIntoView(elementsToBeAnimated) {
elementsToBeAnimated.forEach((element) => {
const rect = element.getBoundingClientRect();
const elemTop = rect.top;
const elemBottom = rect.bottom;
// Only completely visible elements return true:
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
if (isVisible && element.classList.contains('animateRTL')) {
element.classList.add('resetXRTL')
} else {
element.classList.remove('resetXRTL')
}
if (isVisible && element.classList.contains('animateLTR')) {
element.classList.add('resetXLTR')
} else {
element.classList.remove('resetXLTR')
}
});
}
window.addEventListener('scroll', () => {
isScrolledIntoView(animateElementRTL);
isScrolledIntoView(animateElementLTR);
}, false);
}
return (
<div className="font-britanica-black-expanded overflow-hidden text-6xl text-white py-20 md:text-black md:text-9xl md:py-36">
<b className="block animateLTR" id="teste">HARTECH REPARO</b>
<b className="block hartech-stroke animateRTL">DE PLACAS DE VÍDEO</b>
<b className="block animateLTR">HARTECH REPARO</b>
</div >
);
}
CSS
.animateRTL {
transition: transform 2.2s ease-in-out;
transform: translateX(164vh);
}
.animateLTR {
transition: transform 2s ease-in-out;
transform: translateX(-200vh);
}
.resetXRTL{
transform: translateX(0);
}
.resetXLTR{
transform: translateX(0);
}
CodePudding user response:
Honestly rather than writing all that code to check if the element is in the viewport, you'd be better served using an IntersectionObserver (much less painful). https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
const animateElementRTL = Array.from(document.querySelectorAll('.animateRTL'));
const animateElementLTR = Array.from(document.querySelectorAll('.animateLTR'));
const animateOnEntry = (entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio === 0) {
// completely out of viewport, stop animation
if (entry.target.classList.contains('animateRTL')) {
entry.target.classList.remove('resetXRTL');
} else {
entry.target.classList.remove('resetXLTR');
}
} else if (entry.intersectionRatio === 1) {
// fully visible in viewport, start animation
if (entry.target.classList.contains('animateRTL')) {
entry.target.classList.add('resetXRTL');
} else {
entry.target.classList.add('resetXLTR');
}
}
});
}
const rtlObservers = animateElementRTL.map((elem) => {
const observer = new IntersectionObserver(animateOnEntry, {
root: null,
rootMargin: "0px",
threshold: [0, 1]
});
observer.observe(elem);
return observer;
});
const ltrObservers = animateElementLTR.map((elem) => {
const observer = new IntersectionObserver(animateOnEntry, {
root: null,
rootMargin: "0px",
threshold: [0, 1]
});
observer.observe(elem);
return observer;
});
CodePudding user response:
Thanks for the answer however I still not having the same animation as this website: https://www.viraly.com.br/ They have like a keyframe based on scroll. The animation that I have is fixed, there's only one animation.
Check this: https://i.imgur.com/ImsImbk.mp4
This is your code fixed:
const animateElementRTL = Array.from(document.querySelectorAll('.animateRTL'));
const animateElementLTR = Array.from(document.querySelectorAll('.animateLTR'));
const animateOnEntry = (entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio === 0) {
// completely out of viewport, stop animation
if (entry.target.classList.contains('animateRTL')) {
entry.target.classList.remove('resetXRTL');
}
if (entry.target.classList.contains('animateLTR')) {
entry.target.classList.remove('resetXLTR');
}
} else {
// fully visible in viewport, start animation
if (entry.target.classList.contains('animateRTL')) {
entry.target.classList.add('resetXRTL');
}
if (entry.target.classList.contains('animateLTR')) {
entry.target.classList.add('resetXLTR');
}
}
});
}
const rtlObservers = animateElementRTL.map((elem) => {
const observer = new IntersectionObserver(animateOnEntry, {
root: null,
rootMargin: "0px",
threshold: [0, 1]
});
observer.observe(elem);
return observer;
});
const ltrObservers = animateElementLTR.map((elem) => {
const observer = new IntersectionObserver(animateOnEntry, {
root: null,
rootMargin: "0px",
threshold: [0, 1]
});
observer.observe(elem);
return observer;
});
}