从笨拙到流畅:惯性滚动如何重塑移动体验
你是否还记得,早年手机交互是多么笨拙?那时,我们完全依赖键盘和触控笔。想浏览一个长长的列表?只能不断点击难用的方向键或拖着触控笔滑动,直到黑莓的轨迹球出现,才让拇指免于起茧之苦。

2007年,iPhone彻底改变了规则。它只有一个玻璃屏,抛弃触控笔,让手指直接操控。Multi-Touch这项技术被誉为与Mac鼠标、iPod点按轮同级的革命。背后是苹果的深远布局:2005年收购的Fingerworks公司,其手势识别技术早已让MacBook触控板体验封神。
当时,无数开发者和产品经理为之疯狂:如何在网页上实现那种自然的惯性滚动?当快速滚动并突然停止,页面仍能平滑滑行——这就是Momentum Scrolling。社区涌现了iScroll、Scrollability等开源作品,甚至头条Web图集也采用了类似方案。今天,我们就用最简化的Scrollability,拆解这套“指尖魔法”。
一. 实现滚动:从触控事件到画面响应

HTML代码:
JS代码:
界面包含四个元素:顶部黑条、底部黑条、中间窗口A,以及窗口内的列表B(想象成办公室卷帘)。坐标系以窗口A左上角为原点,X轴向右为正,Y轴向下为正(注意与数学坐标相反)。手指在窗口A下滑时,distanceY > 0。HTML和CSS定义了结构与样式,而Javascript则核心控制交互逻辑。JS中,kBounceLimit等k开头参数为常量;startX/Y、touchX/Y记录触点位置;touchAnimator操控列表B的滚动。

当手指触碰屏幕,触发onTouchStart;移动时连续触发onTouchMove;抬起时触发onTouchEnd。touchstart和touchend是瞬时事件,而touchmove在移动中高频触发,实时汇报坐标与时间戳。
让列表随手势移动的逻辑很简单:touchstart时记录起始位置y1;touchmove中根据新位置y2计算位移distance=y2-y1,并移动列表;后续touchmove将前一点作为新起点,循环此过程。
iPhone的滚动还包含“弹簧效果”:当列表已到顶仍下拉时,内容移动距离小于手指拖动距离。实现代码如下:

velocity(速度)是相邻touchmove事件间的Y坐标差。想象一下:老李跑步的步长就是velocity。当他靠近森林公园南门想逃出时,会被弹性拉回。若velocity为1米,弹性折扣可能使其实际移动0.8米,折扣公式为:
(1.0 - (position - max) / bounceLimit)kBounceLimit
其中:position是列表当前位置(类比逃出门的距离);parentOffsetHeight(窗口A高)为598;nodeOffsetHeight(列表B长)为4602;max为0;min为-4004(即parentOffsetHeight-nodeOffsetHeight,列表可上拉的最大距离);bounceLimit为窗口高乘以kBounceLimit(0.75)。转换后,速度衰减系数为0.75 - position/598——position越大,折扣越高,下拉越吃力。
二. 惯性与回弹:让滚动拥有“生命力”
接下来,用代码赋予滚动自然的惯性与边界回弹。

在touchend时加入takeoff()方法:
takeoff根据离手时的velocity计算后续动画:
saveKeyframe方法保存每一帧动画的位置与时间点。
自由滚动状态:
顶部回弹状态:
position = easeOutExpo(decelStep, decelOrigin, decelDelta, kBounceTime);continues = ++decelStep <= kBounceTime && Math.floor(Math.abs(position)) > max;
position通过缓动曲线easeOut计算,参数包括:decelStep(从0开始递增)、decelOrigin(初始position)、decelDelta(max-position)、kBounceTime(240)。对比linear、ease-in、ease-out和ease-in-out曲线,ease-out在前期加速平缓,50%时间点已达成约70%位移,后续逐渐减速至停——完美契合iOS滚动体验。
function easeOutExpo(t, b, c, d) { return (t==d) ? b+c : c (-Math.pow(2, -10 t/d) + 1) + b }continues判断逻辑:
自由滚动时:continues = Math.floor(Math.abs(velocity)10) > 0; // 即velocity绝对值小于0.1时停止
顶部回弹时:continues = ++decelStep <= kBounceTime && Math.floor(Math.abs(position)) > max;
底部回弹时:continues = ++decelStep <= kBounceTime && Math.ceil(position) < min;
底部上拉逻辑类似。
saveKeyframe(!continues); time += kAnimationStep; 根据diff决定是否保存关键帧,最终生成运动轨迹
基于keyframes轨迹,调用滚动代码驱动页面运动。细节不再赘述。
(GIF图略有卡顿,实际体验流畅得多)
三. 浏览器原生支持:从复杂模拟到一行代码
苹果与谷歌后来将惯性滚动原生植入浏览器,极大简化了开发。

曾经需要iScroll等框架实现的效果,现在Chrome只需overflow:scroll;iOS设备加一行-webkit-overflow-scrolling: touch即可。
原生优势明显:
大量浮点计算在JS中效率低下,而原生实现性能卓越。
惯性滚动涉及复杂物理数学,是苹果专利,闭源且远比社区模拟精准。
长列表(如通讯录、朋友圈)需延迟渲染、瓦片化、内存管理等,必须系统层级支持。
iOS还支持CSS Scroll Snapping,两行代码就能让滚动结束时对齐元素。

随着硬件进化,浏览器与操作系统在图形、网络、存储等领域的差距日益缩小。浏览器面向业务,抽象更优——一行CSS或一个JS对象可替代数千行Java代码。消息推送等原生功能也已登陆Web平台。
Web的开放性吸引了全球巨头贡献:微软为Android贡献代码?谷歌修复IE漏洞?这些在HTML5生态中已成常态。Adobe甚至将Photoshop滤镜与混合模式带入CSS,推出filter与blender模块。
http://sarasoueidan.com/demos/css-blender/

交互世界的奥秘远不止于此。未来我们将继续分享更多有趣技术。如果你对移动交互有独到见解,或想探讨其他实现方案,欢迎在评论区畅所欲言!
作者:王伟
交互进阶问答
移动设备究竟指什么?
移动设备,常称手持设备,是口袋大小的计算工具,配备小屏、触控或迷你键盘,让我们随时随地获取信息。
移动设备的核心意义?
它代表灵活与便捷,绝非固定装置,能随时应对突发需求,重塑了工作与娱乐的边界。
媒体形态如何因移动技术变革?
近年互联网与移动终端技术爆发,媒体多元化成为主流。新媒体崛起,打破电视、报纸、户外广告的旧格局,转向电视、网络与移动端的融合生态。
常见移动设备有哪些?
广义包括手机、笔记本、平板、POS机及车载电脑,但通常特指智能手机与平板电脑。
如何定义移动电气设备?
指工作中需频繁移动,或无需固定基础、常变更位置的电气设备,区别于便携式设备,泛指非固定安装的电气装置。
移动通信系统结构有何优劣?
按业务分包括电话与非话业务;按服务对象分公用与专用网络;按范围涵盖陆地、海上及航空通信。其结构灵活但覆盖与容量挑战并存。
如何优化移动设备性能?
关闭USB与光盘的“自动播放”功能可显著提升运行效率,直接修复相关设置即可。
什么是移动设备登录状态?
当用手机或平板登录QQ,电脑端会显示“正在移动设备上使用”。这仅是设备类型标识,不影响功能。
智能终端涵盖哪些设备?
智能终端指支持音视频与数据的多媒体设备,如可视电话、会议终端、多媒体PC等,它们是移动互联的核心节点。
短距通信如何连接移动设备?
短距通信技术(如蓝牙)能高效简化笔记本、手机等设备间的数据交换,提升传输速度与能效,正成为移动生态的纽带。


