React实现块依次从下到上进入的动画

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import React, { useEffect, useState } from 'react';
 
import Nav from './components/Nav';
import Footer from './components/Footer';
import About from './components/About';
import Product from './components/Product';
import Service from './components/Service';
import Value from './components/Value';
import JoinUs from './components/JoinUs';
import ContactUs from './components/ContactUs';
 
import './index.scss';
import { IntlProvider, addLocaleData } from 'react-intl';
import zh_CN from '@/locales/zh-CN';
import en_US from '@/locales/en-US';
 
const getLang = () => {
    const search = window.location.search;
    const params = new URLSearchParams(search);
    return params.get('lang') || 'en';
};
 
const App = () => {
    const [scrollTop, setScrollTop] = useState(0);
    const [locale, setLocale] = useState(getLang());
    const messages = {
        zh: zh_CN,
        en: en_US
    };
    const handleScroll = () => {
        const top =
            document.documentElement.scrollTop || document.body.scrollTop;
        setScrollTop(top);
    };
 
    useEffect(() => {
        handleScroll();
        window.addEventListener('scroll', handleScroll);
        return () => {
            window.removeEventListener('scroll', handleScroll);
        };
    }, []);
 
    return (
        <IntlProvider locale="en" messages={messages[locale]}>
            <div className={`page-content ${locale}`}>
                <Nav scrollTop={scrollTop} />
                <About locale={locale} />
                <Product scrollTop={scrollTop} />
                <Service scrollTop={scrollTop} />
                <Value scrollTop={scrollTop} />
                <JoinUs scrollTop={scrollTop} />
                <ContactUs locale={locale} scrollTop={scrollTop} />
                <Footer />
            </div>
        </IntlProvider>
    );
};
export default App;

 

在app.js中传递scrollTop给组件,用来计算动画触发时的高度

随便一个组件的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import React, { useState, useEffect, useRef } from 'react';
import { Animate } from 'react-move';
import { FormattedMessage } from 'react-intl';
import { easeQuadInOut } from 'd3-ease';
import Location from '@/assets/svg/location.svg';
import ArrowUp from '@/assets/svg/arrow-down.svg';
import Globe from '@/assets/images/globe.png';
import './index.scss';
 
const JoinUs = ({ scrollTop }) => {
    const ref = useRef();
    const [options, setOptions] = useState(false);
    const showOption = () => {
        setOptions(!options);
    };
 
    const handleScroll = top => {
        const obj = ref.current;
        const clientHeight = document.documentElement.clientHeight;
        const diff = clientHeight - obj.offsetTop + top;
 
        const doms = document.querySelectorAll('.section-joinus__select');
        Array.from(doms).forEach((item, index) => {
            if (diff > 0 && !item.style.animation) {
                item.style.animation = `slideUpBox 1s ease-in ${0.2 *
                    index}s forwards`;
            }
        });
 
        const domText = document.querySelector('.section-joinus__resc');
        if (diff > 0 && !domText.style.animation) {
            domText.style.animation = `slideUp 1s ease-in forwards`;
        }
    };
 
    useEffect(() => {
        handleScroll(scrollTop);
    }, [scrollTop]);
 
    const [items, setItems] = useState([
        {
            id: 0,
            country: <FormattedMessage id="component.join.country1" />,
            work1: <FormattedMessage id="component.join.work1" />,
            work2: <FormattedMessage id="component.join.work2" />,
            btntxt: <FormattedMessage id="component.join.btntxt" />
        },
        {
            id: 1,
            country: <FormattedMessage id="component.join.country2" />,
            work1: <FormattedMessage id="component.join.work3" />,
            work2: <FormattedMessage id="component.join.work4" />,
            btntxt: <FormattedMessage id="component.join.btntxt" />
        }
    ]);
 
    return (
        <div className="section section-joinus" ref={ref}>
            <div className="padding37">
                <div className="page-wrapper-inner">
                    <h4>
                        <FormattedMessage id="component.join.inner" />
                    </h4>
                    <div className="section-joinus__resc">
                        <FormattedMessage id="component.join.resc" />
                    </div>
                    {items.map((item, index) => (
                        <div className="section-joinus__select" key={index}>
                            <div
                                className="country"
                                onClick={() => showOption()}
                            >
                                <i className="i-pos">
                                    <Location />
                                </i>
                                <span>{item.country}</span>
                                <i className="i-up">
                                    <ArrowUp />
                                </i>
                            </div>
                            <div
                                className="option"
                                style={{
                                    height:
                                        index === 0
                                            ? options
                                                ? '0'
                                                : '200px'
                                            : options
                                            ? '200px'
                                            : '0'
                                }}
                            >
                                <ul>
                                    <li>{item.work1}</li>
                                    <li>{item.work2}</li>
                                </ul>
                                <button>{item.btntxt}</button>
                            </div>
                        </div>
                    ))}
                </div>
            </div>
 
            <p className="section-joinus__china">
                <FormattedMessage id="component.join.china" />
            </p>
            <p className="section-joinus__singa">
                <FormattedMessage id="component.join.singa" />
            </p>
            <i className="section-joinus__circleChina"></i>
            <i className="section-joinus__circleSinga"></i>
            <div className="section-joinus__bg">
                <img src={Globe} />
            </div>
        </div>
    );
};
export default JoinUs;

  

ps:

forwards:当动画完成后,保持最后一个属性值(在最后一个关键帧中定义)。

 

 

------from  大佬

posted @   芝麻小仙女  阅读(598)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示