让 TimelineLite 在 Gatsby 网站上只播放一次
Have TimelineLite only play once on Gatsby site
我在我的 Gatsby 网站上设置了一个 TimelineLite
时间轴,以便在用户导航到某个页面时为我的 hero
部分设置动画。但是,如果用户单击 link 到当前页面,即如果用户在主页上并单击 link 到主页,它将重新加载页面并触发时间线到 运行 再次。有没有办法确保我当前的 link 在 Gatsby 中处于非活动状态?
Hero.tsx
import React, { useEffect, useRef, useState } from 'react';
import css from 'classnames';
import { ArrowButton } from 'components/arrow-button/ArrowButton';
import { HeadingReveal } from 'components/heading-reveal/HeadingReveal';
import { gsap, Power2, TimelineLite } from 'gsap';
import { RichText } from 'prismic-reactjs';
import htmlSerializer from 'utils/htmlSerializer';
import { linkResolver } from 'utils/linkResolver';
import s from './Hero.scss';
gsap.registerPlugin(TimelineLite, Power2);
export const Hero = ({ slice }: any) => {
const linkType = slice.primary.link._linkType;
const buttonLink =
linkType === 'Link.document' ? slice.primary.link._meta : slice.primary.link.url;
const theme = slice.primary.theme;
const image = slice.primary.image;
const contentRef = useRef(null);
const headingRef = useRef(null);
const copyRef = useRef(null);
const buttonRef = useRef(null);
const [tl] = useState(new TimelineLite({ delay: 0.5 }));
useEffect(() => {
tl.to(contentRef.current, { css: { visibility: 'visible' }, duration: 0 })
.from(headingRef.current, { y: 65, ease: Power2.easeOut, duration: 1 })
.from(copyRef.current, { opacity: 0, y: 20, ease: Power2.easeOut, duration: 1 }, 0.5)
.from(buttonRef.current, { opacity: 0, y: 10, ease: Power2.easeOut, duration: 1 }, 1);
}, [tl]);
return (
<div
className={css(s.hero, s[theme])}
style={{
background: image ? `url(${image.url})` : 'white',
}}
>
<div className={s.hero__container}>
<div className={s.content__left} ref={contentRef}>
<HeadingReveal tag="h1" headingRef={headingRef}>
{RichText.asText(slice.primary.heading)}
</HeadingReveal>
<div className={s.content__copy} ref={copyRef}>
{RichText.render(slice.primary.copy, linkResolver, htmlSerializer)}
</div>
<div className={s.content__button} ref={buttonRef}>
<ArrowButton href={buttonLink}>{slice.primary.button_label}</ArrowButton>
</div>
</div>
</div>
</div>
);
};
如果您正在使用 built-in <Link>
组件进行导航,因为 @reach/router
不会触发也不会 re-renders在同一页面上导航时。那里没有补液。
如果您使用的是锚点 (<a>
),那么您正在刷新整个页面,因此您的所有组件都将被再次触发。
此外,另一种可能对您的情况有效的解决方法是使用具有空 deps ([]
) 的 useEffect
,从而产生 componentDidMount
效果。这样的话,由于页面没有重新加载,所以不会再次触发:
const [tl] = useState(new TimelineLite({ delay: 0.5 }));
useEffect(() => {
tl.to(contentRef.current, { css: { visibility: 'visible' }, duration: 0 })
.from(headingRef.current, { y: 65, ease: Power2.easeOut, duration: 1 })
.from(copyRef.current, { opacity: 0, y: 20, ease: Power2.easeOut, duration: 1 }, 0.5)
.from(buttonRef.current, { opacity: 0, y: 10, ease: Power2.easeOut, duration: 1 }, 1);
return () => unmountFunction() // unmount to avoid infinite triggers
}, []);
注意:由于我不知道您的要求,您可能需要再进行一些调整才能使代码正常工作。这个想法是删除 t1
的依赖以绕过这个问题。
为避免无限触发,您可能还需要使用 return () => unmountFunction()
卸载组件。
我在我的 Gatsby 网站上设置了一个 TimelineLite
时间轴,以便在用户导航到某个页面时为我的 hero
部分设置动画。但是,如果用户单击 link 到当前页面,即如果用户在主页上并单击 link 到主页,它将重新加载页面并触发时间线到 运行 再次。有没有办法确保我当前的 link 在 Gatsby 中处于非活动状态?
Hero.tsx
import React, { useEffect, useRef, useState } from 'react';
import css from 'classnames';
import { ArrowButton } from 'components/arrow-button/ArrowButton';
import { HeadingReveal } from 'components/heading-reveal/HeadingReveal';
import { gsap, Power2, TimelineLite } from 'gsap';
import { RichText } from 'prismic-reactjs';
import htmlSerializer from 'utils/htmlSerializer';
import { linkResolver } from 'utils/linkResolver';
import s from './Hero.scss';
gsap.registerPlugin(TimelineLite, Power2);
export const Hero = ({ slice }: any) => {
const linkType = slice.primary.link._linkType;
const buttonLink =
linkType === 'Link.document' ? slice.primary.link._meta : slice.primary.link.url;
const theme = slice.primary.theme;
const image = slice.primary.image;
const contentRef = useRef(null);
const headingRef = useRef(null);
const copyRef = useRef(null);
const buttonRef = useRef(null);
const [tl] = useState(new TimelineLite({ delay: 0.5 }));
useEffect(() => {
tl.to(contentRef.current, { css: { visibility: 'visible' }, duration: 0 })
.from(headingRef.current, { y: 65, ease: Power2.easeOut, duration: 1 })
.from(copyRef.current, { opacity: 0, y: 20, ease: Power2.easeOut, duration: 1 }, 0.5)
.from(buttonRef.current, { opacity: 0, y: 10, ease: Power2.easeOut, duration: 1 }, 1);
}, [tl]);
return (
<div
className={css(s.hero, s[theme])}
style={{
background: image ? `url(${image.url})` : 'white',
}}
>
<div className={s.hero__container}>
<div className={s.content__left} ref={contentRef}>
<HeadingReveal tag="h1" headingRef={headingRef}>
{RichText.asText(slice.primary.heading)}
</HeadingReveal>
<div className={s.content__copy} ref={copyRef}>
{RichText.render(slice.primary.copy, linkResolver, htmlSerializer)}
</div>
<div className={s.content__button} ref={buttonRef}>
<ArrowButton href={buttonLink}>{slice.primary.button_label}</ArrowButton>
</div>
</div>
</div>
</div>
);
};
如果您正在使用 built-in <Link>
组件进行导航,因为 @reach/router
不会触发也不会 re-renders在同一页面上导航时。那里没有补液。
如果您使用的是锚点 (<a>
),那么您正在刷新整个页面,因此您的所有组件都将被再次触发。
此外,另一种可能对您的情况有效的解决方法是使用具有空 deps ([]
) 的 useEffect
,从而产生 componentDidMount
效果。这样的话,由于页面没有重新加载,所以不会再次触发:
const [tl] = useState(new TimelineLite({ delay: 0.5 }));
useEffect(() => {
tl.to(contentRef.current, { css: { visibility: 'visible' }, duration: 0 })
.from(headingRef.current, { y: 65, ease: Power2.easeOut, duration: 1 })
.from(copyRef.current, { opacity: 0, y: 20, ease: Power2.easeOut, duration: 1 }, 0.5)
.from(buttonRef.current, { opacity: 0, y: 10, ease: Power2.easeOut, duration: 1 }, 1);
return () => unmountFunction() // unmount to avoid infinite triggers
}, []);
注意:由于我不知道您的要求,您可能需要再进行一些调整才能使代码正常工作。这个想法是删除 t1
的依赖以绕过这个问题。
为避免无限触发,您可能还需要使用 return () => unmountFunction()
卸载组件。