本文希望通過介紹一些 CSS 不太常用的技巧,輔以一些實踐,讓讀者可以更加深入的理解掌握 CSS 動畫效果,非常不錯,具有參考借鑒價值,需要的朋友參考下吧
怕標題起的有點大,下述技巧如果你已經掌握了看看就好,歡迎斧正,本文希望通過介紹一些 CSS 不太常用的技巧,輔以一些實踐,讓讀者可以更加深入的理解掌握 CSS 動畫。
廢話少說,直接進入正題,本文提到的動畫不加特殊說明,皆指 CSS 動畫。
正負旋轉相消
嗯。名字起的很奇怪,好像數學概念一樣。
在動畫中,旋轉是非常常用的屬性,
{ transform: rotate(90deg);}
那旋轉有一些什麼進階點的技巧呢?當然是可以改變 transfrom-origin
,改變旋轉中心點啦。
開個玩笑,改變旋轉中心點這個估計大家都知道了,這裡要介紹的技巧是利用父級元素正反兩個方向的旋轉,來製作一些酷炫的 3d 效果。
首先假設一下情境,我們有這樣的一層 HTML 結構:
<p class="reverseRotate"> <p class="rotate"> <p class="content">正負旋轉相消3D動畫</p> </p></p>
樣式如下:
.content
內是我們的主要內容,好了,現在想象一下,如果祖先元素 .rotate
進行正向 linear 360° 旋轉,父級元素 .reverseRotate
進行反向 linear 360° 旋轉,效果回是啥樣?
CSS 代碼如下:
.rotate { animation: rotate 5s linear infinite; }.reverseRotate { animation: reverseRotate 5s linear infinite; }@keyframes rotate { 100% { transform: rotate(360deg); }}@keyframes reverseRotate { 100% { transform: rotate(-360deg); }}
神奇!因為一正一反的旋轉,且easing 函式一樣,所以整個 content
看上去依然是靜止的!注意,這裡整個 content
靜止的非常重要。
有讀者看到這裡就要罵街了,作者你個智障,靜止了不就沒動畫了嗎?哪來的動畫技巧?
別急!雖然看上去是靜止的,但是其實祖先兩個元素都是在旋轉的!這會看上去風平浪靜的效果底下其實是暗流湧動。用開發人員工具選取最外層祖先元素是這樣的:
既然如此,我們繼續思考,如果我在其中旋轉的一個祖先元素上,添加一些別的動畫會是什麼效果?想想就很刺激啊。
為了和文案裡面的 3D 動畫扯上關係,我們先給這幾個元素添加 3D 轉換:
p { transform-style: preserve-3d; perspective: 500px;}
接著,嘗試修改上面的旋轉動畫,在內層旋轉上額外添加一個 rotateX:
@keyframes rotate { 0% { transform: rotateX(0deg) rotateZ(0deg); } 50% { transform: rotateX(40deg) rotateZ(180deg); } 100% { transform: rotateX(0deg) rotateZ(360deg); }}
效果如下:
Wow,這裡需要好好理解一下。由於內容 content
層是靜止的但其實外層兩個圖層都在旋轉,通過設定額外的 rotateX(40deg)
,相當於疊加多了一個動畫,由於正反旋轉抵消了,所有整個動畫只能看到旋轉的 rotateX(40deg)
這個動畫,產生了上述的效果。
CodePen Demo -- Css正負旋轉相消動畫
動畫相同,緩動不同
好的,繼續下一個小技巧。
有的時候我們頁面存在一些具有相同動畫的元素,為了讓動畫不那麼死板,我們可以給相同的動畫,賦予不同的easing 函式,來達到動畫效果。
假設我們有如下的結構:
<p class="container"> <p class="ball ball1"></p> <p class="ball ball2"></p> <p class="ball ball3"></p></p>
樣式如下:
我們給它們相同的動畫,但是賦予不一樣的easing 函式(animation-timing-function),就像這樣:
.ball1 { animation: move 1s ease-in infinite alternate;}.ball2 { animation: move 1s linear infinite alternate;}.ball3 { animation: move 1s ease-out infinite alternate;}@keyframes move { 100% { transform: translateY(5vw); }}
這樣,一個簡單的 loading 效果就製作好了。(當然這個技巧比較簡單,學會合理運用是關鍵)
CodePen Demo -- 動畫相同,緩動不同
奇妙的緩動
easing 函式 timing-function 在動畫中佔據了非常重要的地位。
當你不想使用 CSS 預設提供的 linear
、ease-in
、ease-out
之類easing 函式的,可以自訂 cubic-bezier(1, 1, 0, 0)
,這裡有個非常好用的工具推薦,下面這個網站,可以方便的調出你需要的easing 函式並且拿到對應的 cubic-bezier 。
cubic-bezier.com
過渡取消
我們在製作頁面的時候,為了讓頁面更加有互動感,會給按鈕,陰影,顏色等樣式添加過渡效果,配合 hover 一起使用。
這個是常規思維,如果我們的元素一開始是沒有過渡效果,只有 hover 上去才給它添加一個過渡,又或者一開始元素是有過渡效果的,當我們 hover 上去時,取消它的過渡,會碰撞出什麼樣的火花呢?
使用這個技巧(也許算不上技巧,純粹好玩),我們可以製作出一些有趣的效果,例如下面這個感覺是利用就 JS 才完成的動畫,其實是純 CSS 動畫:
其實就小圓圈是元素預設是帶有 transition
的,只有在 hover 上去的時候,取消它的過渡,簡單的過程:
可以戳這裡感受一下:
CodePen Demo -- Cancle transition
動畫層級的控制,保持動畫層級在最上方
這個問題可能有一點難理解。需要瞭解 CSS 動畫渲染最佳化的相關知識。
先說結論,動畫層級的控制的意思是盡量讓需要進行 CSS 動畫的元素的 z-index
保持在頁面最上方,避免瀏覽器建立不必要的圖形層(GraphicsLayer),能夠很好的提升渲染效能。
OK,再一次提到了圖形層(GraphicsLayer),這是一個瀏覽器渲染原理相關的知識(WebKit/blink核心下)。
簡單來說,瀏覽器為了提升動畫的效能,為了在動畫的每一幀的過程中不必每次都重新繪製整個頁面。在特定方式下可以觸發產生一個合成層,合成層擁有單獨的 GraphicsLayer。
需要進行動畫的元素包含在這個合成層之下,這樣動畫的每一幀只需要去重新繪製這個 Graphics Layer 即可,從而達到提升動畫效能的目的。
那麼一個元素什麼時候會觸發建立一個 Graphics Layer 層?從目前來說,滿足以下任意情況便會建立層:
硬體加速的 iframe 元素(比如 iframe 嵌入的頁面中有合成層)
硬體加速的外掛程式,比如 flash 等等
使用加速視頻解碼的 元素
3D 或者 硬體加速的 2D Canvas 元素
3D 或透視變換(perspective、transform) 的 CSS 屬性
對自己的 opacity 做 CSS 動畫或使用一個動畫變換的元素
擁有加速 CSS 過濾器的元素
元素有一個包含複合層的後代節點(換句話說,就是一個元素擁有一個子項目,該子項目在自己的層裡)
元素有一個 z-index 較低且包含一個複合層的兄弟元素
本題中說到的動畫層級的控制,原因就在於上面產生層的最後一條:
元素有一個 z-index 較低且包含一個複合層的兄弟元素。
這裡是存在坑的地方,首先我們要明確兩點:
我們希望我們的動畫得到 GPU 硬體加速,所以我們會利用類似 transform: translate3d()
這樣的方式產生一個 Graphics Layer 層。
Graphics Layer 雖好,但不是越多越好,每一幀的渲染核心都會去遍曆計算當前所有的 Graphics Layer ,並計算他們下一幀的重繪地區,所以過量的 Graphics Layer 計算也會給渲染造成效能影響。
記住這兩點之後,回到上面我們說的坑。
假設我們有一個輪播圖,有一個 ul 列表,結構如下:
<p class="container"> <p class="swiper">輪播圖</p> <ul class="list"> <li>列表li</li> <li>列表li</li> <li>列表li</li> <li>列表li</li> </ul></p>
假設給他們定義如下 CSS:
.swiper { position: static; animation: 10s move infinite;} .list { position: relative;}@keyframes move { 100% { transform: translate3d(10px, 0, 0); }}
由於給 .swiper
添加了 translate3d(10px, 0, 0)
動畫,所以它會產生一個 Graphics Layer,如所示,用開發人員工具可以開啟層的展示,圖形外的黃色邊框即代表產生了一個獨立的複合層,擁有獨立的 Graphics Layer 。
<
但是!在上面的圖中,我們並沒有給下面的 list
也添加任何能觸發產生 Graphics Layer 的屬性,但是它也同樣也有黃色的邊框,產生了一個獨立的複合層。
原因在於上面那條元素有一個 z-index 較低且包含一個複合層的兄弟元素。我們並不希望 list
元素也產生 Graphics Layer ,但是由於 CSS 層級定義原因,下面的 list 的層級高於上面的 swiper,所以它被動的也產生了一個 Graphics Layer 。
使用 Chrome,我們也可以觀察到這種層級關係,可以看到 .list
的層級高於 .swiper
:
所以,下面我們修改一下 CSS ,改成:
.swiper { position: relative; z-index: 100;} .list { position: relative;}
這裡,我們明確使得 .swiper
的層級高於 .list
,再開啟開發人員工具觀察一下:
可以看到,這一次,.list
元素已經沒有了黃色外邊框,說明此時沒有產生 Graphics Layer 。再看看層級圖:
此時,層級關係才是我們希望看到的,.list
元素沒有觸發產生 Graphics Layer 。而我們希望需要硬體加速的 .swiper
保持在最上方,每次動畫過程中只會獨立重繪這部分的地區。
總結
GPU 硬體加速也會有坑,當我們希望使用利用類似 transform: translate3d() 這樣的方式開啟 GPU 硬體加速,一定要注意元素層級的關係,盡量保持讓需要進行 CSS 動畫的元素的 z-index 保持在頁面最上方。
Graphics Layer 不是越多越好,每一幀的渲染核心都會去遍曆計算當前所有的 Graphics Layer ,並計算他們下一幀的重繪地區,所以過量的 Graphics Layer 計算也會給渲染造成效能影響。
可以使用 Chrome ,用上面介紹的兩個工具對自己的頁面產生的 Graphics Layer 和元素層級進行觀察然後進行相應修改。
上面觀察頁面層級的 chrome 工具非常吃記憶體?好像還是一個處於實驗室的功能,分析稍微大一點的頁面容易直接卡死,所以要多學會使用第一種觀察黃色邊框的方式查看頁面產生的 Graphics Layer 這種方式。
數字動畫
很多技巧單獨拿出來可能都顯得比較單薄,我覺得最重要的是平時多積累,學會融會貫通,在實際項目中靈活組合運用,最近項目需要一個比較富有科技感的數字計數器,展示線上人數的不斷增加。因為是內部需求,沒有設計稿,靠前端自由發揮。
運用了上面提到的一些小技巧,參考了一些 CodePen 上的效果,整了個下述的 3D 數字計數效果,純 CSS 實現,如下: