已经有不少前端同行抱怨iScroll4的各种问题,我个人并不赞同将这些问题归咎于iScroll4,因为iScroll4进无论是touch事件的捕获,还是使用transform来处理滚动,以及将cubic-bezier应用到transition上实现高效的平滑滚动,这些原理我们都是已知的。更多原理细节可以参看[译]手把手教你编写iOS上Mobile Web App实现Fixed Position和模拟滚动 一文。此文则来解释另一个被很多人误解为iScroll4 造成的问题,那就是内容多的页面闪动的问题。现象表现为:

1.滑动过程中,滚动区内的元素在释放手指时出现细微闪动

2.图片缩放过程中释放手指时明显闪动

3.iOS上从mobile safari打开或第三方应用内打开后,切回主屏幕再切回页面时明显闪动

4.页面初始时闪动

大致这些情况下会有闪动,在ipad2,new ipad上会明显看得出。

下面就来分析产生的原因和解决办法:

首先,我们知道iOS5给我们带来了

overflow-y: scroll;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;

实现固定区域内的滚动,当应用这种方式实现滚动时,上面的提到的现象也会不同程度的出现尤其是1,2.是那么我们就可以断定不是iScroll4 的原因了。那么究竟是什么原因呢?

webkit在绘制页面时会将结构分为各种层,当层足够大时就会变成很大的平铺层。这各一来webkit在每次页面结构发生变化时不需要都渲染整个页面而是渲染对应层了,这对渲染速度来说相当的重要。webkit会给各种层分配一定大小的“备份存储区”在内存里缓存起来,这就是绘制层的上下文,通过这个上下文就可以很容易的实现各种效果(动画,3D变换等),“备份存储区”内存占用大小不仅依层而定,跟设备和显示方式也是有关的,假设这在普通屏幕下是1:1的,但在Retina屏幕下则是1:2的,并且放大时这个量会成倍增加;一张图片是10X10,普通屏幕分配的就是10X10,Retina初始则是20X20。这也表明Retina是更加消耗内存的。当层很大时,意味着“备份存储区”会消耗更大的内存,为了避免这点,webkit并不会绘制一个很大的层来存储一个很大的页面,比如说平铺层则会拆分成很多的块来绘制,即尽占用尽可能小的内存,只是将可视范围内的那部分渲染出来。这就是为什么我们在大页面滚动时会发现下面的内容慢慢显示,向上滚动时上面的内容还慢慢显示的原因。

以下则是webkit划分为层绘制的场景:

  1. 页面主容器永远是独立的平铺层
  2. 绘制密集型元素时,如<video>, <canvas>
  3. 应用3D transformations的元素,包括translate3d, rotate3d, translateZ
  4. 内容被加强时,如Filters, masks, reflections, opacity, transitions, animations
  5. 某些特殊的情况下也会,如position:fixed, -webkit-overflow-scroll:touch
  6. 任何在已知层上覆盖的内容

这对我们解决闪动问题有什么帮助呢?按上页的说明,和iScroll4实现原理我们很容易知道,iScroll4作用的滚动区是一个很大的独立层,webkit是不会将这么大的层整个分配内存绘制渲染的,所以,只要将滚动区域可视范围的列表项元素缓存起来就解决这个问题了。记webkit强制缓存起来即是将他们独立成一个层,而且这个层当然不会很大否则会被视为平铺层处理了。一般列表里项里的元素不会像页面主容器一样的大的。实现方式就是将列表项置身于上面的6个场景中,比如:

        #wrap>section>article{-webkit-transform:translateZ(0);}/*注意这里*/
        <div id="wrap">
            <section>
                <article>1</article>
                <article>2</article>
                <article>3</article>
            </section>
        </div>