You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决MUI轮播卡片鼠标悬停时全部放大的问题

问题:MUI卡片悬停时全部放大,而非单张卡片

开发类似Netflix的轮播组件,期望实现鼠标悬停在某张MUI Card上时,仅该卡片放大并展示更多详情,但当前遇到的问题是,悬停时所有卡片都会一同放大。

原组件代码

import * as React from 'react';
import {useState} from 'react';
import Card from '@mui/material/Card';
import CardHeader from '@mui/material/CardHeader';
import CardMedia from '@mui/material/CardMedia';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Avatar from '@mui/material/Avatar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import {red} from '@mui/material/colors';
import FavoriteIcon from '@mui/icons-material/Favorite';
import ShareIcon from '@mui/icons-material/Share';
import {makeStyles} from '@mui/styles';

const ctCardHoveredSx = {
  maxWidth: 345,
  "&:hover": {},
  "& .ct-card": {
    display: "none"
  },
  "&:hover .ct-card": {
    display: "flex"
  },
  "& .ct-card-panel": {
    transition: "transform 0.15s ease-in-out"
  },
  "&:hover .ct-card-panel": {
    transform: "scale3d(1.05, 1.05, 1)"
  }
}

const useStyles = makeStyles({
  root: {
    maxWidth: 310,
    transition: "transform 0.15s ease-in-out",
    "& .ct-card": {
      display: "none"
    }
  },
  cardHovered: {
    transform: "scale3d(1.05, 1.05, 1)",
    "&:hover .ct-card": {
      display: "flex"
    }
  }
});


const CtCard = () => {

  const classes = useStyles();
  const [hovered, setHovered] = useState<boolean>(false);

  return (
    <Card className={classes.root}
          classes={{root: hovered ? classes.cardHovered : ""}}
          onMouseOver={() => setHovered(true)}
          onMouseOut={() => setHovered(false)}
    >
      <CardMedia
        component="img"
        height="194"
        image="https://picsum.photos/200/300"
        alt="Paella dish"
      />
      <CardHeader
        avatar={
          <Avatar sx={{bgcolor: red[500]}} aria-label="recipe">
            R
          </Avatar>
        }
        title="Lorem Ipsum"
        subheader="Lorem Ipsum"
      />
      <CardContent className={'ct-card'}>
        <Typography variant="body2" color="text.secondary">
          This impressive paella is a perfect party dish and a fun meal to cook
          together with your guests. Add 1 cup of frozen peas along with the mussels,
          if you like.
        </Typography>
      </CardContent>
      <CardActions disableSpacing className={'ct-card'}>
        <IconButton aria-label="add to favorites">
          <FavoriteIcon/>
        </IconButton>
        <IconButton aria-label="share">
          <ShareIcon/>
        </IconButton>
      </CardActions>
    </Card>
  );
}

export default CtCard;

组件使用方式

<Stack
      position={'relative'}
      direction={'row'}
      spacing={1}
    >
      {
        Array(pageCount).fill(null).map((v, i) =>
          <CtCard/>
        )
      }
    </Stack>

解决方案

问题核心在于用React状态管理hover效果容易出现类名冲突或状态同步问题,改用CSS伪类:hover直接控制样式,既能实现单张卡片独立响应,又简化代码逻辑。

修改后的代码

import * as React from 'react';
import Card from '@mui/material/Card';
import CardHeader from '@mui/material/CardHeader';
import CardMedia from '@mui/material/CardMedia';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Avatar from '@mui/material/Avatar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import {red} from '@mui/material/colors';
import FavoriteIcon from '@mui/icons-material/Favorite';
import ShareIcon from '@mui/icons-material/Share';
import {makeStyles} from '@mui/styles';

const useStyles = makeStyles({
  root: {
    maxWidth: 310,
    transition: "transform 0.15s ease-in-out",
    // 默认隐藏详情内容
    "& .ct-card": {
      display: "none"
    },
    // 悬停时触发的样式
    "&:hover": {
      transform: "scale3d(1.05, 1.05, 1)",
      // 显示详情内容
      "& .ct-card": {
        display: "flex",
        flexDirection: "column"
      }
    }
  }
});

const CtCard = () => {
  const classes = useStyles();

  return (
    <Card className={classes.root}>
      <CardMedia
        component="img"
        height="194"
        image="https://picsum.photos/200/300"
        alt="Paella dish"
      />
      <CardHeader
        avatar={
          <Avatar sx={{bgcolor: red[500]}} aria-label="recipe">
            R
          </Avatar>
        }
        title="Lorem Ipsum"
        subheader="Lorem Ipsum"
      />
      <CardContent className={'ct-card'}>
        <Typography variant="body2" color="text.secondary">
          This impressive paella is a perfect party dish and a fun meal to cook
          together with your guests. Add 1 cup of frozen peas along with the mussels,
          if you like.
        </Typography>
      </CardContent>
      <CardActions disableSpacing className={'ct-card'}>
        <IconButton aria-label="add to favorites">
          <FavoriteIcon/>
        </IconButton>
        <IconButton aria-label="share">
          <ShareIcon/>
        </IconButton>
      </CardActions>
    </Card>
  );
}

export default CtCard;

关键修改点

  1. 移除了组件内部的hovered状态、onMouseOver和onMouseOut事件监听,完全用CSS伪类处理悬停逻辑
  2. 将悬停时的放大效果和详情内容显示逻辑整合到root类的:hover伪类中,确保每个卡片独立响应自身的悬停事件
  3. 调整ct-card的显示布局,添加flexDirection: "column"保证内容排版正确

这样修改后,鼠标悬停任意一张卡片时,只有该卡片会放大并展示详情,其他卡片不受影响。

内容的提问来源于stack exchange,提问作者czetsuya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 11:21:01