纯CSS悬停倾斜需父容器设perspective、卡片设transform-style:preserve-3d和transform-origin:center center,用:hover预设rotateX/rotateY(±3°~8°)配合ease-out过渡;真鼠标跟随必须JS监听mousemove动态计算角度。
为什么直接用
和
悬停会“翻车”
因为纯
或
会让卡片绕自身坐标轴旋转,视觉上像“翻页”或“仰头”,不是自然的 3D 倾斜;真正要的是鼠标靠近哪边,卡片就向反方向微倾——这需要结合
、
和动态计算的旋转角度,否则悬停后元素会突然跳变、消失或失真。
必须加在父容器(如卡片外层 wrapper),不能只加在卡片自身,否则 3D 效果不生效
默认
会导致旋转中心僵硬,实际需设为
并配合
仅靠 CSS 无法读取鼠标坐标,所以纯 CSS 实现“跟随鼠标倾斜”必须用
+
配合
做固定角度切换,而非实时追踪
纯 CSS 实现可落地的悬停倾斜(非实时追踪版)
适合大多数展示型卡片,用
触发预设的
和
组合,配合
控制倾斜支点:
角度建议控制在 ±3°~±8° 之间,超过 10° 易产生眩晕感
推荐用
或
,避免机械式线性过渡
若卡片内有文字或子元素,确保它们没设置
冲突值,否则会错位
想让倾斜“跟着鼠标走”?必须用 JavaScript 补足
CSS 本身无法获取鼠标相对元素的位置,所以真正的“鼠标驱动 3D 倾斜”需要监听
,动态计算
和
值并写入
:
注意:必须在
上同时设置
和父级
移动端无
,需额外处理
,且触摸点坐标要用
高频触发易卡顿,建议用
节流,不要直接在事件里写
容易被忽略的兼容性与性能坑
这个效果在 Safari 和旧版 Chrome 上表现差异大,尤其涉及
时:
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
立即学习
“
前端免费学习笔记(深入)
”;
iOS Safari 对
支持不稳定,某些版本下子元素会“扁平化”,解决办法是给卡片加
Android WebView 中,若卡片含
+
,边缘可能锯齿,加
可缓解
大量卡片同时启用 3D transform 会触发 GPU 渲染,但若页面已有其他动画(如
或频繁重排),可能引发掉帧,建议用
提前提示(仅对 hover 卡片)
真实项目中,先跑通基础
+
组合,再决定是否上 JS 动态倾斜——多数场景下固定角度 hover 已足够传达交互意图,不必为“更真实”增加维护成本和兼容风险。
transform: rotateX()rotateY()rotateX()rotateY()perspectivetransform-originperspectivetransform-origin: centercenter centertransform-style: preserve-3drotateX()rotateY()transition:hoverrotateXrotateYtransform-origin.card {
width: 300px;
height: 200px;
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
/* 关键:启用 3D 渲染上下文 */
transform-style: preserve-3d;
transition: transform 0.3s ease-out;
}
.card:hover {
/ 向上倾斜 + 向左倾斜 → 模拟鼠标在右下角时的反向响应 /
transform: rotateX(-5deg) rotateY(5deg);
}
/ 父容器必须设 perspective,否则 rotateX/Y 无 3D 感 /
.card-wrapper {
perspective: 1000px;
}
transition-timing-functionease-outcubic-bezier(0.17, 0.67, 0.83, 0.67)transform: translateZ(...)mousemoverotateXrotateYstyle.transformconst card = document.querySelector('.card');
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// 距离中心越远,倾斜越大(最大 ±12°)
const rotateY = (x - centerX) / centerX * 12;
const rotateX = (centerY - y) / centerY * 12;
card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
});cardtransform-style: preserve-3dperspectivemousemovetouchmovee.touches[0]requestAnimationFramestyle.transformpreserve-3dtransform-style: preserve-3dbackface-visibility: hiddenborder-radiustransform-webkit-backface-visibility: hiddenposition: stickywill-change: transformrotateX/rotateYperspective