纯css无法实现弹性下拉效果,因其无法读取touchmove中的deltay来动态映射缩放比例;必须由js捕获触摸距离并设置css自定义属性(如--pull-progress),再通过transform:scaley()配合transition和cubic-bezier实现随手指实时拉伸、回弹的阻尼效果。

为什么纯 CSS 无法实现“弹性”下拉效果
「弹性」不是指动画缓动,而是指视觉上能随手指下拉距离实时缩放、拉伸、回弹——这必须由 JS 捕获 touchmove 中的 deltaY 并映射为 CSS 自定义属性(如 --pull-progress),CSS 本身不能读取触摸坐标。直接写 @keyframes 或 transform: scaleY(1.2) 是静态的,松手后不会自动收缩,更不会根据下拉力度变化。
用 transform: scaleY() + overflow: hidden 实现顺滑拉伸
核心是让 loading 图标容器高度为 0,靠 scaleY() 拉伸而非改变 height,避免重排;同时父容器设 overflow: hidden 防止内容溢出干扰。
-
.loading-indicator必须设height: 0、overflow: hidden,且内部元素用display: flex居中 - 拉伸用
transform: scaleY(calc(var(--pull-progress) * 0.8)),其中--pull-progress由 JS 动态设置(0~1) - 加
transition: transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94),比ease-out更贴合原生阻尼感 - 别用
top或margin-top控制位置——它们触发重排,且松手回弹时易跳变;改用position: absolute+top: calc(-100% + 1px)
touchmove 中如何安全更新 --pull-progress
错误做法:监听 document 或 window,或未判断 scrollTop === 0 就更新。这会导致横向滑动卡片失效、iOS 键盘失焦、甚至触发页面整体拖拽。
- 只监听滚动容器(如
#refresh-wrapper),且绑定时显式传{ passive: false } - 仅当
el.scrollTop === 0 && deltaY > 0时才计算并设置--pull-progress -
deltaY = currentY - startY,但要限制最大值(如 80px),再归一化:Math.min(deltaY / 80, 1) - 松手瞬间若
--pull-progress > 0.6,则设--loading: 1触发旋转;否则设回 0,靠 transition 自动回收
图标旋转与呼吸脉冲需分状态控制
一个 loading 图标常需三阶段反馈:下拉中缓慢旋转、松手后单圈加速、加载中持续旋转+透明度脉冲。全塞进一个 @keyframes 会错乱。
- 定义三组 class 或用自定义属性切换:
.loading-pull(animation: spin 2s linear infinite)、.loading-ready(animation: spin 0.6s cubic-bezier(0.3, 0, 0.7, 1) forwards)、.loading-active(animation: spin 0.8s linear infinite, pulse 2s ease-in-out infinite) -
transform-origin: center必须加在旋转元素上,否则 border 圆形会偏移 - 脉冲用
opacity而非scale,避免和拉伸动画冲突 - JS 切换状态时用
el.classList.replace("a", "b"),不是add/remove—— 否则动画可能中断或叠加
最易被忽略的是 overscroll-behavior: contain:不加它,iOS 下拉超限时整个页面会橡皮筋式拖拽,导致 scrollTop === 0 判断失效,loading 区域定位漂移。这个样式必须加在滚动容器上,而不是 body 或 html。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











