5 javascript css typescript reactjs intersection-observer
我这里有一个运行完美的文本动画。我现在要添加的是一个 Intersection Observer,这样动画只有在我向下滚动到 Box 时才开始。
所以我为实现这一目标所做的是:我使用了 React hookuseRef作为对我想要观察的元素的引用,并将其应用到我的 Box 中ref={containerRef}。然后声明一个回调函数,该函数接收 IntersectionObserverEntries 数组作为参数,在该函数内,我获取第一个也是唯一一个条目,并检查它是否与视口相交,如果是,则使用 Entry.isIntersecting 的值调用 setIsVisible (真假)。之后,我添加了反应钩子 useEffect 并使用回调函数和我之前创建的选项创建了一个观察者构造函数。我在一个新的钩子中实现了这个逻辑,我称之为useElementOnscreen
但是 Typescript 告诉我一个错误containerRef?.current:
Argument of type 'IntersectionObserver' is not assignable to parameter of type 'Element'.
Type 'IntersectionObserver' is missing the following properties from type 'Element': attributes, classList, className, clientHeight, and 160 more.
Run Code Online (Sandbox Code Playgroud)
而且我不知道如何解决这个错误。ref={containerRef}我认为这也是我抛出错误的原因
The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes & { component: ElementType<any>; } & SystemProps<Theme> & { children?: ReactNode; component?: ElementType<...> | undefined; ref?: Ref<...> | undefined; sx?: SxProps<...> | undefined; } & CommonProps & Omit<...>'
Run Code Online (Sandbox Code Playgroud)
动画: 因此,TopAnimateBlock 和 BottomAnimateBlock 具有 numOfLine 属性,因此块内有多少行。BottomAnimateBlock中的第二个属性是delayTopLine,它应该与TopAnimateBlock中的numOfLine具有相同的数字,因为我们需要等待顶部线播放。
TextAnimation.tsx
import { Box, Stack, Typography } from '@mui/material';
import React, { useRef, useEffect, useState } from 'react';
import styled, { keyframes } from 'styled-components';
const showTopText = keyframes`
0% { transform: translate3d(0, 100% , 0); }
40%, 60% { transform: translate3d(0, 50%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
const showBottomText = keyframes`
0% { transform: translate3d(0, -100%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
const Section = styled.section`
width: calc(100% + 10vmin);
display: flex;
flex-flow: column;
padding: 2vmin 0;
overflow: hidden;
&:last-child {
border-top: 1vmin solid white;
}
`;
const Block = styled.div<{ numOfLine: number }>`
position: relative;
`;
const TopAnimateBlock = styled(Block)`
animation: ${showTopText} calc(0.5s * ${props => props.numOfLine}) forwards;
animation-delay: 0.5s;
transform: translateY(calc(100% * ${props => props.numOfLine}));
`;
const BottomAnimateBlock = styled(Block)<{ delayTopLine: number }>`
animation: ${showBottomText} calc(0.5s * ${props => props.numOfLine}) forwards;
animation-delay: calc(0.7s * ${props => props.delayTopLine});
transform: translateY(calc(-100% * ${props => props.numOfLine}));
`;
const TextStyle = styled.p<{ color: string }>`
font-family: Roboto, Arial, sans-serif;
font-size: 12vmin;
color: ${props => props.color};
`;
const useElementOnScreen = (options) => {
const containerRef = useRef<IntersectionObserver | null>(null);
const [isVisible, setIsVisible] = useState(false);
const callbackFunction = (entries) => {
const [entry] = entries;
setIsVisible(entry.isIntersecting);
};
useEffect(() => {
const observer = new IntersectionObserver(callbackFunction, options);
if (containerRef.current) observer.observe(containerRef?.current);
return () => {
if (containerRef.current) observer.unobserve(containerRef?.current);
};
}, [containerRef, options]);
return [containerRef, isVisible];
};
export function Details() {
const [containerRef, isVisible] = useElementOnScreen({
root: null,
rootMargin: '0px',
threshold: 1.0,
});
return (
<>
<Typography>Scroll Down</Typography>
<Box ref={containerRef}>
<Section>
<TopAnimateBlock numOfLine={2}>
<TextStyle color="grey">mimicking</TextStyle>
<TextStyle color="white">apple's design</TextStyle>
</TopAnimateBlock>
</Section>
<Section>
<BottomAnimateBlock numOfLine={1} delayTopLine={2}>
<TextStyle color="white">for the win!</TextStyle>
</BottomAnimateBlock>
</Section>
</Box>
</>
);
};
Run Code Online (Sandbox Code Playgroud)
我可以在代码中大致找到两个问题:
首先是这样的声明:
const containerRef = useRef<IntersectionObserver | null>(null);
Run Code Online (Sandbox Code Playgroud)
通用的实现useRef是通过IntersectionObserver | null. IntersectionObserver这表明 ref 容器将保存或的实例null。但 是ref与元素一起使用Box(对于那些不熟悉材料 UI 的人来说,这将类似于div)。
该声明可以更改为:
const containerRef = useRef<HTMLDivElement | null>(null);
Run Code Online (Sandbox Code Playgroud)
其次,钩子的返回类型未声明,TS 自动通过查看返回的内容来检测它是否为数组 ( [containerRef, isVisible])。Typescript 推断的类型变为:
(boolean | React.MutableRefObject<HTMLDivElement | null>)[]。这意味着返回类型是一个元素数组,每个元素可以具有上述 3 种类型之一。
由于类型实际上是 atuple并且返回的数组元素的类型不同(我们事先知道),因此推断的类型不正确,并且 TS 会抱怨。
在定义钩子时明确声明这一点可以防止 Typescript 抱怨。
const useOnScreen = <T,>(options : T): [MutableRefObject<HTMLDivElement | null>, boolean] => {
const containerRef = useRef<HTMLDivElement | null>(null);
const [isVisible, setIsVisible] = useState(false);
const callbackFunction = (entries : : IntersectionObserverEntry[]) => {
const [entry] = entries;
setIsVisible(entry.isIntersecting);
};
useEffect(() => {
const observer = new IntersectionObserver(callbackFunction, options);
if (containerRef.current) observer.observe(containerRef?.current);
return () => {
if (containerRef.current) observer.unobserve(containerRef?.current);
};
}, [containerRef, options]);
return [containerRef, isVisible];
};
Run Code Online (Sandbox Code Playgroud)
解释元组返回类型和数组返回类型之间差异的链接。
| 归档时间: |
|
| 查看次数: |
3937 次 |
| 最近记录: |