我使用交集观察者来添加和删除类,这样我就可以在视图中显示某些div时开始动画。我使用这个基本上是为了添加动画,就像这个网站在这里:https://crypto.com/us,请注意,当你滚动和某些部分显示动画开始,显示标题。
我的代码如下,工作正常:
useEffect(() => {
const titleElements = document.querySelectorAll('.title, .description, .text');
const options = {
root: null,
rootMargin: '0px',
threshold: .4
}
const callbacks = (entries: any) => {
entries.forEach((e: any) => {
if (e.isIntersecting && e.target.className === 'text') {
e.target.classList.add('text-animation');
} else if (e.isIntersecting && e.target.className !== 'text') {
e.target.classList.add('title-animation');
} else {
e.target.classList.remove('title-animation');
e.target.classList.remove('text-animation');
}
});
}
let observer = new IntersectionObserver(callbacks, options);
titleElements.forEach((e) => {
observer.observe(e);
})
}, [])
我还注意到我没有在另一个类中使用react useState。每次值改变时,下面的函数运行,返回一个或另一个类。注意:这个函数每次值改变时都会运行,当它发生时,组件会再次渲染,因为我将键值设置为总是不同的,所以如果这个函数两次或两次以上返回'current- pricup ',这个动画将总是在react每次渲染'new'组件时运行。
const handleUpdateEffect = (price: any, i: any) => {
if (prevList.length === 1) {
return ''
} else if (price > prevList[i].current_price) {
return 'current-price-up'
} else if (price < prevList[i].current_price) {
return 'current-price-down'
} else {
return 'current-price-keep'
}
}
一切都工作得很好,我的问题是:一旦我使用react是坏的做法添加和删除类直接在DOM中,即使我使用useEffect(类添加或删除后,一切都呈现)在第一个例子或在第二个例子中使用一个函数?
我应该为所有这些类应用useState吗?它会导致性能问题吗?我一直使用useState,但我发现这种方式使用'. classlist。add',它在这种情况下工作,我也意识到我在第二个例子中使用的是一个函数而不是状态,在什么情况下,什么方式是正确的?我应该总是使用useState来切换类来启动动画和添加效果吗?
TL;DR -是的,你不应该在使用React时使用element.classList
添加和删除classname
"proper"处理这种情况的方法实际上取决于你想要呈现元素的方式。
如果你想要添加动画的元素是完全不同且不相关的,你可以考虑将这个逻辑分离成某种组件或钩子,赋予它们各自的IntersectionObserver
和isAnimating
状态;比如:
const options = {
root: null,
rootMargin: '0px',
threshold: .4
}
function useAnimateOnScroll(element: HTMLElement | null) {
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
if (!element) return;
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) setIsAnimating(true);
else setIsAnimating(false);
}, options);
observer.observer(element);
}, [element]);
return isAnimating;
}
function MyAnimatingTitle() {
const [element, setElement] = useState<HTMLElement | null>(null);
const isAnimating = useAnimateOnScroll(element);
return (
<div ref={(ref) => {
// this is a way to get the reference to the actual DOM element
setElement(ref);
} className={`text ${isAnimating ? "text-animation" : ""}`}>My Title</div>
);
}
这个钩子将赋予每个组件/元素对自己动画的责任。从你的问题中我看到你担心性能,但我认为这是一个过早优化的例子。我几乎可以完全肯定,这种方法不会对应用程序的性能产生任何明显的影响;有更多的useState
调用不会使你的网页变慢。
如果元素是然而,相关的是,您可能希望映射到一些数组来呈现它们。然后,您可以将此数组用于IntersectionObserver
和相关的classname。
如果有多个元素你可以在使用useEffect
渲染后将其ref推入变量数组和setElement
以获得所有ref,这样做可以避免多次重新渲染,因为useAnimateOnScroll
函数中的useEffect每次都被调用setElement
。
useEffect(() => {
setElement([...elementArray]);
}, [])
然后在useAnimateOnScroll
函数中映射它以获得每个元素的观察者,作为参考,您可以使用键或id
function useAnimateOnScroll(element: Array<HTMLElement | null>) {
const [isAnimating, setIsAnimating] = useState<{[key: string]: boolean}>({});
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
console.log(entries);
entries.map((e: any) => {
const name = e.target.id
if (e.isIntersecting) {
setIsAnimating((prev) => {
return {
...prev, [name]: true,
}
});
} else {
setIsAnimating((prev) => {
return {
...prev, [name]: false,
}
})
}
})
}, options);
element.map((e: HTMLElement | null) => {
observer.observe(e!);
});
}, [element]);
console.log(isAnimating);
console.log(isAnimating['box1']);
return isAnimating;
}
然后为了访问这个观察你使用你之前设置的引用作为useAnimateOnScroll
函数的名称,你可以看到我使用的是id引用它们:
function MyAnimatingTitle() {
const [element, setElement] = useState<HTMLElement | null>(null);
const isAnimating = useAnimateOnScroll(element);
return (
<div id='title2' className={`title ${isAnimating['title2'] ?
'title-animation' : ''}`} ref={(ref) => {
elementArray.push(ref);
}}> This is the title 1</div>
<div id='descrip1' className={`title ${isAnimating['descrip1'] ?
'description-animation' : ''}`} ref={(ref) => {
elementArray.push(ref);
}}>This is the description</div>
<div id='title2' className={`title ${isAnimating['title2'] ?
'title-animation' : ''}`} ref={(ref) => {
elementArray.push(ref);
}}>This is 2nd title</div>
);
}
我弄清楚如何通过这个资源为多个元素做到这一点,我发现并适应了我的问题:https://betterprogramming.pub/react-useinview-hook-intersection-observer-animations-and-multiple-refs-73c68a33b5b1