浅谈移动设备交互体验之惯性滚动(移动端惯性滑动)
zhezhongyun 2025-02-04 17:07 41 浏览
很久以前,手机上的交互依赖键盘和触控笔。我们要查看一个很长很长的列表,必须使用非常难用的触控笔或键盘的上下左右键。后来黑莓发明了滚动球,缓解了大拇指按出茧的问题。
2007年,苹果推出iPhone。iPhone只有一个玻璃屏,没有触控笔,直接用手指操作,支持多手指。Multi-Touch这项技术在推出时被誉为和Mouse(Mac),Click wheel(iPod)一样革命性的发明。当然最近的3D Touch(Apple watch)也是革命性的。在推出iPhone之前,苹果已经做了多年的铺垫。2005年收购的小公司finger works就是专门做手势识别的团队,用macbook的人几乎是脱离鼠标的,因为其触控板非常好用,所用技术来自这家公司。
当时有很多程序猿和产品经理讨论这个技术如何实现。疯狂的Web开发者要在浏览器上实现基于鼠标中键的滚动效果:当很快地滚动中键并停止时,页面由于惯性,会继续往下滚动一段距离才停止。有一个专业名词用来描述,叫Momentum Scrolling。还有人用Web写了示例,用来模拟手指和屏幕交互过程中的数学逻辑,iScroll 和 Scrollability 都是不错的作品。头条的Web图集也有使用Javascript(以下简称JS)实现的惯性滚动代码。下面我们用最简单的Scrollability来讲解,如何实现惯性滚动。
一. 实现滚动
HTML代码:
JS代码:
页面中有四个元素,顶部的黑条,底部的黑条,黑条中的窗口A,以及窗口内一堆英文名列表B(可以想象成办公室的卷帘)。坐标系从窗口A左上角开始,横轴X(右边为正向),纵轴Y(下边为正向)。注意,这里Y轴的方向和数学课本里相反。手指在窗口A上移动,往下移动一段距离,得到的 distanceY > 0。代码由HTML,CSS和JS构成,HTML定义了两个黑条,窗口A和卷帘B。CSS定义了他们的颜色,位置等属性。Javascript则是重点要讲的,它用来控制手指和屏幕的交互。JS中定义了几个功能和变量,其中kBounceLimit这样以k开头的,是数学公式中的固定不变的参数。startX,startY,touchX,touchY用来记录手指位置,touchAnimator是负责操作卷帘B滚动的对象。
这几行代码表示,当用户手指触碰到屏幕时,会执行onTouchStart,去做一些事情。手指在屏幕上移来移去时,会执行onTouchMove。手指离开屏幕的瞬间,硬件会通知程序手指离开,此时执行onTouchEnd。touchstart和touchend都是瞬间事件,从手指按下,移动,抬起的一套过程中只会被通知一次,touchmove是连续触发的,移动1厘米,会收到几十个通知,告诉程序当前手指坐标x,y,时间点。
让卷帘随着手指移动的办法是,在touchstart时,记录手指位置y1;touchmove时根据得到y2,算出当前移动的距离distance=y2-y1,将卷帘在y轴上移动distance距离; touchmove不断触发,不断执行前面两步,把上一次的touchmove当作起始点,紧接着触发的touchmove当作后来的点,计算distance移动卷帘。
iPhone在处理滚动的过程中,当页面已经到最顶部时,再往下拉会有弹簧效果:手指拉了一个屏幕的距离,内容只移动了半屏。用这样的代码实现其效果:
velocity的含义是:手指在屏幕移动一段距离,触发n个touchmove事件,相邻两个事件的y坐标之差就是velocity(速度)。
打个比方,老李跑步,他的步长就是 velocity。当老李还没跑,想逃出森林公园南门时会有人把他往回拉。Velocity是1米,往回拉的弹性会给velocity打折,身体移动距离可能是0.8米,具体的折扣为:
(1.0 - (position - max) / bounceLimit)*kBounceLimit
position:当前卷帘移动到的位置,(逃出南门的距离)
parentOffsetHeight : 598 窗口A的高度
nodeOffsetHeight : 4602 卷帘B的长度
max:0
min:-4004 (parentOffsetHeight-nodeOffsetHeight,卷帘被挡住的长度,从下往上拉卷帘,最多移动的距离就是min值)
absMin = min
absMax = max
bounceLimit :窗口高度 parentOffsetHeight*kBounceLimit
kBounceLimit:0.75
转换后,速度velocity衰减的系数为 0.75 - position/ 598,当position越大,velocity打折越多,越难往下拖动。
二. 惯性和回弹
接着,我们用代码实现自由滚动和下拉时的回弹
在touchend时,加入takeoff()方法
takeoff方法中,根据手指离开屏幕时的velocity计算后面滚动动画的代码:
savekeyframe方法会保存计算出来的每一帧动画,包含位置和时间点。
自由滚动时的状态:
在顶部回弹时的状态:
position = easeOutExpo(decelStep, decelOrigin, decelDelta, kBounceTime);
continues = ++decelStep <= kBounceTime && Math.floor(Math.abs(position)) > max;迭代计算position的公式为缓动曲线easeOut,接受的四个参数为:
decelStep :初始值为0,每次减速,自增1
decelOrigin :decelStep为0时 position值
decelDelta :decelStep为0时 max-position
kBounceTime :240
对比linear,ease-in,ease-out和ease-in-out四个曲线,ease-out方程x=easeOut(t)在一开始有平缓的加速,在时间t到50%时,位置x已经快到达70%,
在后续50%的时间内减速直到停止。符合我们平时看到的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; // Math.abs(velocity) < 0.1
顶部回弹时:continues = ++decelStep <= kBounceTime && Math.floor(Math.abs(position)) > max; // decelStep<=240加位置约束
底部回弹时:continues = ++decelStep <= kBounceTime && Math.ceil(position) < min; //同上
底部上拉的状态,同上。
saveKeyframe(!continues);
time += kAnimationStep;作用是根据diff判断是否保存这个关键帧,最后得到位置和时间的运动轨迹
根据keyframes得到运动轨迹,再使用实现滚动的代码,让页面运动起来。这里就不展开说明了
(gif图略卡,实际效果流畅很多)
三. 浏览器实现
苹果和谷歌在意识到这个需求后,为web开发者提供了原生的支持。
之前需要用iscroll等框架的需求,Chrome浏览器只需 overflow:scroll 一行代码即可。iOS设备需再加一行代码 -webkit-overflow-scrolling: touch;
原生实现的好处是
计算位置的过程要进行大量小数运算,这一点JS非常慢。
惯性滚动是苹果专利,原理涉及数学物理知识很多,远比web开发者模仿出来的要复杂,而且不开源。
如果滚动的内容非常长(手机通讯录,微信朋友圈等),要做延迟渲染,生成瓦片,贴图,销毁或者回收,内存管理,这些工作必须由系统支持,而不是业务支持。
iOS支持Scroll Snapping with CSS Snap Points(点击访问),在滚动结束时对齐到内部元素,只需要两行css代码。
随着手机硬件的发展,浏览器和操作系统在图形渲染,网络API,数据存储等方面的差距越来越小。而且浏览器面向业务,对底层架构的抽象更好,一行css,一个javascript对象可以替代几千行java代码(or 几百行swift代码?)。很多只在操作系统中提供的功能,例如消息推送,也已经在浏览器实现。
Web的开放性让很多公司都对其添砖加瓦。你很难想象微软会往Android仓库中贡献代码,或者谷歌给IE浏览器解决bug,但他们确实都在为HTML5标准做贡献。另一个例子是Adobe,他们希望将Photoshop中的滤镜,图层混合模式等功能提供给web开发者,于是提议做了css filter,css blender。
http://sarasoueidan.com/demos/css-blender/
还有很多简单好玩的东西,以后会在博客中慢慢写。如有描述错误和不当,请批评指正。
作者:王伟
相关推荐
- 3 分钟!AI 从零开发五子棋全过程曝光,网友:这效率我服了
-
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8...
- 一行代码实现display"过渡动画"原理
-
作者:Peter谭老师转发链接:https://mp.weixin.qq.com/s/XhwPOv62gypzq5MhhP-5vg写本文的起因上篇文章,提到如何让display出现过渡动画,却没有仔...
- 脑洞:琼恩·雪诺、蝙蝠侠和魔形女的灵魂宠物了解一下
-
AlekseiVinogradovisaRussianfreelancedigitalartistwhoshareshisskillsandtalentwith120k...
- 浏览器的渲染机制、重绘、重排
-
1、什么是重排和重绘网页生成过程:HTML被HTML解析器解析成DOM树css则被css解析器解析成CSSOM树结合DOM树和CSSOM树,生成一棵渲染树(RenderTree)生成布局(flo...
- 托福写作高频考题写作思路&词汇丨考虫独家
-
科技话题与媒体话题是托福写作的常考话题很多考生对这两类话题里的专有词汇表达也许很不了解所以今天就跟随考虫托福写作老师刘云龙老师一起来学习在这些话题的写作里你可以使用哪些有用的表达。希望大家有收获!记得...
- 在优麒麟上使用 Electron 开发桌面应用
-
使用Web标准来创建桌面GUI,上手快、成本低、跨平台、自适应分辨率,这些都是Electron的优势。作者/来源:优麒麟Electron是由Github开发,用HTML、CSS和...
- php手把手教你做网站(三十八)jquery 转轮盘抽奖,开盲盒
-
抽奖和开盲盒性质一样的都是通过ajax读取后台的随机数据。1、转轮盘本来是想直接绘图实现轮盘,但是没有找到怎么填充文字,只好把轮盘弄成了背景图,通常用于游戏抽道具,商城积分抽奖,公司年末员工抽奖点击抽...
- 用 CSS 整活!3D 轮播图手把手教学,快乐代码敲出来
-
兄弟们,今天咱来搞点好玩的——用CSS整一个3D轮播图!咱野生程序员就是要在代码里找乐子,技术和快乐咱都得要!代码是写不完的,但咱能自己敲出快乐来,走起!一、先整个容器,搭个舞台咋先写一个...
- 实现一个超酷的 3D 立体卡片效 #前端开发
-
今天我们来实现一个超酷的3D立体卡片效果。正常情况下就是一个普通的图片展示卡片,鼠标悬停的时候图片会跳出卡片,并将影子投射到背景卡片上,在视觉上有一个3D立体感。html主要分成3个部分:容器→背景层...
- Vue 3 Teleport与Suspense:解决UI难题的两个"隐藏大招"
-
模态框的"层级噩梦"与Teleport的救赎"这个模态框怎么又被父容器截断了?"团队协作开发后台系统时,小张第N次遇到这个问题。多层嵌套的组件结构里,弹窗被overfl...
- 让交互更加生动!有意思的鼠标跟随 3D 旋转动效
-
今天,群友问了这样一个问题,如下所示的鼠标跟随交互效果,如何实现:简单分析一下,这个交互效果主要有两个核心:借助了CSS3D的能力元素的旋转需要和鼠标的移动相结合本文,就将讲述如何使用纯CSS...
- 填坑:transform元素导致zindex失效终极方法
-
今天遇到了使用css3动画的元素层级被放大置顶的问题,ios浏览器上没问题,安卓原生浏览器和安卓微信上有问题。使用了css3动画的元素z-index失效,兄弟元素设置多高的z-index都盖不住解决办...
- 诡异的层级错乱:一个被transform隐藏的CSS陷阱
-
周五下午三点十七分,设计部突然发来紧急截图——原本应该悬浮在顶部的导航菜单,此刻正诡异地被下方的轮播图遮挡。我盯着屏幕上错乱的层级关系,手指下意识地敲下z-index:9999,心里清楚这不过是程序...
- 动画篇--碎片动画
-
本文授权转载,作者:Sindri的小巢(简书)前言从最开始动笔动画篇的博客,至今已经过去了四个多月。这段时间回头看了看自己之前的动画文章,发现用来讲解动画的例子确实不那么的赏心悦目。于是这段时间总是想...
- Nature:大洋转换断层处的拉张构造与两阶段地壳增生
-
Nature:大洋转换断层处的拉张构造与两阶段地壳增生转换断层是三种基本的板块边界之一,全球总长度超过48000km(Bird,2003),它们的发现为板块构造理论的建立奠定了重要的基础(Wil...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- CSS 水平对齐 (Horizontal Align) (30)
- opacity 属性 (32)