卡片 3D 倾斜悬停特效(鼠标跟随 + 聚光高光 + 内容分层 CSS JS 源码)
卡片 3D 倾斜悬停完整实现:鼠标跟随 rotateX/rotateY、光标聚光高光、内容按深度分层浮起、平滑插值回弹,原生 JS 无依赖,适合官网卡片与作品展示。
效果演示(真实可交互)
把鼠标移到卡片上:卡片会跟随光标在三维空间倾斜,高光聚点贴着鼠标移动,图标、标题、按钮按不同深度浮起,移出后平滑回正。对比本站「3D 卡片翻转」组件的简单倾斜,本组件额外包含聚光与分层深度。
3D 卡片倾斜
鼠标移上来,卡片会像悬浮在空中一样跟随你的光标转动
移动鼠标 · 移出卡片自动回正
完整代码
HTML 结构:.t3-card 是倾斜主体,.t3-glare 是聚光层,.t3-inner > * 是需要分层的元素,用 data-depth 声明浮起高度:
<div class="t3-card" id="t3Card">
<div class="t3-glare" id="t3Glare"></div>
<div class="t3-inner">
<span class="t3-icon" data-depth="46">🎴</span>
<h3 class="t3-title" data-depth="30">3D 卡片倾斜</h3>
<p class="t3-sub" data-depth="18">...</p>
<button class="t3-btn" data-depth="38">马上体验</button>
</div>
</div>
核心 CSS(骨架):
.t3-wrap { perspective: 900px; } /* 父容器提供景深 */
.t3-card {
transform-style: preserve-3d; /* 保留子元素 3D 上下文 */
transform: rotateX(0) rotateY(0);
transition: transform .5s cubic-bezier(.22, .61, .36, 1);
will-change: transform;
}
.t3-inner > * { transform: translateZ(var(--depth, 0px)); } /* 分层浮起 */
.t3-glare {
position: absolute; inset: 0;
background: radial-gradient(circle at var(--gx, 50%) var(--gy, 50%),
rgba(255, 255, 255, .16), transparent 55%);
opacity: 0;
transition: opacity .35s ease;
}
.t3-card:hover .t3-glare { opacity: 1; }
核心 JS(平滑跟随 + 分层 + 聚光):
const card = document.getElementById('t3Card');
const glare = document.getElementById('t3Glare');
const MAX = 14;
const layers = card.querySelectorAll('[data-depth]');
let cur = { x: 0, y: 0 }, target = { x: 0, y: 0 }, raf = null;
card.addEventListener('mousemove', (e) => {
const r = card.getBoundingClientRect();
const nx = (e.clientX - r.left) / r.width - 0.5; // -0.5 ~ 0.5
const ny = (e.clientY - r.top) / r.height - 0.5;
target = { x: -ny * MAX * 2, y: nx * MAX * 2 };
glare.style.setProperty('--gx', (nx + 0.5) * 100 + '%');
glare.style.setProperty('--gy', (ny + 0.5) * 100 + '%');
if (!raf) {
const tick = () => {
cur.x += (target.x - cur.x) * 0.12; // 插值平滑
cur.y += (target.y - cur.y) * 0.12;
card.style.transform = `rotateX(${cur.x}deg) rotateY(${cur.y}deg)`;
layers.forEach((el) => {
const d = +el.dataset.depth;
el.style.setProperty('--depth', d + 'px'); // 简版:恒定深度
});
raf = Math.abs(target.x - cur.x) > .05 || Math.abs(target.y - cur.y) > .05
? requestAnimationFrame(tick) : null;
};
raf = requestAnimationFrame(tick);
}
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'rotateX(0deg) rotateY(0deg)'; // CSS 过渡回正
});
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
最大倾斜角 MAX | 14° | 光标移到边缘时的最大角度 |
| 透视距离 | 900px | 数值越小立体感越强 |
| 插值系数 | 0.12 | 每帧跟随比例,越小越”粘滞” |
| 聚光透明度 | .16 | 高光径向渐变的峰值透明度 |
分层深度 data-depth | 18-46px | 元素浮起高度,越大越”突出” |
兼容性与性能
- 原生 JS + CSS,无任何第三方依赖;
transform-style: preserve-3d需要 Chrome 36+ / Safari 9+ / Firefox 36+ - 动画由
requestAnimationFrame驱动,只在鼠标移动时运行,静止时零开销 - 使用 CSS 变量
--depth与--gx/--gy传递数值,避免频繁直接改 style 造成的强制同步布局 - 移出时交给 CSS
transition回正,避免 JS 在动画结束时残留一帧抖动
常见问题
Q: 卡片不倾斜或倾斜方向反了? 检查父容器是否设置了 perspective,且 .t3-card 的 transform-style: preserve-3d 未被覆盖;方向反了就把 target 里的两个负号互换。
Q: 分层元素没有浮起效果? 确认子元素有 transform: translateZ(var(--depth)) 且 JS 正确设置了 --depth;同时父元素不能有 overflow: hidden,它会压平 3D 上下文。
Q: 鼠标移出后卡片回正很生硬? 移出时 JS 直接把 transform 清空,回正动画由 CSS transition 承担;如果觉得太快,把 .t3-card 的 transition 时长从 .5s 调大即可。
Q: 可以商用吗? 可以,本组件为粤喵自研原创,免费商用无需署名;JS 是标准 DOM API,在 React/Vue 中只需把 getElementById 换成 ref 即可复用。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。