webkit---缩放重排

来源:互联网 发布:软件测试的发展前途 编辑:程序博客网 时间:2024/04/27 10:32

        有的网页(特别是桌面版网页)在手机上浏览时字体特别小,用户们不得不放大才能看清,为了避免网页放大后需要左右移动来看其内容,浏览器一般都会很贴心的对该网页的文字进行重排,以适应屏幕的宽度。这个功能很实用,但现在好像并不是webkit的自带功能,而是需要各家浏览器独自实现。那么这个功能主要涉及到两个技术点:缩放重排和重新定位。

        首先说一下缩放重排,实际上就只需要设置RenderBlock节点的排版宽度,然后借由其本身的layout过程就可以了。那么排版宽度设置成多少?原始的排版宽度是屏幕宽度width,缩放之后的排版宽度就是(width/此时缩放比例)。正常的排版是无视缩放因子的,而是在排版完成之后再按照缩放因子进行缩放。还有一点,显然并不是所有的节点都需要重排,这就需要大家自己判断了,一般只需要重排含有较多文字的节点。

        另一点是重新定位,在缩放重排之后,网页的布局会发生变化,将用户原本正在看的那段文字移到屏幕中间需要一定的判断。特别是在缩放比较激烈时如何准确做到这一点就比较有难度了。一般的做法是取两个手指的中点,认为这个点就是用户正在关注的点。在重排之前使用hitTestResultAtPoint来判断该点落在DOM树到哪个node节点上,并记录该点距离该node节点四周边界的比例。重排之后,重新定位到这个node节点,x轴可以直接与node的左边界对齐(为了美观也可以用算法稍微修饰下位置),y轴可以根据之前记录的老节点的比例,应用到新节点上。

        上述方法有一个问题,就是缩放比较激烈时y轴容易定位不准。因为重排之后节点的形状改变了,在Y轴方向上被拉长了,之前记录的比例在新节点上肯定有出入。不知大家有没有更好的方法,希望不吝赐教。

        在什么时机进行定位呢?chromium中WebViewImpl::applyScrollAndScale函数是个不错的时机。在chromium中,缩放和滚动这种消息传到Render进程都不会给webkit,而是送到CC模块进行处理,记录上层传下来的数据。然后经过如下过程:

#0  WebKit::WebViewImpl::applyScrollAndScale
#1  WebKit::WebLayerTreeViewImpl::applyScrollAndScale
#2  cc::LayerTreeHost::applyScrollAndScale
#3  0x58675824 in cc::ThreadProxy::beginFrame

        到达applyScrollAndScale函数,注意并不是每次beginFrame都会到达这个函数,它会判断缩放和滚动因子有没有发生变化,只有缩放或者滚动因子改变了才会到达applyScrollAndScale。滚动就不必说了,那么缩放因子是怎么判断是否有变化呢(比如我们在放大过程中,只要手不松开,实际上不会到达applyScrollAndScale)?是由LayerTreeHostImpl::processScrollDeltas这个函数判断的。

        if (m_pinchGestureActive)
                computePinchZoomDeltas(scrollInfo.get());
        else if (m_pageScaleAnimation.get())
                computeDoubleTapZoomDeltas(scrollInfo.get());

        可以看到,在缩放过程中(手不松开),通过computePinchZoomDeltas来判断。一旦缩放完成(手松开)就用computeDoubleTapZoomDeltas来判断。

        看computePinchZoomDeltas的代码可以看到:

       if (m_pinchZoomViewport.pageScaleDelta() > 0.95)
               return;

       pageScaleDelta是对比之前状态的缩放因子,也就是说只有缩小,并且缩小幅度超过原来的95%时才会计算,放大时并不计算。原因是,放大过程中并不需要刷新屏幕,而缩小过程中如果不刷新屏幕,由于现在浏览器都是网格方式渲染的,会造成原先viewport之外的内容由于缩小而进入屏幕,但是没有及时得到刷新而显示空白。所以缩小的过程中需要刷新。实际上现在有的浏览器缩小的过程中也不进行刷新,会有一点点用户体验上的影响。

       无论如何缩放完成(手松开)一定要重新计算,并走到WebViewImpl::applyScrollAndScale函数。这个函数需要实现这么几个功能:

       1. 计算绝对的缩放因子和滚动位置

       2. 设置重排宽度

       3. 计算被击中的DOM树node节点,记录相关信息

       4. 调用WebViewImpl::setPageScaleFactor,该函数最终导致实现重排

       5. 通过重排后新节点位置,计算移动数据

       6. 调用setScrollPosition来移动

       原本的WebViewImpl::applyScrollAndScale函数只有 1 4 两步,为了实现重排定位,其余都是新增的

      至此,一个缩放重排功能的大体框架就介绍完了,为了更好的用户体验其中还有很多细节需要细细打磨。

0 0