Netflix克隆项目本地JSON映射列表hover布局异常问题求助
问题描述
我正在开发Netflix克隆项目,将本地JSON数据映射为媒体列表组件。当前遇到的问题是:当鼠标悬浮在列表项上时,当前项会触发悬浮展开效果,但下一个列表项会占据该悬浮项的原始位置,导致布局错乱。
项目相关代码
本地JSON数据示例
export const products = [ { name: 'iPhone 11', image: '/assets/images (7).jpeg', time: '1 hour 14mins', age: '+16', year: '1999', desc: 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Praesentium hic rem eveniet error possimus, neque ex doloribus.', genre: 'Action', id: 0, }, { name: 'iPhone 11', image: '/assets/images (13).jpeg', time: '1 hour 14mins', age: '+16', year: '1999', desc: 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Praesentium hic rem eveniet error possimus, neque ex doloribus.', genre: 'Action', id: 1, }, { name: 'iPhone 11', image: '/assets/images (14).jpeg', time: '1 hour 14mins', age: '+16', year: '1999', desc: 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Praesentium hic rem eveniet error possimus, neque ex doloribus.', genre: 'Action', id: 2, } ]
List.js 代码
import React from 'react' import { ArrowBackIosOutlined, ArrowForwardIosOutlined, } from "@material-ui/icons"; import { useRef, useState } from "react"; import ListItem from "../listItem/ListItem"; import "./list.scss"; import { products } from './listdata' export default function List() { const [isMoved, setIsMoved] = useState(false); const [slideNumber, setSlideNumber] = useState(0); const listRef = useRef(); const handleClick = (direction) => { setIsMoved(true); let distance = listRef.current.getBoundingClientRect().x - 50; if (direction === "left" && slideNumber > 0) { setSlideNumber(slideNumber - 1); listRef.current.style.transform = `translateX(${230 + distance}px)`; } if (direction === "right" && slideNumber < 5.6) { setSlideNumber(slideNumber + 1); listRef.current.style.transform = `translateX(${-230 + distance}px)`; } }; return ( <div className="list"> <span className="listTitle">Continue to watch</span> <div className="wrapper"> <ArrowBackIosOutlined className="sliderArrow left" onClick={() => handleClick("left")} style={{ display: !isMoved && "none" }} /> <div className="container" ref={listRef}> {products.map((product) => ( <div key={product.id}> <ListItem product={product} /> </div> ))} </div> <ArrowForwardIosOutlined className="sliderArrow right" onClick={() => handleClick("right")} /> </div> </div> ); }
ListItem.js 代码
import React from 'react' import "./listItem.scss"; import trail from '../../assets/The Imperfects _ Official Trailer _ Netflix.mp4' import { PlayArrow, Add, ThumbUpAltOutlined, Check, } from "@material-ui/icons"; import { useState } from "react"; export default function ListItem({ product }) { const [isHovered, setIsHovered] = useState(false); const trailer = trail; return ( <div className="listItem" style={{left: isHovered && product.id * 225 - 50 + product.id * 2.5}} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > <img src={product.image} alt="" /> {isHovered && ( <> <video height='180px' src={trail} autoPlay={true} loop /> <div className="itemInfo"> <div className="icons"> <PlayArrow className="icon" style={{color: 'black'}} /> <Check className="icon" /> <Add className="icon" /> <ThumbUpAltOutlined className="icon" /> </div> <div className="itemInfoTop"> <span>{product.time}</span> <span className="limit">{product.age}</span> <span>{product.year}</span> </div> <div className="desc"> {product.desc} </div> <div className="genre">{product.genre}</div> </div> </> )} </div> ); }
问题分析
布局错乱的核心原因:
- 悬浮时通过动态
left属性移动列表项,导致当前项脱离正常布局流,后续元素会自动填补其原始位置 - 悬浮展开的内容没有独立的定位容器,直接插入文档流会挤压或位移其他元素
解决方案
1. 重构ListItem组件,改用绝对定位承载悬浮内容
// ListItem.js export default function ListItem({ product }) { const [isHovered, setIsHovered] = useState(false); const trailer = trail; return ( <div className="listItem"> <img src={product.image} alt="" /> {/* 悬浮内容容器,通过绝对定位覆盖在原列表项上方 */} {isHovered && ( <div className="hoverOverlay"> <video height='180px' src={trail} autoPlay={true} loop muted /> <div className="itemInfo"> <div className="icons"> <PlayArrow className="icon" style={{color: 'black'}} /> <Check className="icon" /> <Add className="icon" /> <ThumbUpAltOutlined className="icon" /> </div> <div className="itemInfoTop"> <span>{product.time}</span> <span className="limit">{product.age}</span> <span>{product.year}</span> </div> <div className="desc">{product.desc}</div> <div className="genre">{product.genre}</div> </div> </div> )} </div> ); }
2. 调整ListItem.scss样式,固定布局规则
// listItem.scss .listItem { position: relative; width: 225px; height: 125px; margin-right: 2.5px; cursor: pointer; overflow: visible; // 允许悬浮内容超出容器范围 img { width: 100%; height: 100%; object-fit: cover; } .hoverOverlay { position: absolute; top: 0; left: 0; width: 400px; height: 180px; background-color: #181818; z-index: 10; padding: 10px; display: flex; flex-direction: column; justify-content: space-between; video { width: 100%; object-fit: cover; } .itemInfo { color: white; font-size: 14px; .icons { display: flex; gap: 10px; margin-bottom: 8px; .icon { cursor: pointer; } } .itemInfoTop { display: flex; gap: 10px; margin-bottom: 8px; .limit { border: 1px solid #ccc; padding: 0 4px; } } .desc { margin-bottom: 8px; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } } } }
3. 优化List容器样式,确保横向排列稳定
// list.scss .container { display: flex; overflow: hidden; gap: 2.5px; align-items: flex-start; // 避免元素垂直错位 }
优化说明
- 用
relative+absolute定位让悬浮内容覆盖在原列表项上方,不会改变原布局流,避免后续元素填补位置 - 给悬浮内容设置固定宽高,确保展开时不会挤压其他元素
- 视频添加
muted属性,符合浏览器自动播放规则 - 调整列表容器的对齐方式,避免元素垂直错位
内容的提问来源于stack exchange,提问作者Samuel Paschalson
相关产品推荐
相关产品推荐

