記一次淘寶首頁奇葩的渲染問題-渲染色塊

或許你曾經在 Chrome 瀏覽器上碰到過這樣讓人瞠目結舌的問題:

  • hover 觸發一個層展示,hover 離開後,這個層還遺留殘影
  • 瀏覽器沒有清理一個元素渲染的上一個狀態,導致頁面多出一個錯位的跟該元素一模一樣的影子
  • 交互時突然出現一個方形色塊,覆蓋在元素上
  • 或者還有更奇葩的……

以上列舉到的三個問題,我在維護淘寶首頁的時候都遇到過。這些都是瀏覽器渲染頁面時,因爲渲染引擎的 bug 導致的問題,不常見,更加難以寫 demo 演示,它們只在特定的複雜場景下,程序計算存在誤差或者漏洞的時候出現,尤其是涉及到邊界判斷的時候。

問題復現

很難得有機會讓我碰到一個可以復現的,我把它記錄下來了。如下圖所示,hover 到學習模塊的邊界位置時:

problem

手動 hover 和模擬 hover 都有一樣的問題,沒有多想,立馬加上了一句話修復了這個問題:

 
.channel2 .channel-item {
    transform: translateZ(0);
}

這個不是直覺,多次遇到這種奇葩問題,我第一想到的便是使用 3D 加速將這個渲染層隔離渲染,80% 以上的概率能夠解決問題,而解決問題的關鍵在於找準加這句代碼的 DOM 元素。

探索 bug

這個層在我的代碼中肯定是不存在的,我們只能用 bug 來形容這個問題。因爲元素剛好貼在 .channel2 的邊界,猜測應該跟層渲染有關,於是打開了控制檯 ESC -> Rendering -> Show layer borders,看到了這個:

detail

仔細觀察,可以看到,這個粉色塊在瓦片邊界和父元素邊界之中,可以斷定,這幾個瓦片在渲染的時候存在問題。


這裏需要補充下關於瓦片的知識。瓦片,英文裏頭稱之爲 tile,它是 webkit/blink 渲染頁面時的中間過程,將整個頁面分成多個大小一樣的瓦片,併發渲染每個瓦片的內容。一個元素開啓 3D 硬件加速之後,會變成一個獨立的層,這個層的渲染也會被分割成瓦片,可以想象成一個子頁面。

瓦片和瓦片之間的邊界計算是處理的難點,因爲渲染的內容不能錯位。


其實讓我找到問題根本原因的是,rendering 塊的顏色,平時在網頁上開啓 show layer borders 看到的是半透明的綠色塊,而這裏顯示的是粉色塊,搜索了下不同色塊代表的含義,沒找到具體的文檔說明,但是找到了 代碼:

 
// Missing resize invalidations are in salmon pink.
SkColor DebugColors::MissingResizeInvalidations() {
  return SkColorSetARGB(255, 255, 155, 170);
}

對應的就是這個顏色,“缺失調整驗證”,在 chromium 的源碼倉庫中搜了上面的代碼,找到了 具體說明:

 
if (!deflated_content_rect.Contains(canvas_playback_rect)) {
  if (clear_canvas_with_debug_color) {
    // Any non-painted areas outside of the content bounds are left in
    // this color.  If this is seen then it means that cc neglected to
    // rerasterize a tile that used to intersect with the content rect
    // after the content bounds grew.
    canvas->save();
    canvas->translate(-canvas_bitmap_rect.x(), -canvas_bitmap_rect.y());
    canvas->clipRect(gfx::RectToSkRect(content_rect),
                     SkRegion::kDifference_Op);
    canvas->drawColor(DebugColors::MissingResizeInvalidations(),
                      SkXfermode::kSrc_Mode);
    canvas->restore();
  }
}

這裏能看的肯定就是註釋啦,沒有太多上下文,看的挺頭痛!大致翻譯了下上下幾段註釋:

  1. 即使完全覆蓋,對於觸碰到渲染層邊界的柵格化處理,我們依然需要,在上次記錄沒有覆蓋到的紋理下方和紋理化線性過濾的上方,柵格化處理背景顏色。
  2. 內容的最後的紋理可能只有部分被柵格覆蓋
  3. 在內容邊界外沒有被渲染到的部分將使用 MissingResizeInvalidations 顏色,如果這個塊能夠被看見,那就意味着程序忽視處理了內邊邊界增長之後柵格化與內容相交的瓦片。

從第三句大致可以瞭解到,因爲元素的邊界增長導致了這個渲染 bug,回頭看了下元素的邊界狀態,果然…

直接原因

我們看看 hover 上去之後,層邊界的變化:

border

很明顯,這裏的高度溢出了,但是沒有處理,看了下這個元素的 css,確實高度上沒有做處理,在元素上添加

 
.channel-item {
  overflow: hidden;
}

同樣可以解決問題。

最後的解決手段:

resolve

層渲染的問題我還是比較喜歡使用 3d 硬件加速來處理,而 overflow:hidden 這樣的 css 佈局處理上,我是不太推薦的,搞不好就把哪個重要的內容隱藏掉了。

類似問題處理方案

如果以後大家遇到類似的問題,可以打開 chrome 的層和瓦片分析工具,看看渲染出來的塊有沒有異常色塊,尤其是粉色塊。也可以觀察交互過程中,元素的邊界有沒有變化。

CSS 在瀏覽器中的渲染是我們觸及比較少的知識,如果想迅速找到問題,必須對瀏覽器的渲染原理有所瞭解,並且能夠熟練的使用 chrome 提供的調試工具,這是基礎。


===============

原文鏈接: http://taobaofed.org/blog/2015/11/23/a-strange-bug-research-at-taobao-home-page/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章