加入模糊效果動畫

模糊處理是將使用者焦點重新導向的好方法。讓部分視覺元素能夠模糊處理,同時讓其他元素聚焦在使用者的焦點上。使用者會忽略經過模糊處理的內容,改為把重點放在他們能閱讀的內容上。例如將遊標懸停在圖示上時,畫面上會出現顯示個別項目詳細資料的圖示清單。在這段期間,剩餘的選項可能會經過模糊處理,以便將使用者重新導向至新顯示的資訊。

重點摘要

為模糊效果加上動畫效果並不容易,因為處理速度非常緩慢。請改為預先運算一系列逐漸模糊的版本,並在兩個版本之間交叉漸變。我的同事 Yi Gu 編寫了一個程式庫,可協助您處理一切!歡迎觀看我們的示範

但是,如果在沒有過渡期的情況下套用這項技巧,可能會相當難以察覺。為模糊元素加上動畫效果 (從未模糊處理轉換成模糊效果) 似乎是合理的選擇,但如果您曾在網站上嘗試進行這類操作,可能會發現動畫沒有任何效果,但如果沒有功能強大的機器,這個示範或許會呈現流暢動畫效果。我們可以做得更好嗎?

問題

CPU 會將標記轉換為紋理。紋理會上傳至 GPU。GPU 會使用著色器將這些紋理繪製到影格緩衝區。模糊效果是透過著色器進行。

目前我們無法有效率地進行模糊化作業的動畫。但是,我們可以找到一個看起來足夠,但實際上是在技術上來說,而不是動畫的模糊效果。首先,讓我們先瞭解動畫模糊效果緩慢的原因。如要模糊處理網路上的元素,有兩種技巧:CSS filter 屬性和 SVG 篩選器。多虧支援提高支援和使用便利性,因此通常會用到 CSS 篩選器。不過,如果您需要支援 Internet Explorer,您就不會有選擇,但可以使用 SVG 篩選器,因為 IE 10 和 11 支援這兩種篩選器,但不支援 CSS 篩選器。好消息是,動畫模糊效果的解決方法也適用於這兩種技術。我們現在就利用開發人員工具找出瓶頸

因此,如果您在開發人員工具中啟用「Paint Flashing」,則完全不會閃爍。似乎沒有進行重新繪製。技術上來說,「重新繪製」是指 CPU 必須重新繪製宣傳元素的紋理。每當元素都「提升並」經過模糊處理時,GPU 就會使用著色器套用模糊效果。

SVG 篩選器和 CSS 篩選器都會使用對話篩選器套用模糊效果。卷積篩選器非常昂貴,因為每個輸出像素都必須考慮多個輸入像素。圖片越大或模糊半徑範圍越大,效果越成本也越大。

這就是造成問題的原因,是因為每個影格執行都會執行成本更低的 GPU 作業,平均耗用 16 毫秒的影格預算,因此最終得到的結果低於 60fps。

兔子洞

該如何讓這項作業順利運作?我們當然可以!我們不會為實際的模糊處理值 (模糊的半徑) 加上動畫效果,而是預先計算模糊的重複副本,其中模糊值會指數遞增,然後使用 opacity 交叉淡出。

交叉淡出是一系列重疊的不透明度淡入和淡出效果。舉例來說,如果我們有四個模糊處理階段,就會同時淡出第一個階段,同時淡入第二階段的淡出效果。當第二階段的不透明度達到 100% 且第一個階段已達到 0% 時,我們就會淡出第二個階段,並在第三階段淡出。建立完成後,我們最後將第三階段淡出,然後淡出第四個和最後版本。在此案例中,每個階段佔所需總時間長度的 1⁄4。這看起來與真正的動畫模糊十分相似

在我們的實驗中,為每個階段以指數方式調升模糊半徑,可產生最佳視覺結果。例如:假設有四個模糊處理階段,我們會將 filter: blur(2^n) 套用至各個階段,即階段 0:1px、階段 1:2px、階段 2:4px 和階段 3: 8px。如果我們使用 will-change: transform 強制將這些模糊處理的副本放在自己的圖層 (稱為「升級」),這些元素的不透明度變更應該非常快速。理論上,這有利於我們預先載入昂貴的模糊處理工作。結果了,邏輯失誤。如果執行這個示範,會發現影格速率仍然低於 60fps,且模糊效果實際上更低

開發人員工具中的追蹤記錄,顯示 GPU 長時間處於忙碌狀態的追蹤記錄。

快速查看開發人員工具,瞭解 GPU 仍非常忙碌,且每個影格的延展至約 90 毫秒。但為什麼?我們不會再變更模糊處理的值 只有不透明度的變化,會發生什麼事?問題同樣只會發生一次,因為模糊效果的性質:如之前所述,如果元素都經過宣傳和模糊處理,GPU 就會套用效果。因此,即使我們不再為模糊值加上動畫效果,紋理本身仍會保持修復模糊,且需要先將 GPU 的每個影格重新模糊處理。相較於單純實作,GPU 影格速率會較差,實際上 GPU 的實際工作量也會比以往更多,因為大多數兩個紋理都必須獨立進行模糊處理。

我們提出的想法並不完美,但會讓動畫速度飛快無比。 我們返回宣傳模糊處理的元素,而是升級父項包裝函式。如果元素經過模糊處理及升級,GPU 套用了效果。這就是導致示範速度變慢的原因。如果元素經過模糊處理但未進行升級,模糊效果會改為光柵化為最接近的父項紋理。在此範例中,這是推送的父項包裝函式元素。經過模糊處理的圖片現在是父項元素的紋理,可重複用於所有日後的影格。這種做法之所以有效,是因為我們知道模糊的元素不是動畫,快取這些元素其實很有用。以下是實作這項技術的示範。不曉得 Moto G4 對這個方法有何看法?劇透警報:感覺很棒:

開發人員工具顯示 GPU 有大量閒置時間的追蹤記錄。

現在 GPU 有大量進步空間,以及流暢的 60fps 效能。我們成功了!

生產中

在我們的示範中,我們多次複製 DOM 結構,以複製內容的複本,以不同的強度來模糊處理。您可能會好奇,這對作者的 CSS 樣式或甚至是 JavaScript 可能會產生一些非預期的副作用,因此在正式環境中運作了您呢?你說的對。輸入 Shadow DOM!

雖然大多數人都認為 Shadow DOM 是將「內部」元素附加至其自訂元素的方式,但也是獨立性和效能的獨立性!JavaScript 和 CSS 不得設定陰影 DOM 邊界,這樣我們才能複製內容,而不會幹擾開發人員的樣式或應用程式邏輯。每個副本都已有 <div> 元素,可用來光柵化,現在使用這些 <div> 做為陰影主機。我們使用 attachShadow({mode: 'closed'}) 建立 ShadowRoot,並將內容副本附加到 ShadowRoot,而非 <div> 本身。我們必須將所有樣式表複製到 ShadowRoot 中,確保副本的樣式與原始範本相同。

有些瀏覽器不支援 Shadow DOM v1,而對於那些瀏覽器,我們會改回使用複製內容的方式,希望能讓一切都無法正常運作。我們可以搭配 ShadyCSS 使用 Shadow DOM polyfill,但我們並未在程式庫中實作此項目。

很好

結語

這種效果比較不會太淡,由於我們要複製 DOM 元素並強制移到其專屬圖層,所以可以推送低階裝置的限制。將「所有」樣式表複製到每個 ShadowRoot 也會產生效能風險,因此您應決定是否要調整邏輯和樣式,而不會受到 LightDOM 中的副本影響,或者使用 ShadowDOM 技術。但我們的技巧有時可能值得投資請查看我們 GitHub 存放區中的程式碼以及示範,如有任何問題,歡迎透過 Twitter 與我聯絡!