1. 背景
chromium中網頁的可滑動子節(jié)點滑動到邊緣后,再次滑動會帶動父節(jié)點滑動。在一些場景中需要將發(fā)生在子節(jié)點的滑動事件鎖住,即使滑動到邊緣也不帶動父節(jié)點的滑動。例如以下頁面中間的歌詞部分滑動到邊緣后,再次滑動不希望將未消耗的滑動量傳遞到父節(jié)點,目前是通過js來實現,如果能夠在chromium中直接實現滑動持久閂鎖的效果,可以不必使用js。
2. scroll手勢Begin處理流程
在InputHandlerProxy::HandleInputEvent中kGestureScrollBegin對應于scroll手勢的起始處理,kGestureScrollUpdate對應于scroll手勢的進行處理,kGestureScrollEnd對應于scroll手勢的結束處理。
在起始處理過程中,最終會調用LayerTreeHostImpl: DistributeScrollDelta進行滑動鏈(current_scroll_chain)的確認,scroll_chain中放入可以消耗滑動量的scroll node,子節(jié)點在鏈表尾,父節(jié)點在鏈表頭。Scroll node 與 active layer對應關系為每個layer都會指定4顆property tree的一個節(jié)點。
關于4顆屬性樹的相關信息在src/cc/trees/property_tree.h里
3. scroll chain
其中viewport_scroll_node對應于網頁的滑動節(jié)點,當該節(jié)點被放入滑動鏈時,有可能會將滑動量傳遞到網頁本身,即網頁子元素的滑動有可能會傳遞到網頁本身,由于chromium做了scroll latch,滑動嵌套時不會將滑動傳遞到parent節(jié)點,但是二次滑動時就會直接滑動parent節(jié)點(子節(jié)點無法消耗滑動量,沒有進入scroll chain)。
4. scroll latch
要想實現可滑動子元素在滑動到邊緣后后續(xù)滑動都不帶動parent節(jié)點,可以在scroll chain中進行更改。當滑動發(fā)生在可滑動子元素上時,通過CanConsumeDelta判斷該節(jié)點是否能夠消耗滑動量,如果可以則進入滑動鏈。而當可滑動子節(jié)點以及滑動到邊緣時,此函數返回false則滑動鏈跳過該子節(jié)點,直接加入父節(jié)點。所以可以在此進行判斷等相關處理,若存在不可消耗滑動的子節(jié)點,父節(jié)點不進入滑動鏈,此處細節(jié)部分是對滑動方向的一致性判斷,防止橫向滑動鎖住垂直滑動。
Scroll node有水平和垂直滑動的屬性,但是從測試結果來看,只要area是可滑動的兩者都為true,此性質無法使用。可以從如何判斷滑動是否能夠消耗來進行處理,詳細見CanConsumeDelta函數。
修改DistributeScrollDelta函數,首先獲取當前滑動的手勢方向并創(chuàng)建是否需要持久閂鎖的標志。
根據是否需要持久閂鎖,判斷頁面節(jié)點是否能進入scroll chain。
判斷當前scroll node的可滑動方向是否與手勢的滑動方向完全一致,如果完全一致且不能消耗滑動量,設置持久閂鎖標志。
(ps:以上實現是針對上級滑動節(jié)點為整個網頁節(jié)點的情形,如果是多層滑動嵌套,可以直接在child_scroll_should_latch=true時break即可)。
所以進行修改后滑動鏈將為空,此時LayerTreeHostImpl::ScrollBeginImpl處理為返回scroll_ignore信息。
到了InputHandlerProxy:: HandleGestureScrollBegin函數中后,render端會丟棄該滑動事件并且通知browser端,最終該滑動事件沒有產生效果,即沒有帶動父節(jié)點滑動,子節(jié)點滑動在邊緣被持久閂鎖。
5. 調試
【修改前】
首次滑動到邊緣時,滑動的子節(jié)點4和滑動的父節(jié)點3都進入scroll chain。
在邊緣處再次滑動時,滑動的子節(jié)點已經不能消耗滑動量,所以只有父節(jié)點3進入scroll chain,從而將滑動了整個網頁。
【修改后】
在邊緣處再次滑動時,發(fā)生持久閂鎖