一个简单但能显著提升网页用户体验的 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 |
注意事项
- 只有设置了
perspective的祖先元素内的 3D 变换才会呈现透视效果。 perspective本身不会触发 3D 渲染上下文,必须配合transform使用。- 使用
transform-style: preserve-3d可以保持 3D 层级关系。 - 合理使用
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(如倾斜视差效果)
性能优化建议
- 使用
transform和opacity进行动画:这两个属性不会触发重排(reflow)和重绘(repaint),性能更好。 - 合理使用
will-change属性:提前告知浏览器哪些属性将要发生变化,让浏览器提前进行优化。 - 避免过度复杂的 3D 变换:过多的 3D 变换可能会影响页面性能,特别是在移动设备上。
- 使用 CSS 硬件加速 :通过
transform: translateZ(0)或transform: translate3d(0, 0, 0)触发硬件加速。
css
.card {
will-change: transform;
transform: translateZ(0); /* 触发硬件加速 */
}
附:参考资料
- Bilibili 视频:透视 perspective 让三维变换更真实
- MDN - CSS perspective
- MDN - CSS transform-style
- MDN - CSS backface-visibility
- react-tilt - npm
- CSS will-change 属性
学习优秀作品,是提升技术的最佳路径。本文作为自己的学习笔记,也希望这篇解析对你有所帮助