瀏覽器渲染原理與 CSS 動畫
1. 瀏覽器渲染
1.1 瀏覽器渲染的步驟
瀏覽器渲染流程大致分為以下幾個步驟:
- 根據 HTML 構建 HTML 樹(DOM);
- 根據 CSS 構建 CSS 樹(CSSOM);
- 將兩棵樹合併成一棵渲染樹(render tree);
- Layout 佈局(文件流、盒模型、計算大小和位置);
- Paint 繪製(把邊框顏色,文字顏色陰影等畫出來);
- Composite 合成(根據層疊關係展示畫面)。
1.2 更新 CSS 樣式的方法和步驟
一般用 JavaScript 更新樣式,可以修改樣式或者新增類,新增類更為方便常用。使用 JavaScript 更新樣式要經過佈局、繪製、合成這幾個步驟中的一步、二步或者全部。
- 經過佈局、繪製、合成全部過程,如刪除某 div 元素;
- 經過繪製、合成過程,如改變某元素背景顏色,因為位置、大小未改變,不影響佈局;
- 經過合成過程,如改變 transform 屬性,在 Chrome 瀏覽器只需要合成。
1.3 各屬性觸發相應流程的查詢方式
通過 csstriggers.com 查詢各屬性改變時在各瀏覽器觸發了哪些的流程。
各主要瀏覽器核心標識如下:
- Blink:Chrome 瀏覽器
- Geckko:Firefox 瀏覽器
- Webkit:Safari 瀏覽器
- EdgeHTML:IE 最新版
1.4 瀏覽器渲染與 CSS 動畫優化的關係
開啟 Chrome 開發者工具,按 ESC,點選 Rendering,勾選 Paint flashing,此時頁面會出現綠色部分閃爍,綠色閃爍代表重新渲染,就相當於在螢幕上這塊區域重新畫一下。
通過 JavaScript 定時器設定每秒移動固定位移實現的動畫,對應元素一直在重新渲染。而通過設定 transform 實現的動畫,在 Chrome 瀏覽器測試,元素只在起始位置渲染,優化了 CSS 效能。
CSS 動畫優化經常採用以下方式
- JS 優化:使用 requestAnimationFrame 代替 setTimeout 或 setInterval;
- CSS 優化:使用 will-change 或 transform。
2. CSS 動畫
每個靜止的畫面都是幀,連續的幀形成了動畫。影視通常每秒 24 幀,遊戲每秒 30 幀以上,有追求畫面的遊戲達到 120 ~ 140 幀甚至更高。
2.1 transform 變形
四個常用功能:
- 位移 translate
- 縮放 scale
- 旋轉 rotate
- 傾斜 skew
transform 一般需要配合 transition 使用。inline-block 元素不支援 transform,需要先變成 block。
針對 translateZ 的移動,需要設定 perspective(透視原點位置)才能觀察到效果。如 perspective: 1000px,即透視原點位於距元素正中心 1000px 遠處。
/* 通過 transform 實現絕對定位元素居中,不支援 IE */
.demo {
width: 200px;
height: 200px;
border: 1px solid red;
position: absolute;
left: 50%;
top: 50%;
/* 回半個身位 */
/* transform: translateX(-50%) translateY(-50%);*/
transform: translate(-50%,-50%);
}
複製程式碼
2.2 transition 過渡
- 作用:補充中間幀
- 語法: transition: 屬性名 時長 過渡方式 延遲時間
可拆分設定 transition-property: width,background,transition-duration: 2s,10s。
可自定義貝塞爾曲線 cubic-bezier(0,0.95,1,0.07)
延遲時間 transition-delay: 3s;
所有屬性同樣過渡 transition: all 2s;
- 過渡方式:linear(線性) | ease(緩動)| ease in(緩入,從慢到快)| ease out(緩出,從快到慢)| ease-in-out(緩入緩出)| ...
- display:none --> block 沒辦法過渡,一般改為 visibility:hidden --> visible
- visibility: hidden 仍然佔用域的空間
- 過渡必須要有起始
2.3 animation 動畫
宣告關鍵幀,新增動畫。@keyframes 兩種寫法。
.demo.strat {
animation: moveExample 1s;
}
/* 寫法一 */
@keyframes moveExample {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
/* 寫法二 */
@keyframes moveExample {
0% {
transform: translateX(0%);
}
30% {
transform: rotate(45deg);
}
60%,70% {
transform: scale(1.2);
}
100% {
transform: translateX(100%);
}
}
複製程式碼
animation 簡寫
- animation: 時長 | 過渡方式 | 延遲 | 次數 | 方向 | 填充方式 | 是否暫停 | 動畫名
- animation-name: moveExample —— 動畫名稱
- animation-duration: 3s —— 動畫時間週期
- animation-timing-function: ease —— 動畫速度曲線
- animation-delay: 1s —— 動畫延遲時間
- animation-iteration-count: 3(3次) | infinite(無限次)—— 動畫迴圈次數
- animation-direction: normal —— 運動的方向。
alternate交替運動(奇數次正常運動,偶數次反向),reverse 反向運動,alternate-reverse 奇數次反向偶數次正向運動。
- animation-fill-mode: fowards; —— 使得動畫保留最後一幀的效果
3. 小案例
- 使用 transform、transition 和 :hover 實現會動的紅心
* {
padding: 0;
margin: 0;
}
.wrap {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
#heart {
position: relative;
transition: all 1s;
}
#heart:hover {
transform: scale(1.2);
transition: all 1s;
}
.left {
position: absolute;
bottom: 100%;
right: 100%;
width: 200px;
height: 200px;
background: red;
border-radius: 50% 0 0 50%;
transform: rotate(45deg) translateX(170px);
}
.right {
position: absolute;
bottom: 100%;
left: 100%;
width: 200px;
height: 200px;
background: red;
border-radius: 50% 50% 0 0;
transform: rotate(45deg) translateY(170px);
}
.bottom {
width: 200px;
height: 200px;
background: red;
transform: rotate(45deg);
}
複製程式碼
<body>
<div class="wrap">
<div id="heart">
<div class="left"></div>
<div class="right"></div>
<div class="bottom"></div>
</div>
</div>
</body>
複製程式碼
- 使用 animation 實現會動的紅心。
#heart {
position: relative;
animation: heart 0.7s ease infinite alternate;
}
@keyframes heart {
0% {
transform: scale(1);
}
100% {
transform: scale(1.2);
}
}
複製程式碼