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

如何用Framer Motion实现卡片背景色从左到右的Hover渐变效果?

实现卡片背景从左到右的悬停过渡效果(Framer Motion + React)

我需要复现一个卡片悬停效果:鼠标悬停时,卡片背景从var(--dark-bg)向左到右过渡变为网站主色var(--secondary)。目前用Framer Motion只能实现背景色整体同步变化,无法达成方向过渡效果,现有代码如下:

import { FC } from "react";
import { motion } from "framer-motion";
import { Stack, Typography, Divider, Button } from "@mui/material";


const NewsCard: FC = () => {
    return (
        <motion.div
            whileHover={{
                // backgroundColor: ["var(--dark-bg)", "var(--secondary)"]
                // background: "linear-gradient(to right, var(--dark-bg) 0%, var(--secondary) 100%)",
                // backgroundColor: "var(--secondary)",
                // backgroundSize: "250%"
            }}
            transition={{
                duration: 0.3,
                ease: "easeIn"
            }}
            style={{
                // height: "90%",
                // height: "100%",
                // border: "1px solid var(--secondary)",
                backgroundColor: "var(--dark-bg)"
                // background: "linear-gradient(to right, var(--dark-bg) 0%, var(--secondary) 100%)"
            }}
        >
            <Stack
                direction="row"
                justifyContent="space-between"
                alignItems="center"
            >
                <Stack direction="row" alignItems="center">
                    <Typography variant="overline">
                        Tag1
                    </Typography>
                    <Typography variant="overline">
                        Tag2
                    </Typography>
                    <Typography variant="overline">
                        Tag3
                    </Typography>
                </Stack>
                <Typography variant="overline">
                    {/* <time dateTime={ article.created_at!.toString() }>
                        { dayjs(article.created_at).format("DD MMM YYYY") }
                    </time> */}
                    11/02/2024
                </Typography>
            </Stack>
            <Divider sx={{ borderColor: "secondary.main" }} />
            <Stack justifyContent="space-between"
                style={{
                    // height: "93%"
                    // display: "grid",
                    // gridTemplateRows: "auto 1fr"
                }}
            >
                <Stack>
                    <Typography variant="h4">
                        Ut sapiente ut nemo dicta et rem rerum ratione beatae. Ut sapiente ut nemo dicta et rem rerum ratione beatae.
                    </Typography>
                    <Stack spacing={1} direction="row">
                        <div style={{ width: "20px", height: "20px", backgroundColor: "red" }} />
                        <Typography>by</Typography>
                        <Typography>Hazem Saeed</Typography>
                    </Stack>
                </Stack>

                <Stack sx={{ mt: "7vh !important" }}>
                    <Typography
                        sx={{
                            width: "95%",
                            height: "10vh",
                            textOverflow: "ellipsis",
                            // whiteSpace: "nowrap",
                            overflow: "hidden"
                        }}
                    >
                        Ipsam quam et non ut. Reprehenderit unde sunt minus. Sit quia similique sunt quam. Velit neque voluptatem quod. Et qui quam sint cupiditate sunt repellendus unde est temporibus.
                    </Typography>
                    <Stack alignItems="flex-end">
                        <Button
                            variant="outlined"
                            size="small"
                            sx={{ width: "130px" }}
                        >
                            read more
                        </Button>
                    </Stack>
                </Stack>
            </Stack>
        </motion.div>
    );
};

export default NewsCard;

解决方案

要实现这种方向式的背景过渡,不能直接修改backgroundColor,而是需要通过叠加背景层+位移动画来实现。具体步骤:

  • 给卡片容器设置position: relative和overflow: hidden,限制背景层的显示范围
  • 添加一个绝对定位的motion.div作为过渡背景层,初始时完全偏移到卡片左侧(x: -100%)
  • 利用Framer Motion的whileHover触发背景层的位移动画,使其从左到右覆盖整个卡片
  • 确保卡片内容的层级高于过渡背景层,避免被遮挡

修改后的完整代码:

import { FC } from "react";
import { motion } from "framer-motion";
import { Stack, Typography, Divider, Button } from "@mui/material";

const NewsCard: FC = () => {
    return (
        <div
            style={{
                position: "relative",
                overflow: "hidden",
                backgroundColor: "var(--dark-bg)"
            }}
        >
            {/* 背景过渡层 */}
            <motion.div
                initial={{ x: "-100%" }}
                whileHover={{ x: 0 }}
                transition={{
                    duration: 0.3,
                    ease: "easeIn"
                }}
                style={{
                    position: "absolute",
                    top: 0,
                    left: 0,
                    width: "100%",
                    height: "100%",
                    backgroundColor: "var(--secondary)",
                    zIndex: 0
                }}
            />
            {/* 卡片内容容器,设置z-index高于背景层 */}
            <Stack
                style={{
                    position: "relative",
                    zIndex: 1,
                    padding: "1rem"
                }}
            >
                <Stack
                    direction="row"
                    justifyContent="space-between"
                    alignItems="center"
                >
                    <Stack direction="row" alignItems="center" spacing={2}>
                        <Typography variant="overline">
                            Tag1
                        </Typography>
                        <Typography variant="overline">
                            Tag2
                        </Typography>
                        <Typography variant="overline">
                            Tag3
                        </Typography>
                    </Stack>
                    <Typography variant="overline">
                        11/02/2024
                    </Typography>
                </Stack>
                <Divider sx={{ borderColor: "secondary.main", my: 1 }} />
                <Stack justifyContent="space-between">
                    <Stack spacing={2}>
                        <Typography variant="h4">
                            Ut sapiente ut nemo dicta et rem rerum ratione beatae. Ut sapiente ut nemo dicta et rem rerum ratione beatae.
                        </Typography>
                        <Stack spacing={1} direction="row" alignItems="center">
                            <div style={{ width: "20px", height: "20px", backgroundColor: "red" }} />
                            <Typography>by</Typography>
                            <Typography>Hazem Saeed</Typography>
                        </Stack>
                    </Stack>

                    <Stack sx={{ mt: "7vh" }} spacing={2}>
                        <Typography
                            sx={{
                                width: "95%",
                                height: "10vh",
                                textOverflow: "ellipsis",
                                overflow: "hidden"
                            }}
                        >
                            Ipsam quam et non ut. Reprehenderit unde sunt minus. Sit quia similique sunt quam. Velit neque voluptatem quod. Et qui quam sint cupiditate sunt repellendus unde est temporibus.
                        </Typography>
                        <Stack alignItems="flex-end">
                            <Button
                                variant="outlined"
                                size="small"
                                sx={{ width: "130px" }}
                            >
                                read more
                            </Button>
                        </Stack>
                    </Stack>
                </Stack>
            </Stack>
        </div>
    );
};

export default NewsCard;

关键修改说明

  • 将原motion.div拆分为容器+背景过渡层+内容层结构,避免内容被背景层遮挡
  • 背景过渡层使用absolute定位,通过x属性的位移动画实现从左到右的填充效果
  • 内容层设置position: relative和z-index: 1,确保文字、按钮等内容始终可见
  • 调整了部分Stack的spacing和内边距,优化布局合理性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:32:03