用transform等不触发重排的属性做按钮过渡,避免width/height导致热区跳变;悬浮条显隐需requestAnimationFrame节流+帧同步;底部固定条用env(safe-area-inset-bottom)适配iOS键盘;z-index失效时检查祖先元素是否创建了层叠上下文。

用 transition 做按钮微动效,别碰 width/height
直接改 width 或 height 触发 transition 会导致点击热区跳变——用户 hover 瞬间 hit area 缩小,点不中。正确做法是只过渡 transform、opacity、background-color 这类不触发重排的属性。
示例写法:
.floating-bar button {
transition: transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94),
background-color 0.2s ease,
opacity 0.15s ease;
}
.floating-bar button:hover {
transform: translateY(-2px) scale(1.03);
background-color: #3a86ff;
opacity: 1;
}- 动画时长控制在
0.15s–0.25s,超过0.3s就显拖沓 - 用
cubic-bezier(0.25, 0.46, 0.45, 0.94)替代ease,更轻快自然 - 别给
:active加 transform 缩放——手指按下去瞬间放大,松手回弹易误触相邻元素
底部悬浮条滚动时“呼吸式”显隐,用 requestAnimationFrame 而非 scroll 直触
监听 scroll 事件直接操作 display 或 opacity,iOS Safari 和低端安卓机会卡顿甚至丢帧。必须节流 + 帧同步。
关键逻辑:
立即学习“前端免费学习笔记(深入)”;
let lastScrollY = 0;
let ticking = false;
<p>function updateBarVisibility() {
const currentScrollY = window.scrollY;
const isScrollingDown = currentScrollY > lastScrollY;</p><p>if (isScrollingDown && currentScrollY > 200) {
bar.classList.add('hidden');
} else if (!isScrollingDown) {
bar.classList.remove('hidden');
}
lastScrollY = currentScrollY;
}</p><p>window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
updateBarVisibility();
ticking = false;
});
ticking = true;
}
});- 加
Math.abs(currentScrollY - lastScrollY) > 5过滤 iOS 地址栏抖动 -
.hidden应用opacity: 0; pointer-events: none;,而非display: none,避免 DOM 重排 - 显隐过渡也走
transition: opacity 0.25s ease-out,和 hover 动效节奏一致
移动端固定条被键盘顶出视口?bottom: env(safe-area-inset-bottom) 是解
iOS 键盘唤起时,bottom: 20px 会把条压到屏幕外。硬编码值失效,必须用环境变量适配安全区域。
CSS 写法:
.floating-bar {
position: fixed;
bottom: calc(20px + env(safe-area-inset-bottom));
left: 20px;
right: 20px;
z-index: 1000;
}-
env(safe-area-inset-bottom)在无刘海/无键盘时为0px,有键盘时自动补足高度(通常20–40px) - 必须配合
viewportmeta 的viewport-fit=cover才生效:<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> - Android 无需此处理,但加了也不报错,可全端统一写
z-index 失效不是数值不够,是父容器创建了局部层叠上下文
即使设了 z-index: 9999,按钮仍被遮挡,大概率是它某个祖先元素带了 transform、filter 或 will-change。这些属性会强制创建新的层叠上下文,让子元素的 z-index 只在该上下文内生效。
- 检查 DOM 结构,确认按钮是否挂在
<body>下——最稳妥方案是 JS 动态document.body.appendChild(bar) - 若必须嵌套,确保最近的定位祖先(含
position: relative/absolute/fixed)没带transform: translateZ(0)类似写法 - 用浏览器 DevTools 的 Layers 面板,看按钮是否被框进某个「Paint Rectangle」里,被截断即说明上下文隔离
微动效真正难的不是写几行 transition,而是所有交互路径都得闭环:hover、focus、active、键盘 Tab、屏幕阅读器 announce、iOS 键盘收起后位置恢复——漏掉任意一环,用户就会在某个场景下“点不到”或“看不见”。



















