瀏覽器實現移動端高效能css3動畫

來源:互聯網
上載者:User
在現有的前端動畫體系中,通常有兩種模式:JS動畫與CSS3動畫。 JS動畫是通過JS動態改寫樣式實現動畫能力的一種方案,在PC端相容低端瀏覽器中不失為一種推薦方案。 而在移動端,我們選擇效能更優瀏覽器原生實現方案:CSS3動畫

高效能移動Web相較PC的情境需要考慮的因素也相對更多更複雜,我們總結為以下幾點: 流量、功耗與流暢度。 在PC時代我們更多的是考慮體驗上的流暢度,而在Mobile端本身豐富的情境下,需要額外關注對使用者基站網路流量使用的情況,裝置耗電量的情況。

關於流暢度,主要體現在前端動畫中,在現有的前端動畫體系中,通常有兩種模式:JS動畫與CSS3動畫。 JS動畫是通過JS動態改寫樣式實現動畫能力的一種方案,在PC端相容低端瀏覽器中不失為一種推薦方案。 而在移動端,我們選擇效能更優瀏覽器原生實現方案:CSS3動畫。
然而,CSS3動畫在移動多終端裝置情境下,相比PC會面對更多的效能問題,主要體現在動畫的卡頓與閃爍。
目前對提升移動端CSS3動畫體驗的主要方法有幾點:

儘可能多的利用硬體能力,如使用3D變形來開啟GPU加速

代碼如下:

-webkit-transform: translate3d(0, 0, 0);-moz-transform: translate3d(0, 0, 0);-ms-transform: translate3d(0, 0, 0);transform: translate3d(0, 0, 0);

如動畫過程有閃爍(通常發生在動畫開始的時候),可以嘗試下面的Hack:

代碼如下:

-webkit-backface-visibility: hidden;-moz-backface-visibility: hidden;-ms-backface-visibility: hidden;backface-visibility: hidden;-webkit-perspective: 1000;-moz-perspective: 1000;-ms-perspective: 1000;perspective: 1000;

如下面一個元素通過translate3d右移500px的動畫流暢度會明顯優於使用left屬性:

代碼如下:

#ball-1 {  transition: -webkit-transform .5s ease;  -webkit-transform: translate3d(0, 0, 0);}#ball-1.slidein {  -webkit-transform: translate3d(500px, 0, 0);}#ball-2 {  transition: left .5s ease;  left:0;}#ball-2.slidein {  left:500px;}

註:3D變形會消耗更多的記憶體與功耗,應確實有效能問題時才去使用它,兼在權衡

儘可能少的使用box-shadows與gradients

box-shadows與gradients往往都是頁面的效能殺手,尤其是在一個元素同時都使用了它們,所以擁抱扁平化設計吧。
儘可能的讓動畫元素不在文檔流中,以減少重排

代碼如下:

position: fixed;position: absolute;

最佳化 DOM layout 效能

我們從執行個體開始描述這個主題:

代碼如下:

var newWidth = ap.offset Width + 10;ap.style.width = newWidth + 'px';var newHeight = ap.offsetHeight + 10;ap.style.height = newHeight + 'px';var newWidth = ap.offsetWidth + 10;var newHeight = ap.offsetHeight + 10;ap.style.width = newWidth + 'px';ap.style.height = newHeight + 'px';

這是兩段能力上完全等同的代碼,顯式的差異正如我們所見,只有執行順序的區別。但真是如此嗎?下面是加了說明注釋的代碼版本,很好的闡述了其中的進一步差異:

代碼如下:

// 觸發兩次 layoutvar newWidth = ap.offsetWidth + 10;   // Readap.style.width = newWidth + 'px';     // Writevar newHeight = ap.offsetHeight + 10; // Readap.style.height = newHeight + 'px';   // Write// 只觸發一次 layoutvar newWidth = ap.offsetWidth + 10;   // Readvar newHeight = ap.offsetHeight + 10; // Readap.style.width = newWidth + 'px';     // Writeap.style.height = newHeight + 'px';   // Write

從注釋中可找到規律,連續的讀取offsetWidth/Height屬性與連續的設定width/height屬性,相比分別讀取設定單個屬性可少觸發一次layout。
從結論看似乎與執行隊列有關,沒錯,這是瀏覽器的最佳化策略。所有可觸發layout的操作都會被暫時放入 layout-queue 中,等到必須更新的時候,再計算整個隊列中所有操作影響的結果,如此就可只進行一次的layout,從而提升效能。
關鍵一,可觸發layout的操作,哪些操作下會layout的更新(也稱為reflow或者relayout)?
我們從瀏覽器的源碼實現入手,以開源Webkit/Blink為例, 對layout的更新,Webkit 主要通過 Document::updateLayout 與Document::updateLayoutIgnorePendingStylesheets 兩個方法:

代碼如下:

void Document::updateLayout(){    ASSERT(isMainThread());    FrameView* frameView = view();    if (frameView && frameView->isInLayout()) {        ASSERT_NOT_REACHED();        return;    }    if (Element* oe = ownerElement())        oe->document()->updateLayout();    updateStyleIfNeeded();    StackStats::LayoutCheckPoint layoutCheckPoint;    if (frameView && renderer() && (frameView->layoutPending() || renderer()->needsLayout()))        frameView->layout();    if (m_focusedNode && !m_didPostCheckFocusedNodeTask) {        postTask(CheckFocusedNodeTask::create());        m_didPostCheckFocusedNodeTask = true;    }}void Document::updateLayoutIgnorePendingStylesheets(){    bool oldIgnore = m_ignorePendingStylesheets;    if (!haveStylesheetsLoaded()) {        m_ignorePendingStylesheets = true;        HTMLElement* bodyElement = body();        if (bodyElement && !bodyElement->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) {            m_pendingSheetLayout = DidLayoutWithPendingSheets;            styleResolverChanged(RecalcStyleImmediately);        } else if (m_hasNodesWithPlaceholderStyle)            recalcStyle(Force);    }    updateLayout();    m_ignorePendingStylesheets = oldIgnore;}

從 updateLayoutIgnorePendingStylesheets 方法的內部實現可知,其也是對 updateLayout 方法的擴充,並且在現有的 layout 更新模式中,大部分情境都是調用 updateLayoutIgnorePendingStylesheets 來進行layout的更新。
搜尋 Webkit 實現中調用 updateLayoutIgnorePendingStylesheets 方法的代碼, 得到以下可導致觸發 layout 的操作:

Element: clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth

Frame, HTMLImageElement: height, width

Range: getBoundingClientRect(), getClientRects()

SVGLocatable: computeCTM(), getBBox()

SVGTextContent: getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()

SVGUse: instanceRoot

window: getComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()

【相關推薦】

1. CSS3免費視頻教程

2. h5+css3實現圖片飛入和淡入淡出效果的代碼執行個體

3. 教你使用CSS3製作8種Loading動畫

4. 教你用CSS繪製標準的圓形圖案

5. CSS3完成一個方框圓角效果的代碼教程

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.