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 大佬
分类:
react从入门到放弃
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· 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工具