使用 CSS `perspective` 实现 3D 卡片效果

一个简单但能显著提升网页用户体验的 3D 卡片组件。本文将带你从零开始理解 perspective 的原理,并结合 React 生态中的优秀库(如 react-tilt),打造一个随鼠标移动而动态倾斜的 3D 卡片组件。

在现代网页设计中,3D 效果不仅能提升视觉吸引力,还能增强用户交互体验。其中 CSS 的 perspective 属性是实现基础 3D 变换的核心属性之一。本文将通过实际案例,深入解析如何利用 CSS perspective 创建令人印象深刻的 3D 卡片效果。

起因是在浏览一个壁纸网站时看到一个引人注目的 3D 卡片效果,作为一个优秀的前端开发,那必须得研究并借鉴一下。


前置知识

基本概念

1. 什么是透视(Perspective)?

在现实世界中,远处的物体看起来更小,近处的物体看起来更大。这种效果称为透视

CSS 的 perspective 就是用来模拟这种视觉效果的。

2. 默认情况

默认情况下,所有变换都是 正交投影 (orthographic projection),没有远近缩放效果。启用 perspective 后,就会变成 透视投影(perspective projection)。


语法

css 复制代码
perspective: none | <length>;
  • none:默认值,表示不应用透视(等同于无穷远的视角,无深度感)。
  • <length>:指定观察者与 Z=0 平面的距离,单位通常是 px。值越小,透视效果越强;值越大,效果越弱(趋近于无透视)。

注意:perspective 不能为负值,也不能使用百分比。


两种使用方式

方式 1:作为父容器的属性(推荐)

css 复制代码
.container {
    perspective: 600px;
}
.child {
    transform: rotateY(45deg);
}
html 复制代码
<div class="container">
    <div class="child">3D 元素</div>
</div>

优点

  • 所有子元素共享同一个消失点(vanishing point),看起来更自然。
  • 更符合真实世界的透视逻辑。

方式 2:作为 transform 的函数(不推荐用于复杂场景)

css 复制代码
.element {
    transform: perspective(600px) rotateY(45deg);
}

⚠️ 缺点

  • 每个元素都有自己的消失点,多个元素同时变换时会显得不协调。
  • 不适合构建复杂的 3D 场景。

相关属性详解

perspective-origin

  • perspective-origin 定义了透视的"观察中心点",默认是元素的中心(50% 50%)。
  • 类似于相机对准的位置。
css 复制代码
.container {
    perspective: 800px;
    perspective-origin: top left; /* 从左上角观察 */
}

transform-style

  • 决定子元素如何在 3D 空间中渲染
  • flat(默认):子元素在 2D 平面中渲染,不保留 3D 层级关系
  • preserve-3d:保留 3D 层级关系,子元素在 3D 空间中渲染
css 复制代码
.container {
    perspective: 800px;
    transform-style: preserve-3d;
}

transform-style: flat

transform-style: preserve-3d

backface-visibility

  • 控制元素背面是否可见
  • visible(默认):背面可见
  • hidden:背面不可见
css 复制代码
.card {
    backface-visibility: hidden;
}

总结

特性 说明
作用 控制 3D 变换的透视强度
值越小 透视越强(夸张的近大远小)
值越大 透视越弱(接近平面)
推荐用法 应用于父容器,而非单个元素
配合属性 transform, transform-style, perspective-origin

注意事项

  1. 只有设置了 perspective 的祖先元素内的 3D 变换才会呈现透视效果
  2. perspective 本身不会触发 3D 渲染上下文,必须配合 transform 使用。
  3. 使用 transform-style: preserve-3d 可以保持 3D 层级关系。
  4. 合理使用 backface-visibility: hidden 可以避免不必要的渲染问题。

实际应用场景

带透视的 3D 响应式卡片

现在用 perspective 来复刻一个开头说的壁纸网站的 3D 卡片效果:当鼠标在卡片上移动时,卡片会根据鼠标位置做出响应式的旋转效果,产生逼真的 3D 倾斜感。

关键点就在于要动态计算旋转的角度

原生 JavaScript 版本

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>3D Hover Card with Perspective</title>
        <style>
            body {
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh;
                margin: 0;
                background: #f0f2f5;
                font-family: Arial, sans-serif;
            }

            /* 关键:设置 perspective 在父容器上 */
            .card-container {
                width: 300px;
                height: 200px;
                perspective: 1000px; /* 透视距离,控制3D强度 */
            }

            .card {
                width: 100%;
                height: 100%;
                background: white;
                border-radius: 16px;
                box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
                display: flex;
                justify-content: center;
                align-items: center;
                font-size: 20px;
                color: #333;
                cursor: pointer;

                /* 启用3D变换上下文 */
                transform-style: preserve-3d;
                transition: transform 0.1s ease-out;

                /* 初始轻微倾斜(可选) */
                transform: rotateX(5deg) rotateY(0deg);
            }
        </style>
    </head>
    <body>
        <div class="card-container">
            <div class="card" id="card">✨ 3D Hover Card</div>
        </div>

        <script>
            const card = document.getElementById('card');
            const container = card.parentElement;

            container.addEventListener('mousemove', (e) => {
                // 获取容器尺寸和鼠标位置
                const rect = container.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;

                // 计算中心点偏移(归一化到 -1 ~ 1)
                const centerX = (x - rect.width / 2) / (rect.width / 2); // -1 到 1
                const centerY = (y - rect.height / 2) / (rect.height / 2); // -1 到 1

                // 控制旋转角度(可调节灵敏度)
                const rotateY = centerX * 15; // 水平方向最大 ±15 度
                const rotateX = -centerY * 15; // 垂直方向(注意负号:向下移动应向上仰)

                card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
            });

            container.addEventListener('mouseleave', () => {
                // 鼠标离开时恢复原状
                card.style.transform = 'rotateX(5deg) rotateY(0deg)';
            });
        </script>
    </body>
</html>

使用 React 开源库 react-tilt

安装
bash 复制代码
npm install react-tilt
使用示例
jsx 复制代码
import Tilt from 'react-tilt';

function TiltCard() {
    const options = {
        reverse: false, // 是否反转方向
        max: 35, // 最大倾斜角度
        perspective: 1000, // 透视距离(对应 CSS perspective)
        scale: 1.1, // 鼠标悬停时放大比例
        speed: 1000, // 过渡动画速度(ms)
        transition: true,
        axis: null, // 锁定轴('x' 或 'y')
        reset: true, // 鼠标离开后是否重置
        easing: 'cubic-bezier(.03,.98,.52,.99)',
    };

    return (
        <Tilt options={options} className="tilt-root">
            <div className="card-content">
                <h2>Dynamic 3D Card</h2>
                <p>Move your mouse around!</p>
            </div>
        </Tilt>
    );
}

我应用到了自己的博客列表页,作为卡片式的结构

其他应用场景

  • 产品展示(如 360° 查看)
  • 3D 轮播图
  • 游戏或交互式 UI(如倾斜视差效果)

性能优化建议

  1. 使用 transformopacity 进行动画:这两个属性不会触发重排(reflow)和重绘(repaint),性能更好。
  2. 合理使用 will-change 属性:提前告知浏览器哪些属性将要发生变化,让浏览器提前进行优化。
  3. 避免过度复杂的 3D 变换:过多的 3D 变换可能会影响页面性能,特别是在移动设备上。
  4. 使用 CSS 硬件加速 :通过 transform: translateZ(0)transform: translate3d(0, 0, 0) 触发硬件加速。
css 复制代码
.card {
    will-change: transform;
    transform: translateZ(0); /* 触发硬件加速 */
}

附:参考资料


学习优秀作品,是提升技术的最佳路径。本文作为自己的学习笔记,也希望这篇解析对你有所帮助

相关推荐
若安程序开发2 小时前
web京东商城前端项目4页面
前端
申阳2 小时前
Day 8:06. 基于Nuxt开发博客项目-我的服务模块开发
前端·后端·程序员
转角羊儿2 小时前
layui框架中,表单元素不显示问题
前端·javascript·layui
muyouking112 小时前
WASM 3.0 两大领域实战:SvelteKit前端新范式(完整版)
前端·wasm
Hilaku3 小时前
当你的Ant-Design成了你最大的技术债
前端·javascript·前端框架
Highcharts.js3 小时前
时间序列图的“性能陷阱”:Highcharts “金融级”优化方案
前端·python·金融
摇滚侠3 小时前
Vue 项目实战《尚医通》,完成预约通知业务,笔记21
前端·vue.js·笔记·前端框架
IT_陈寒3 小时前
SpringBoot性能优化实战:我从10万QPS项目中总结的7个核心技巧
前端·人工智能·后端
顾安r4 小时前
11.9 脚本网页 消消乐
前端·javascript·flask·html·pygame