头部(或尾部)固定,内容区域滚动,在手机APP的UI设计中已经成为一种标准,在此基础上还衍生出了下拉刷新等经典的交互设计。
而在WEB页面中,要模拟实现类似的效果非常麻烦,虽然理论上position:fixed加-webkit-overflow-scrolling:touch两个样式就可以解决,但实际开发中考虑到性能和低版本浏览器的兼容问题,仅仅使用样式并不是一个可接受的解决方案。
于是乎,诞生了不少处理滚动的JS组件,比较有代表性的如TouchScroll,iScroll等,在页面内容相对有限的情况下,使用这些组件来处理滚动效果很不错,zepto加iScroll在一段时间内也几乎成为WEBAPP开发的标准配置。
但是,当页面数据量较大时,例如某个产品分类下有数百条数据时,当然在PC端一般一定会使用分页来解决,但是由于手机APP的使用习惯,用户更喜欢不断向下滚动来加载更多数据,在这种情况下,按照传统做法,页面的DOM节点会不断增加,随之带来的效果是,闪烁,滚动动画帧数下降,甚至页面崩溃。
很容易想到,要实现数据列表的流畅滚动,首先需要限制DOM的数量。由于本人爱好电子游戏,此时想到了2D卷轴游戏中著名的Carmark scroll,其中有一个重要理念,把移出屏幕的地图tile无用部分重新渲染并拼接到移入屏幕部分,地图tile的总块数不变。把这个概念进行一下转化,和HTML页面以及DOM结合起来考虑,如果我们把每一个DOM列表项节点当作一块TILE并进行绝对定位,在页面滚动时,把消失于滚动区域的节点进行重新定位和内容填充,这样就实现了页面不停滚动,内容不断变化,DOM节点数却不会增加,这样就基本解决了传统方法遇到的问题。
结合上面的文字描述和下图就很好理解了:
根据上述思路,我实现了一个简单的滚动组件。
下载地址:
http://3gimg.qq.com/cube/listView.min.js
http://3gimg.qq.com/cube/listView.js
调用方法如下:
var list = new Cube.ListView({ scrollBody: document.querySelector('.scroller'), //滚动容器 template: ' <%=data.content%> ', // 列表项的模板 itemHeight: 101, // 单个列表项的高度,暂时只支持等高的列表项,后续计划支持任意高度 dataList: (function() { var arr = [], i; for (i = 1; i <= 1000; i ++) { arr.push({content:'第' + i + '项'}); } return arr; }()) // 数据数组,JSON格式的数据,数据字段和上面模板里的标记对应 });
可以到DEMO页面测试:http://cube.qq.com/apps/listViewDemo.html
需要注意的是,由于在滚动时会进行单个列表项内部结构的频繁替换,当单个列表项DOM结构相对复杂时,会影响滚动的帧数,这里有一个优化方案是,滚动中间经过的项只进行局部填充,仅对滚动停止后最后一屏的列表项进行完整填充。
组件内部的模板函数使用的是jQuery.tmpl风格的模板语法,这里使用者可以替换成自己习惯的模板引擎或函数,对应代码在replaceTpl方法里。
同时,因为列表项DOM和数据并非一一对应,在需要绑定事件的时候,不可能对每一项一一绑定,这时使用事件代理方式来绑定事件就可以了。
该组件目前已经应用于手机QQ浏览器每日精选更多页面,可以用4.3及以上版本的手机QQ浏览器访问查看效果。
本文出自Tencent Wuhan Blog