React绝对定位元素过渡动画未触发问题求助
Hero滑块动画无法触发问题:Ref更新但DOM样式未变化
我尝试为heroTitle和heroBtn添加过渡动画,但动画无法触发。打印element.current显示样式已更新,但React渲染的当前元素样式没有变化。想要实现标题从下方滑入、按钮淡入的效果,却因样式未更新导致动画失效。
Ref.current已更新

DOM未更新

React JSX代码
import { useState, useEffect, useRef } from "react"; import '../../styles/home.css' import useSwipe from "../../hooks/useSwipe"; const HeroSlider = () => { const [imgIndex, setImgIndex] = useState(0); const intervalTim = useRef(null); const progressRef = useRef(null); const heroTitle = useRef(null); const heroBtn = useRef(null); const autoplay = false; const heroImgs = [ 'https://placehold.co/1920x1080?text=1', 'https://placehold.co/1920x1080?text=2', 'https://placehold.co/1920x1080?text=3' ] function showPrevImage() { setImgIndex((index) => index === 0 ? heroImgs.length - 1 : index - 1); } function showNextImage() { setImgIndex((index) => index === heroImgs.length - 1 ? 0 : index + 1); } const { handleTouchStart, handleTouchMove, handleTouchEnd } = useSwipe(showNextImage, showPrevImage); console.log(imgIndex) const resetProgressBar = () => { if (progressRef.current) { progressRef.current.style.transition = 'none'; progressRef.current.style.width = '0%'; setTimeout(() => { progressRef.current.style.transition = 'width 5s linear'; progressRef.current.style.width = '105%'; }, 25); // 延迟让浏览器重置宽度 } }; // 这段代码理应生效 // 预期在渲染时执行,但样式未更新 useEffect(() => { heroTitle.current.style.bottom = 0; heroBtn.current.style.opacity = 1; }, []) useEffect(() => { //resetProgressBar(); console.log('interval render') intervalTim.current = autoplay && setInterval(() => { showNextImage(); resetProgressBar(); }, 5000); return () => { console.log('clear') clearInterval(intervalTim.current) }; }, [imgIndex]); return ( <section key={test}> <div className="hero-slider"> <div className="hero-slider__main-img-slider-container" onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} > {heroImgs.map((imgUrl, i) => ( <div aria-hidden={imgIndex !== i} style={{transform: `translateX(${-100 * imgIndex}%)`}} key={imgUrl} className="hero-slider__main-img-container" > <img className="hero-slider__main-img" src={imgUrl} alt={`img ${i + 1}`} /> <div className="hero-slider__hero-text-container"> <div className="hero-slider__hero-text"> <div className="hero-slider__hero-title-container"> // HeroTitle ref <h1 ref={heroTitle} className="hero-slider__hero-title">Title</h1> </div> // HeroButton ref <button ref={heroBtn} className="hero-slider__hero-btn">shop</button> </div> </div> </div> ))} </div> <div className="hero-slider__navigation-btns-container"> <ul className="hero-slider__navigation-btns-list"> {heroImgs.map((_, i) => ( <li key={i}> <button aria-label={`Show image ${i + 1}`} style={i === imgIndex ? {backgroundColor: 'black'} : {backgroundColor: 'white'}} className="hero-slider__navigation-btn" onClick={() => { setImgIndex(i) }}> </button> </li> ))} </ul> </div> <div className="hero-slider__arrow-btns-container"> <button aria-label="Show previous image." className="hero-slider__prev-btn" onClick={showPrevImage}>left</button> <button aria-label="Show next image." className="hero-slider__next-btn" onClick={showNextImage}>right</button> </div> <div className="hero-slider__progress-bar-container"> <div className="hero-slider__progress-bar" ref={progressRef}></div> </div> </div> </section> ); } export default HeroSlider;
CSS代码
.hero-slider { width: 100%; height: 70vh; position: relative; margin: 0 0 50px; font-size: 1rem; } .hero-slider__main-img-slider-container { display: flex; width: 100%; height: 100%; overflow: hidden; } .hero-slider__main-img-container { flex-shrink: 0; flex-grow: 0; width: 100%; height: 100%; position: relative; transition: transform 0.3s ease-in-out; } .hero-slider__main-img { width: 100%; height: 100%; object-fit: cover; display: block; } /* 待动画元素 */ .hero-slider__hero-text-container { position: absolute; bottom: 0; left: 0; width: 100%; padding: 0 0 60px; } .hero-slider__hero-text { max-width: 1300px; margin: 0 auto; padding: 0 40px; border: 2px solid blue; } .hero-slider__hero-title-container { border: 2px solid lightseagreen; position: relative; height: 100px; } /* Hero标题和按钮 */ .hero-slider__hero-title { font-size: 4em; position: absolute; bottom: -100px; transition: bottom 1s; } .hero-slider__hero-btn { background-color: black; color: white; padding: 10px; font-size: 1.25em; opacity: 0; transition: opacity 1s; } /* 待动画元素结束 */ .hero-slider__arrow-btns-container { position: absolute; bottom: -20px; right: 40px; z-index: 3; } .hero-slider__navigation-btns-container { position: absolute; bottom: 10px; left: 0; right: 0; z-index: 2; } .hero-slider__navigation-btns-list { max-width: 1300px; margin: 0 auto; padding: 0 40px; display: flex; gap: 10px; list-style: none; } .hero-slider__navigation-btn { border: 2px solid #000; width: 15px; height: 15px; border-radius: 50%; } .hero-slider__navigation-btn:focus-visible { outline: 1px solid #000; } .hero-slider__navigation-btn:is(:hover, :focus-visible) { transform: scale(1.1); } .hero-slider__prev-btn, .hero-slider__next-btn { background-color: black; color: white; padding: 10px; } .hero-slider__prev-btn { margin-right: 10px; } .hero-slider__arrow-btns-container button:hover { background-color: red; } .hero-slider__progress-bar-container { overflow: hidden; position: absolute; bottom: 0; left: 0; width: 100%; z-index: 1; height: 50px; } .hero-slider__progress-bar { position: absolute; bottom: 0; left: 0; height: 3px; background-color: rgba(255, 255, 255, 0.8); width: 0; transition: width 5s linear; border-top-right-radius: 20px; border-bottom-right-radius: 20px; } .hero-slider__progress-bar-inital { transition: none; width: 0%; } .hero-slider__progress-bar-animation { transition: width 5s linear; width: 105%; } @media screen and (max-width: 750px) { .hero-slider__navigation-btns-list { padding: 0 20px; } .hero-slider__hero-text { padding: 0 20px; } }
已尝试的解决方案
- 添加延迟适配DOM渲染
useEffect(() => { setTimeout(() => { heroTitle.current.style.bottom = '0'; heroBtn.current.style.opacity = '1'; }, 100); }, []);
- 使用useState触发useEffect
const [test, setTest] = useState(0); useEffect(() => { setTimeout(() => { heroTitle.current.style.bottom = '0'; heroBtn.current.style.opacity = '1'; }, 100); }, [test]); // 为图片容器添加点击事件测试状态逻辑 <div onClick={() => {setTest((prev) => prev + 1)}} className="hero-slider__main-img-slider-container" onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} >
- 设置元素初始样式
useEffect(() => { // 设置初始状态 heroTitle.current.style.bottom = '-100px'; heroBtn.current.style.opacity = '0'; // 短延迟后设置最终状态 setTimeout(() => { heroTitle.current.style.bottom = '0'; heroBtn.current.style.opacity = '1'; }, 100); }, []);
内容的提问来源于stack exchange,提问作者Angel
相关产品推荐
相关产品推荐

