Netflix风格卡片延迟hover缩放效果及状态异常问题求助
问题:Netflix卡片悬停状态异常
我想复刻Netflix的UI效果:鼠标悬停在卡片上超过1秒时,显示视频图片区域,同时触发缩放和从opacity 0到1的淡入动画。目前动画已经实现,但存在UI问题:即使鼠标离开卡片,isHovered状态仍会被自动设为true。
现有React组件代码
export default function Card({ movieData, isLiked = false }) { const [isHovered, setIsHovered] = useState(false); console.log("movdata"); const nav = useNavigate(); let timeoutId = null; const handleMouseEnter = () => { timeoutId = setTimeout(() => { setIsHovered(true); }, 1000); }; const handleMouseLeave = () => { clearTimeout(timeoutId); setIsHovered(false); }; return ( <Container onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} className={isHovered ? "hovered" : ""} > <img src={`https://image.tmdb.org/t/p/w500${movieData.image}`} alt="age" /> <div className="hover rr" style={{ visibility: `${isHovered === true ? "visible" : "hidden"}`, opacity: `${isHovered === true ? "1" : "0"}`, }} > { <> <div className="image-video"> <img src={`https://image.tmdb.org/t/p/w500${movieData.image}`} alt="age" onClick={() => nav("/player")} /> <video src={op} autoPlay loop muted onClick={() => nav("/player")} > <div className="mute icons flex j-between"> <div className="controls flex"> <IoVolumeHighSharp /> <IoVolumeMuteSharp /> </div> </div> </video> </div> <div className="info-container flex column"> <h3 className="name" onClick={() => nav("/player")}> {movieData.name} </h3> <div className="icons flex j-between"> <div className="controls flex"> <IoPlayCircleSharp title="Play" onClick={() => nav("/player")} /> <BsHandThumbsUp /> {isLiked ? ( <BsCheck title="Remove From Fav" /> ) : ( <AiOutlinePlus title="Add to Fav" /> )} </div> <div className="info"> <BiChevronDown title="More" /> </div> </div> <div className="genres flex"> <ul className="flex"> {movieData.genres.map((genre) => ( <li key={genre}>{genre}</li> ))} </ul> </div> </div> </> } </div> </Container> ); }
现有CSS样式
max-width: 16.66666667%; width: 16.66666667%; height: 100%; cursor: pointer; padding: 0 0.2vw; position: relative; &.hovered { transition-delay: 0.5s; transform: scale(1.3); transition: transform 600ms ease-in; } img { position: relative; border-radius: 0.2rem; width: 227px; border-radius: 5px; max-width: 227px; height: 100%; z-index: -1; } .hover { z-index: 999; height: max-content; position: absolute; top: -10vh; border-radius: 0.3rem; box-shadow: #000000c4 0px 3px 10px; width: 250px; height: 290px; background-color: #181818; .image-video { position: relative; height: 140px; img { width: 227px; height: 140px; object-fit: cover; border-radius: 0.3rem; top: 0; z-index: 4; position: absolute; } video { width: 100%; height: 140px; object-fit: cover; border-radius: 0.3rem; top: 0; z-index: 5; position: absolute; } } .info-container { padding: 1rem; gap: 0.5rem; font-size: 0.8vw; }
问题原因与解决方案
核心问题
timeoutId是普通变量,组件每次重渲染都会重置为null,导致旧定时器无法被清除,到期后仍会触发setIsHovered(true)。- 缩放后的
.hover悬浮层z-index极高,鼠标移到上面时会触发原卡片的handleMouseLeave,但实际用户仍在卡片交互区域内。
修复步骤
用
useRef保存定时器ID
替换普通变量为useRef,确保定时器ID在组件生命周期内不丢失:import { useState, useRef, useEffect } from 'react'; // 补充导入useRef和useEffect // ... const timeoutId = useRef(null); // 替换let timeoutId = null; const handleMouseEnter = () => { timeoutId.current = setTimeout(() => { setIsHovered(true); }, 1000); }; const handleMouseLeave = () => { clearTimeout(timeoutId.current); timeoutId.current = null; setIsHovered(false); };添加组件卸载时的定时器清理
避免组件卸载后定时器仍运行导致内存泄漏:useEffect(() => { return () => { clearTimeout(timeoutId.current); }; }, []);处理悬浮层鼠标事件穿透
给.hover层添加pointer-events: none,让鼠标事件穿透到原卡片,同时恢复内部可点击元素的事件响应:.hover { z-index: 999; height: max-content; position: absolute; top: -10vh; border-radius: 0.3rem; box-shadow: #000000c4 0px 3px 10px; width: 250px; height: 290px; background-color: #181818; pointer-events: none; /* 添加此行 */ .image-video, .info-container { pointer-events: auto; /* 内部可点击元素恢复事件响应 */ } /* 原有样式保留 */ }
内容的提问来源于stack exchange,提问作者Fam fas
相关产品推荐
相关产品推荐

