Web基础体验改进一:滑动闩锁(Scroll Latch)

1. 背景

chromium中网页的可滑动子节点滑动到边缘后,再次滑动会带动父节点滑动。在一些场景中需要将发生在子节点的滑动事件锁住,即使滑动到边缘也不带动父节点的滑动。例如以下页面中间的歌词部分滑动到边缘后,再次滑动不希望将未消耗的滑动量传递到父节点,目前是通过js来实现,如果能够在chromium中直接实现滑动持久闩锁的效果,可以不必使用js。

image

2. scroll手势Begin处理流程

image
image

在InputHandlerProxy::HandleInputEvent中kGestureScrollBegin对应于scroll手势的起始处理,kGestureScrollUpdate对应于scroll手势的进行处理,kGestureScrollEnd对应于scroll手势的结束处理。

image

在起始处理过程中,最终会调用LayerTreeHostImpl: DistributeScrollDelta进行滑动链(current_scroll_chain)的确认,scroll_chain中放入可以消耗滑动量的scroll node,子节点在链表尾,父节点在链表头。Scroll node 与 active layer对应关系为每个layer都会指定4颗property tree的一个节点。

image

关于4颗属性树的相关信息在src/cc/trees/property_tree.h里

image

3. scroll chain

image

其中viewport_scroll_node对应于网页的滑动节点,当该节点被放入滑动链时,有可能会将滑动量传递到网页本身,即网页子元素的滑动有可能会传递到网页本身,由于chromium做了scroll latch,滑动嵌套时不会将滑动传递到parent节点,但是二次滑动时就会直接滑动parent节点(子节点无法消耗滑动量,没有进入scroll chain)。

4. scroll latch

要想实现可滑动子元素在滑动到边缘后后续滑动都不带动parent节点,可以在scroll chain中进行更改。当滑动发生在可滑动子元素上时,通过CanConsumeDelta判断该节点是否能够消耗滑动量,如果可以则进入滑动链。而当可滑动子节点以及滑动到边缘时,此函数返回false则滑动链跳过该子节点,直接加入父节点。所以可以在此进行判断等相关处理,若存在不可消耗滑动的子节点,父节点不进入滑动链,此处细节部分是对滑动方向的一致性判断,防止横向滑动锁住垂直滑动。

Scroll node有水平和垂直滑动的属性,但是从测试结果来看,只要area是可滑动的两者都为true,此性质无法使用。可以从如何判断滑动是否能够消耗来进行处理,详细见CanConsumeDelta函数。

image

修改DistributeScrollDelta函数,首先获取当前滑动的手势方向并创建是否需要持久闩锁的标志。

根据是否需要持久闩锁,判断页面节点是否能进入scroll chain。

image

判断当前scroll node的可滑动方向是否与手势的滑动方向完全一致,如果完全一致且不能消耗滑动量,设置持久闩锁标志。

image

(ps:以上实现是针对上级滑动节点为整个网页节点的情形,如果是多层滑动嵌套,可以直接在child_scroll_should_latch=true时break即可)。

所以进行修改后滑动链将为空,此时LayerTreeHostImpl::ScrollBeginImpl处理为返回scroll_ignore信息。

image

到了InputHandlerProxy:: HandleGestureScrollBegin函数中后,render端会丢弃该滑动事件并且通知browser端,最终该滑动事件没有产生效果,即没有带动父节点滑动,子节点滑动在边缘被持久闩锁。

5. 调试

【修改前】

首次滑动到边缘时,滑动的子节点4和滑动的父节点3都进入scroll chain。

image

在边缘处再次滑动时,滑动的子节点已经不能消耗滑动量,所以只有父节点3进入scroll chain,从而将滑动了整个网页。


image

【修改后】
在边缘处再次滑动时,发生持久闩锁


image

你可能感兴趣的:(Web基础体验改进一:滑动闩锁(Scroll Latch))