你可能不知道的Animation動畫技巧與細節

做者:凹凸曼 - 阿文css

引言

在 web 應用中,前端同窗在實現動畫效果時每每經常使用的幾種方案:html

  1. css3 transition / animation - 實現過渡動畫
  2. setInterval / setTimeout - 經過設置一個間隔時間來不斷的改變圖像的位置
  3. requestAnimationFrame - 經過一個回調函數來改變圖像位置,由系統來決定這個回調函數的執行時機,比定時修改的性能更好,不存在失幀現象

在大多數需求中,css3 的 transition / animation 都能知足咱們的需求,而且相對於 js 實現,能夠大大提高咱們的開發效率,下降開發成本。前端

本篇文章將着重對 animation 的使用作個總結,若是你的工做中動畫需求較多,相信本篇文章可以讓你有所收穫:css3

  • Animation 經常使用動畫屬性
  • Animation 實現不間斷播報
  • Animation 實現回彈效果
  • Animation 實現直播點贊效果 ❤️
  • Animation 與 Svg 又會擦出怎樣的火花呢?🔥web

    1. Loading 組件
    2. 進度條組件
  • Animation steps() 運用 ⏰segmentfault

    1. 實現打字效果
    2. 繪製幀動畫

Animation 經常使用動畫屬性

介紹完 animation 經常使用屬性,爲了將這些屬性更好地理解與運用,下面將手把手實現一些 DEMO 具體講述瀏覽器

Animation 實現不間斷播報

實現不間斷播報 DEMO app

經過修改內容在父元素中的 y 軸的位置來實現廣播效果svg

@keyframes scroll {
  0%{
    transform: translate(0, 0);
  }
  100%{
    transform: translate(0, -$height);
  }
}

.ul {
  animation-name: scroll;
  animation-duration: 5s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  /* animation: scroll 5s linear infinite; 動畫屬性簡寫 */
}

此處爲了保存廣播滾動效果的連貫性,防止滾動到最後一幀時沒有內容,須要多添加一條重複數據進行填充函數

<div class="ul">
  <div class="li">小劉同窗加入了凹凸實驗室</div>
  <div class="li">小鄧同窗加入了凹凸實驗室</div>
  <div class="li">小李同窗加入了凹凸實驗室</div>
  <div class="li">小王同窗加入了凹凸實驗室</div>
    <!--   插入用於填充的數據數據 -->
  <div class="li">小劉同窗加入了凹凸實驗室</div>
</div>

Animation 實現回彈效果

經過將過渡動畫拆分爲多個階段,每一個階段的 top 屬性停留在不一樣的位置來實現

實現回彈效果 DEMO

/* 規定動畫,改變top,opacity */
@keyframes animate {
  0% {
    top: -100%;
    opacity: 0;
  }
  25% {
    top: 60;
    opacity: 1;
  }
  50% {
    top: 48%;
    opacity: 1;
  }
  75% {
    top: 52%;
    opacity: 1;
  }
  100%{
    top: 50%;
    opacity: 1;
  }
}

爲了讓過渡效果更天然,這裏經過 cubic-bezier() 函數定義一個貝塞爾曲線來控制動畫播放速度

過渡動畫執行完後,爲了將讓元素應用動畫最後一幀的屬性值,咱們須要使用 animation-fill-mode: forwards

.popup {
  animation-name: animate;
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.21, 0.85, 1, 1);
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  /* animation: animate 0.5s cubic-bezier(0.21, 0.85, 1, 1) 1 forwards; 動畫屬性簡寫 */
}

Animation 實現點贊效果 Online Code

實現點贊效果 DEMO

相信大多數同窗都知道點贊效果,本文章會實現一個簡易版的點贊效果,主要講述一下實現思路:

  1. 爲了讓氣泡能夠向上偏移,咱們須要先實現一個 y 軸方向上移動的 @keyframes 動畫
/* 規定動畫,改變y軸偏移距離*/
@keyframes animation-y {
  0%{
   transform:  translate(-50%, 100px) scale(0);
  }
  50%{
   transform:  translate(-50%, -100px) scale(1.5);
  }
  100%{
    transform:  translate(-50%, -300px) scale(1.5);
  }
}
  1. 爲了讓氣泡向上偏移時顯得不太單調,咱們能夠再實現一個 x 軸方向上移動的 @keyframes 動畫
/* 規定動畫,改變x軸偏移距離 */
@keyframes animation-x {
  0%{
    margin-left: 0px;
  }
  25%{
    margin-left: 25px;
  }
  75%{
    margin-left: -25px;
  }
  100%{
    margin-left: 0px;
  }
}

這裏我理解:

  • 雖然是修改 margin 來改變 x 軸偏移距離,但實際上與修改 transform沒有太大的性能差別
  • 由於經過 @keyframes animation-y 中的 transform 已經新建了一個渲染層 ( PaintLayers )
  • animation 屬性 可讓該渲染層提高至 合成層(Compositing Layers) 擁有單獨的圖形層 ( GraphicsLayer ),即開啓了硬件加速 ,不會影響其餘渲染層的 paint、layout
  • 對於合成層(Compositing Layers)相關知識不是很瞭解的同窗,能夠閱讀一下這篇文章從瀏覽器渲染層面解析 css3 動效優化原理
  • 以下圖所示:

如筆者這裏理解有誤,還請讀者大佬指出,感激涕零~

  1. 給氣泡應用上咱們所實現的兩個 @keyframes 動畫
.bubble {
  animation: animation-x 3s -2s linear infinite,animation-y 4s 0s linear 1;
/*  給 bubble 開啓了硬件加速 */
}
  1. 在點贊事件中,經過 js 操做動態添加/移除氣泡元素
function like() {
  const likeDom = document.createElement('div');
  likeDom.className = 'bubble'; // 添加樣式
  document.body.appendChild(likeDom);  // 添加元素
  setTimeout( () => {
    document.body.removeChild(likeDom);  // 移除元素
  }, 4000)
}

Animation 與 Svg 繪製 loading/進度條 組件 🔥 Online Code

Animation 與 Svg 繪製 loading/進度條 組件 🔥 DEMO

  1. 首先,咱們使用 svg 繪製一個圓周長爲2 * 25 * PI = 157 的圓
<svg with='200' height='200' viewBox="0 0 100 100"  >
  <circle cx="50" cy="50" r="25"  fill="transparent" stroke-width="4" stroke="#0079f5" ></circie>
</svg>

  1. 將實線圓繪製成虛線圓,這裏須要用 stoke-dasharray:50, 50 (可簡寫爲50) 屬性來繪製虛線, stoke-dasharray 參考資料
  • 它的值是一個數列,數與數之間用逗號或者空白隔開,指定短劃線(50px)缺口(50px)的長度。
  • 因爲50(短劃線) + 50(缺口) + 50(段劃線) = 150, 150 < 157,沒法繪製出完整的圓,因此會致使右邊存在缺口(7px)
<svg with='200' height='200' viewBox="0 0 100 100"  >
  <circle cx="50" cy="50" r="25"  fill="transparent" stroke-width="4" stroke-dasharray="50" stroke="#0079f5" ></circie>
</svg>

  1. stroke-dashoffset 屬性可使圓的短劃線和缺口產生偏移,添加 @keyframes 動畫後可以實現從無到有的效果,stoke-dashoffset 參考資料
  • 設置 stroke-dasharray="157 157",指定 短劃線(157px)缺口(157px) 的長度。
  • 添加 @keyframes 動畫 修改stroke-dashoffset值, 值爲正數逆時針偏移🔄,, 值爲負數時,順時針偏移🔃
@keyframes loading {
  0%{
    stroke-dashoffset: 0;
  }
  100%{
    stroke-dashoffset: -157; /* 線條順時針偏移 */
  }
}
circle{
    animation: loading 1s 0s ease-out infinite;
}

  1. 修改短劃線和缺口值
  • 爲了讓 loading 組件線條可見,咱們須要一個50px的短劃線,設置 stroke-dasharray="50"
  • 爲了讓短劃線發生偏移後能夠徹底消失,缺口須要大於或等於圓周長157,設置 stroke-dasharray="50 157"
  • 添加 @keyframes 動畫,爲了讓動畫結束時仍處理動畫開始位置,須要修改 stroke-dashoffset:-207(短劃線+缺口長度)
  • 進度條也是相似原理,幫助理解 stroke-dashoffset 屬性,具體實現請查看示例
@keyframes loading {
  0%{
    stroke-dashoffset: 0;
  }
  100%{
    stroke-dashoffset: -207; /* 保證動畫結束時仍處理動畫開始位置 */
  }
}
circle{
    animation: loading 1s 0s ease-out infinite;
}

Animation steps()運用

steps()animation-timing-function 的屬性值

animation-timing-function : steps(number[, end | start])
  • steps 函數指定了一個階躍函數,它接受兩個參數
  • 第一個參數接受一個整數值,表示兩個關鍵幀之間分幾步完成
  • 第二個參數有兩個值 start or end。默認值爲 end
  • step-start 等同於 step(1, start)。step-end 等同於 step(1, end)

steps 適用於關鍵幀動畫,第一個參數將兩個關鍵幀細分爲N幀,第二個參數決定從一幀到另外一幀的中間間隔是用開始幀仍是結束幀來進行填充。

看下圖能夠發現:

  • steps(N, start)將動畫分爲N段,動畫在每一段的起點發生階躍(即圖中的空心圓 → 實心圓),動畫結束時停留在了第 N 幀
  • steps(N, end)將動畫分爲N段,動畫在每一段的終點發生階躍(即圖中的空心圓 → 實心圓),動畫結束時第 N 幀已經被跳過(即圖中的空心圓 → 實心圓),停留在了 N+1 幀。

實踐出真知!

Animation 實現打字效果

Animation 實現打字效果 DEMO

  • 此處用英文字母(I'm an O2man.)舉例,一共有13個字符。[經測試,多數中文字體每一個字符寬高都相等]
  • steps(13)能夠將 @keyframes 動畫分爲13階段運行,且每一階段運行距離相等

效果以下:

/* 改變容器寬度 */
@keyframes animate-x {
  0%{
    width: 0;
  }
}

p {
    width: 125px;
    overflow: hidden;
    border-right: 1px solid transparent;
    animation: animate-x 3s 0s steps(13) 1 forwards;
}
  • 能夠發現僅僅這樣還不夠,動畫運行過程當中出現了字符被截斷的狀況,爲了保證每一個階段運行後能準確無誤地顯示當前所處階段的字符,咱們還須要保證每一個字符的width與動畫每一階段運行的距離相等
  • 設置Monaco字體屬性,用以保證每一個字符的 width 相同,具體像素受fontSize屬性影響,示例中的字體寬度約爲 9.6px,9.6px * 13(段數) = 124.8px (125px),因此當咱們設置容器寬度爲 125px,便可的達成目的:每一個字符的 width 與動畫每一階段運行的距離相等(約爲 9.6px )
p {
    /* 設置 Monaco 字體屬性,字體大小爲16px,用以保證每一個字符的 width 相同,width 約爲9.6p */
    font-family: Monaco;
    /* 9.6px * 13 = 124.8px (125px) */
    width: 125px ;
    font-size: 16px;
    overflow: hidden;
    border-right: 1px solid transparent;
    /* 同時應用動畫 animate-x、cursor-x */
    animation: animate-x 3s 0s steps(13) 1 forwards,cursor-x 0.4s 0s linear infinite;
}

Animation 實現幀動畫 ⏰

Animation 實現幀動畫 ⏰ DEMO

.main {
  width: 260px;
  height: 200px;
  background: url(url) no-repeat;
  background-size: 100%;
  background-position: 0 0;
}
  • 添加 @keyframes 修改 background-position,讓背景圖移動
@keyframes animate {
    0% {
        background-position: 0 0;
    }

    100% {
        background-position: 0 100%;
    }
}
.main{
  width: 260px;
  height: 200px;
  background: url(url) no-repeat;
  background-size: 100%;
  background-position: 0 0;
  animation: animate 2s 1s steps(47) infinite alternate;
}
  • 同時, css 還提供了animation-play-state用於控制動畫是否暫停
input:checked+.main{
    animation-play-state: paused;
}

文章篇幅較長,感謝你們的閱讀,但願各位看客可以有所收穫~ ~ ~


參考資料

Animation 經常使用動畫屬性
CSS 參考手冊
steps() 參考資料
SVG 學習之 stroke-dasharray 和 stroke-dashoffset 詳解
理解 CSS3 Animation 中的 steps()
【譯】css 動畫裏的 steps()用法詳解
CSS Will Change


歡迎關注凹凸實驗室博客:aotu.io

或者關注凹凸實驗室公衆號(AOTULabs),不定時推送文章。

相關文章
相關標籤/搜索