Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 一区二区免费视频,国产视频99,久久亚洲人成国产精品

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          前端系列:在線CSS3動畫制作

          前端系列:在線CSS3動畫制作

          分享下之前的文章:

          前端系列:純CSS代碼來寫多邊形和不規則圖形

          http://www.toutiao.com/i6366385579411636738/

          前端系列:如何用CSS代碼來寫26個字母,前端福利

          http://www.toutiao.com/i6366084511695897089/

          前端系列:在線認識貝塞爾曲線的運動軌跡(中文版網站)

          http://www.toutiao.com/i6363443065914393090/

          有興趣的可以看看,學習學習

          接下來說正題,先說明一下,這個是英文的網站,你可以用翻譯的插件來實現翻譯的功能,這個網站還是蠻不錯的,動畫制作好后,可以copy代碼,可以直接用,也可以自己研究是怎么實現這個功能的,一起學習,一起交流

          首先介紹頁面的整體布局,看下圖

          網站截圖-英文版

          網站截圖-中文版

          怎么編輯動畫呢,這里簡單做下介紹:

          首先重置一下動畫,點擊reset(重置),看下圖:

          重置操作

          動畫屬性調節

          時間,動畫屬性等設置好后,開始來編輯制作動畫,先在時間軸上點擊一個時間,然后在編輯區域里改變物體的位置(旋轉也可以),看不懂的看下面的動圖:

          動畫編輯演示

          也可以旋轉:

          旋轉也是可以的

          最后動畫做好了,就可以copy代碼了,直接點擊

          copy代碼

          有興趣的可以看看學學。

          網址是:http://cssanimate.com/

          想學習交流的可以打開下面網址掃描加入一起交流有趣好玩的分享:

          http://www.mackxin.com/xininn.html

          馨客棧導航:http://www.mackxin.com/nav.html

          馨客棧前端導航:http://www.mackxin.com/webnav.html

          關注分享,體驗樂趣

          分享是一種態度

          eb Animation API 介紹

          當我們談及網頁動畫時,自然聯想到的是 CSS3 動畫、JS 動畫、SVG 動畫 等技術以及 jQuery.animate() 等動畫封裝庫,根據實際動畫內容設計去選擇不同的實現方式,然而,每個現行的動畫技術都存在一定的缺點,如 CSS3動畫必須通過JS去獲取動態改變的值,一個動畫效果分散在css文件和js文件里不好維護,setInterval 的時間往往是不精確的而且還會卡頓,引入額外的動畫封裝庫也并非對性能敏感的業務適用。

          Web Animation API 的歷史也應該有幾年了,但是每當做動畫效果時,筆者就是依賴各種庫,很少想著去原生實現,最終造成了我們的項目各種依賴庫,體積也不斷變大,性能如何也不得而知,作為前端開發的我們多么希望原生的JS去支持通用的動畫解決方案, Web Animation API 可能就是一個不錯的解決方案。

          W3C 提出 Web Animation API(簡稱 WAAPI)正緣于此,它致力于集合 CSS3 動畫的性能、JavaScript 的靈活、動畫庫的豐富等各家所長,將盡可能多的動畫控制由原生瀏覽器實現,并添加許多 CSS 不具備的變量、控制以及或調的選項。它為我們提供了一種通用語言來描述DOM元素的動畫,主要方法有:Animation,KeyframeEffect,AnimationEvent,DocumentTimeline,EffectTiming。關于這個API的詳細介紹,可以參照MDN的這篇文檔,鏈接地址:https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API。

          使用Web Animations API,我們可以將交互式動畫從樣式表移動到JavaScript,將表示與行為分開。我們不再需要依賴DOM的技術,例如編寫CSS屬性作用于元素以控制方向。為了構建自定義動畫庫和創建交互式動畫,Web Animations API可能是完成工作的完美工具,你無需借助第三方動畫庫,就可以輕松實現一個效果不錯的動畫。

          為了讓大家對這個API有個清晰的認識,筆者在接下來的系列文章里,用五六個例子讓大家理解這個API,今天筆者將用此API實現一個隨機移動的圖片開始進行介紹,比如用這個效果我們可以制作一個隨機飄浮移動的廣告位,游戲里隨機走動的怪物等等,本例中的特點就是為了體現Web Animation API的靈活性和強大性,我沒有引用任何第三方類庫,比如(JQ)以及也沒有使用setTimeout和requestAnimationFrame()函數。

          本篇文章預計時間 5 分鐘

          動畫效果

          開始前,我們先來看看完成后的動畫效果,示例如下效果:

          頁面布局

          無論圖片怎么隨機移動,我們都希望在指定的容器里,而不是漫無邊際,首先我們在html頁面定義容器:

          <div id="container">
          </div>
          

          接下來定義容器的樣式:

          body {
           margin: 0;
          }
          div#container {
           height:500px;
           width:100%;
           background: #C6CEF7;
          }
          #target {
           position: absolute;
           filter: drop-shadow(-12px 12px 7px rgba(0,0,0,0.5));
          }
          

          腳本部分

          獲取容器

          var container=document.getElementById("container");
          

          加載動畫

          為了更加直觀性,我選擇一個走動的gif圖片,由于圖片的加載需要一些時間,為了不破壞動畫的連貫性,確保圖片加載完了我們在執行動畫,相關代碼如下:

          var target=document.createElement("img");
          target.id="target";
          target.onload=function() {
           floatHead();
          }
          target.src="walk.gif";
          container.appendChild(target);
          

          大家都看到了,onload部分我們加載了floatHead()函數,接下來我們來進行相關實現,此函數主要包含以下功能:創建一個隨機位置,計算移動時間,封裝移動動畫。

          隨機位置

          我們利用Math.floor函數實現了其隨機位置的變化,示例代碼如下:

          function makeNewPosition() {
           var containerVspace=container.offsetHeight - target.offsetHeight,
           containerHspace=container.offsetWidth - target.offsetWidth,
           newX=Math.floor(Math.random() * containerVspace),
           newY=Math.floor(Math.random() * containerHspace);
           return [newX, newY];
          }
          

          這里的隨機位置,我們返回了一個數組,描述的是圖片相對容器的位置,即top,left。這里你需要理解offsetHeight,offsetWidth,可理解為div的可視高度或寬度,樣式的height或Width+上下padding或左右padding+上下border-width或左右border-width。

          計算時間

          動畫是有時間屬性的,我們進行位置的移動,需要花多久時間,假設運動速度為0.1個單位/毫秒。這個函數包含兩個數組:prev為當前目標的原始X和Y位置,next為移動目標的位置。此函數沒有進行進行精確的距離計算,只是判斷了x和y軸上移動的距離大小用最大的距離除以速度,示例代碼如下:

          function velocity(prev, next) { 
           var x=Math.abs(prev[1] - next[1]),
           y=Math.abs(prev[0] - next[0]),
           larger=x > y ? x : y,
           speedModifier=0.1,
           time=Math.ceil(larger / speedModifier);
           return time; 
          }
          

          封裝移動動畫

          接下來是我們Web Animations API的核心部分,我們使用其核心API在加上上述我們完成的兩個函數讓其動起來,示例代碼如下:

          function floatHead() {
           var newPos=makeNewPosition(),
           oldTop=target.offsetTop,
           oldLeft=target.offsetLeft,
           target.animate([
           { top: oldTop+"px", left: oldLeft+"px" },
           { top: newPos[0]+"px", left: newPos[1]+"px" }
           ], {
           duration: velocity([oldTop, oldLeft],newPos),
           fill: "forwards"
           }).onfinish=function() {
           floatHead();
           }
          }
          

          該Animation的animate函數有兩個參數,一個是KeyframeEffects數組和AnimationEffectTimingPropertiesoptions 的對象。基本上,第一個參數映射到您將放入CSS中的內容@keyframes,你可以想象成css中的@keyframes內容,比如以下代碼:

          @keyframes emphasis {
           0% {
           transform: scale(1); 
           opacity: 1; 
           }
           30% {
           transform: scale(.5); 
           opacity: .5; 
           }
           78.75% {
           transform: scale(.667); 
           opacity: .667; 
           }
           100% {
           transform: scale(.6);
           opacity: .6; 
           }
          }
          

          你可以將“{}”里的信息順序依次放到一個數組里;第二個參數是時間控制 timing,包括有 duration 持續時間、iterations 執行次數、direction 動畫方向、easing 緩動函數等屬性。比如以下代碼:

          #toAnimate {
           animation: emphasis 700ms ease-in-out 10ms infinite alternate forwards;
          }
          

          你還可能注意到我們使用了onfinish事件完成了floatHead函數的反復調用,其是Animation的屬性,監聽動畫完成事件,如果動畫完成繼續執行floatHead(),相當不斷的遞歸調用。

          最終完成的代碼

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <style>
           body {
           margin: 0;
           }
           div#container {
           height:500px;
           width:100%;
           background: #C6CEF7;
           }
           #target {
           position: absolute;
           filter: drop-shadow(-12px 12px 7px rgba(0,0,0,0.5));
           }
           </style>
           <meta charset="UTF-8">
           <title>前端達人示例展示——圖片隨機移動</title>
          </head>
          <body>
          <div id="container"></div>
          <script>
           function makeNewPosition() {
           var containerVspace=container.offsetHeight - target.offsetHeight,
           containerHspace=container.offsetWidth - target.offsetWidth,
           newX=Math.floor(Math.random() * containerVspace),
           newY=Math.floor(Math.random() * containerHspace);
           return [newX, newY];
           }
           function velocity(prev, next) {
           var x=Math.abs(prev[1] - next[1]),
           y=Math.abs(prev[0] - next[0]),
           larger=x > y ? x : y,
           speedModifier=0.2,
           time=Math.ceil(larger / speedModifier);
           return time;
           }
           function floatHead() {
           var newPos=makeNewPosition(),
           oldTop=target.offsetTop,
           oldLeft=target.offsetLeft;
           target.animate([
           { top: oldTop+"px", left: oldLeft+"px" },
           { top: newPos[0]+"px", left: newPos[1]+"px" }
           ], {
           duration: velocity([oldTop, oldLeft],newPos),
           fill: 'forwards'
           }).onfinish=function() {
           floatHead();
           }
           }
           var container=document.getElementById("container"),
           target=document.createElement("img");
           target.id="target";
           target.onload=function() {
           floatHead();
           }
           target.src="walk.gif";
           target.width="200";
           container.appendChild(target);
          </script>
          </body>
          </html>
          

          兼容情況

          最后聊聊你關心的各瀏覽器兼容問題,如下所示顯示了各個瀏覽器的兼容情況:

          看來好多都是部分支持,沒有完全支持,筆者也親自測試了下,在pc端最新版的谷歌瀏覽器和Firefox是沒有任何問題的可以完美運行,筆者的safari還是運行不起來,在iPhone XS Max無法運行。

          作為一名前端開發者,在移動端大行其道怎么能容忍在手機端沒有效果,為了在現代瀏覽器廠商還沒完全跟進到位的時候搶先用上 WAAPI(Web Animation API簡稱),我們可以選擇引入針對 Web Animation API 的 Polyfill 庫 [https://github.com/web-animations/web-animations-js],從而在 IE/Firefox/Safari 等瀏覽器上體驗到 WAAPI 的精彩。

          因此我們只需要文件里引入以下js,就可以完美體驗:

          <script src="https://cdn.jsdelivr.net/web-animations/latest/web-animations.min.js"></script>
          

          移動端瀏覽器,Android 5.0 以上的 Android Browser 和 Chrome for Android 本身就已經支持 WAAPI 了,加上 Polyfill 之后,筆者的手機終于可以看到運行效果了,微信里的QQ內核瀏覽器也能完美運行,pc端的safari也可以完美運行。可以說是全平臺支持了,有了這個庫你可以放心大膽的使用了。

          小節

          好了今天的代碼擼完了,js代碼還不到50行(注:為了在手機端運行,引入了web-animations.min.js),您可以點擊"https://www.qianduandaren.com/demo/walk/"行預覽,筆者親測在iPhone XS Max運行良好,其他手機沒有,有待親們的測試,歡迎到留言區告知。下一篇文章我們用不到20行的原生js代碼純手工擼一個漂亮的時鐘,敬請期待。

          更多精彩內容,請微信關注“前端達人”公眾號

          文將比較全面細致的梳理一下 CSS 動畫的方方面面,針對每個屬性用法的講解及進階用法的示意,希望能成為一個比較好的從入門到進階的教程。

          CSS 動畫介紹及語法

          首先,我們來簡單介紹一下 CSS 動畫。

          最新版本的 CSS 動畫由規范 -- CSS Animations Level 1 定義。

          CSS 動畫用于實現元素從一個 CSS 樣式配置轉換到另一個 CSS 樣式配置。

          動畫包括兩個部分: 描述動畫的樣式規則和用于指定動畫開始、結束以及中間點樣式的關鍵幀。

          簡單來說,看下面的例子:

          div {
              animation: change 3s;
          }
          
          @keyframes change {
              0% {
                  color: #f00;
              }
              100% {
                  color: #000;
              }
          }
          
          1. animation: move 1s 部分就是動畫的第一部分,用于描述動畫的各個規則;
          2. @keyframes move {} 部分就是動畫的第二部分,用于指定動畫開始、結束以及中間點樣式的關鍵幀;

          一個 CSS 動畫一定要由上述兩部分組成。

          CSS 動畫的語法

          接下來,我們簡單看看 CSS 動畫的語法。

          創建動畫序列,需要使用 animation 屬性或其子屬性,該屬性允許配置動畫時間、時長以及其他動畫細節,但該屬性不能配置動畫的實際表現,動畫的實際表現是由 @keyframes 規則實現。

          animation 的子屬性有:

          • animation-name:指定由 @keyframes 描述的關鍵幀名稱。
          • animation-duration:設置動畫一個周期的時長。
          • animation-delay:設置延時,即從元素加載完成之后到動畫序列開始執行的這段時間。
          • animation-direction:設置動畫在每次運行完后是反向運行還是重新回到開始位置重復運行。
          • animation-iteration-count:設置動畫重復次數, 可以指定 infinite 無限次重復動畫
          • animation-play-state:允許暫停和恢復動畫。
          • animation-timing-function:設置動畫速度, 即通過建立加速度曲線,設置動畫在關鍵幀之間是如何變化。
          • animation-fill-mode:指定動畫執行前后如何為目標元素應用樣式
          • @keyframes 規則,當然,一個動畫想要運行,還應該包括 @keyframes 規則,在內部設定動畫關鍵幀

          其中,對于一個動畫:

          • 必須項animation-nameanimation-duration@keyframes規則
          • 非必須項animation-delayanimation-directionanimation-iteration-countanimation-play-stateanimation-timing-functionanimation-fill-mode,當然不是說它們不重要,只是不設置時,它們都有默認值

          上面已經給了一個簡單的 DEMO, 就用上述的 DEMO,看看結果:

          這就是一個最基本的 CSS 動畫,本文將從 animation 的各個子屬性入手,探究 CSS 動畫的方方面面。

          animation-name / animation-duration 詳解

          整體而言,單個的 animation-nameanimation-duration 沒有太多的技巧,非常好理解,放在一起。

          首先介紹一下 animation-name,通過 animation-name,CSS 引擎將會找到對應的 @keyframes 規則。

          當然,它和 CSS 規則命名一樣,也存在一些騷操作。譬如,他是支持 emoji 表情的,所以代碼中的 animation-name 命名也可以這樣寫:

          div {
              animation:  3s;
          }
          
          @keyframes  {
              0% {
                  color: #f00;
              }
              100% {
                  color: #000;
              }
          }
          

          animation-duration 設置動畫一個周期的時長,上述 DEMO 中,就是設定動畫整體持續 3s,這個也非常好理解。

          animation-delay 詳解

          animation-delay 就比較有意思了,它可以設置動畫延時,即從元素加載完成之后到動畫序列開始執行的這段時間。

          簡單的一個 DEMO:

          <div></div>
          <div></div>
          
          div {
              width: 100px;
              height: 100px;
              background: #000;
              animation-name: move;
              animation-duration: 2s;
          }
          
          div:nth-child(2) {
              animation-delay: 1s;
          }
          @keyframes move {
              0% {
                  transform: translate(0);
              }
              100% {
                  transform: translate(200px);
              }
          }
          

          比較下列兩個動畫,一個添加了 animation-delay,一個沒有,非常直觀:

          上述第二個 div,關于 animation 屬性,也可以簡寫為 animation: move 2s 1s,第一個時間值表示持續時間,第二個時間值表示延遲時間。

          animation-delay 可以為負值

          關于 animation-delay,最有意思的技巧在于,它可以是負數。也就是說,雖然屬性名是動畫延遲時間,但是運用了負數之后,動畫可以提前進行

          假設我們要實現這樣一個 loading 動畫效果:

          有幾種思路:

          1. 初始 3 個球的位置就是間隔 120°,同時開始旋轉,但是這樣代碼量會稍微多一點
          2. 另外一種思路,同一個動畫,3 個元素的其中兩個延遲整個動畫的 1/3,2/3 時間出發

          方案 2 的核心偽代碼如下:

          .item:nth-child(1) {
              animation: rotate 3s infinite linear;
          }
          .item:nth-child(2) {
              animation: rotate 3s infinite 1s linear;
          }
          .item:nth-child(3) {
              animation: rotate 3s infinite 2s linear;
          }
          

          但是,在動畫的前 2s,另外兩個元素是不會動的,只有 2s 過后,整個動畫才是我們想要的:

          此時,我們可以讓第 2、3 個元素的延遲時間,改為負值,這樣可以讓動畫延遲進行 -1s-2s,也就是提前進行 1s2s

          .item:nth-child(1) {
              animation: rotate 3s infinite linear;
          }
          .item:nth-child(2) {
              animation: rotate 3s infinite -1s linear;
          }
          .item:nth-child(3) {
              animation: rotate 3s infinite -2s linear;
          }
          

          這樣,每個元素都無需等待,直接就是運動狀態中的,并且元素間隔位置是我們想要的結果:

          利用 animation-duration 和 animation-delay 構建隨機效果

          還有一個有意思的小技巧。

          同一個動畫,我們利用一定范圍內隨機的 animation-duration 和一定范圍內隨機的 animation-delay,可以有效的構建更為隨機的動畫效果,讓動畫更加的自然。

          我在下述兩個純 CSS 動畫中,都使用了這樣的技巧:

          1. 純 CSS 實現華為充電動畫:
          1. 純 CSS 實現火焰動畫:

          純 CSS 實現華為充電動畫為例子,簡單講解一下。

          仔細觀察這一部分,上升的一個一個圓球,拋去這里的一些融合效果,只關注不斷上升的圓球,看著像是沒有什么規律可言:

          我們來模擬一下,如果是使用 10 個 animation-durationanimation-delay 都一致的圓的話,核心偽代碼:

          <ul>
              <li></li>
              <!--共 10 個...--> 
              <li></li>
          </ul>
          
          ul {
              display: flex;
              flex-wrap: nowrap;
              gap: 5px;
          }
          li {
              background: #000;
              animation: move 3s infinite 1s linear;
          }
          @keyframes move {
              0% {
                  transform: translate(0, 0);
              }
              100% {
                  transform: translate(0, -100px);
              }
          }
          

          這樣,小球的運動會是這樣的整齊劃一:

          要讓小球的運動顯得非常的隨機,只需要讓 animation-durationanimation-delay 都在一定范圍內浮動即可,改造下 CSS:

          @for $i from 1 to 11 {
              li:nth-child(#{$i}) {
                  animation-duration: #{random(2000)/1000 + 2}s;
                  animation-delay: #{random(1000)/1000 + 1}s;
              }
          }
          

          我們利用 SASS 的循環和 random() 函數,讓 animation-duration 在 2-4 秒范圍內隨機,讓 animation-delay 在 1-2 秒范圍內隨機,這樣,我們就可以得到非常自然且不同的上升動畫效果,基本不會出現重復的畫面,很好的模擬了隨機效果:

          CodePen Demo -- 利用范圍隨機 animation-duration 和 animation-delay 實現隨機動畫效果

          animation-timing-function 緩動函數

          緩動函數在動畫中非常重要,它定義了動畫在每一動畫周期中執行的節奏。

          緩動主要分為兩類:

          1. cubic-bezier-timing-function 三次貝塞爾曲線緩動函數
          2. step-timing-function 步驟緩動函數(這個翻譯是我自己翻的,可能有點奇怪)

          三次貝塞爾曲線緩動函數

          首先先看看三次貝塞爾曲線緩動函數。在 CSS 中,支持一些緩動函數關鍵字。

          /* Keyword values */
          animation-timing-function: ease;  // 動畫以低速開始,然后加快,在結束前變慢
          animation-timing-function: ease-in;  // 動畫以低速開始
          animation-timing-function: ease-out; // 動畫以低速結束
          animation-timing-function: ease-in-out; // 動畫以低速開始和結束
          animation-timing-function: linear; // 勻速,動畫從頭到尾的速度是相同的
          

          關于它們之間的效果對比:

          除了 CSS 支持的這 5 個關鍵字,我們還可以使用 cubic-bezier() 方法自定義三次貝塞爾曲線:

          animation-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);
          

          這里有個非常好用的網站 -- cubic-bezier 用于創建和調試生成不同的貝塞爾曲線參數。

          三次貝塞爾曲線緩動對動畫的影響

          關于緩動函數對動畫的影響,這里有一個非常好的示例。這里我們使用了純 CSS 實現了一個鐘的效果,對于其中的動畫的運動,如果是 animation-timing-function: linear,效果如下:

          而如果我們我把緩動函數替換一下,變成 animation-timing-function: cubic-bezier(1,-0.21,.85,1.29),它的曲線對應如下:

          整個鐘的動畫律動效果將變成這樣,完全不一樣的感覺:

          CodePen Demo - 緩動不同效果不同

          對于許多精益求精的動畫,在設計中其實都考慮到了緩動函數。我很久之前看到過一篇《基于物理學的動畫用戶體驗設計》,可惜如今已經無法找到原文。其中傳達出的一些概念是,動畫的設計依據實際在生活中的表現去考量。

          譬如 linear 這個緩動,實際應用于某些動畫中會顯得很不自然,因為由于空氣阻力的存在,程序模擬的勻速直線運動在現實生活中是很難實現的。因此對于這樣一個用戶平時很少感知到的運動是很難建立信任感的。這樣的勻速直線運動也是我們在進行動效設計時需要極力避免的。

          步驟緩動函數

          接下來再講講步驟緩動函數。在 CSS 的 animation-timing-function 中,它有如下幾種表現形態:

          {
              /* Keyword values */
              animation-timing-function: step-start;
              animation-timing-function: step-end;
          
              /* Function values */
              animation-timing-function: steps(6, start)
              animation-timing-function: steps(4, end);
          }
          

          在 CSS 中,使用步驟緩動函數最多的,就是利用其來實現逐幀動畫。假設我們有這樣一張圖(圖片大小為 1536 x 256,圖片來源于網絡):

          可以發現它其實是一個人物行進過程中的 6 種狀態,或者可以為 6 幀,我們利用 animation-timing-function: steps(6) 可以將其用一個 CSS 動畫串聯起來,代碼非常的簡單:

          <div class="box"></div>
          
          .box {
            width: 256px;
            height: 256px;
            background: url('https://github.com/iamalperen/playground/blob/main/SpriteSheetAnimation/sprite.png?raw=true');
            animation: sprite .6s steps(6, end) infinite;
          }
          @keyframes sprite {
            0% { 
              background-position: 0 0;
            }
            100% { 
              background-position: -1536px 0;
            }
          }
          

          簡單解釋一下上述代碼,首先要知道,剛好 256 x 6=1536,所以上述圖片其實可以剛好均分為 6 段:

          1. 我們設定了一個大小都為 256px 的 div,給這個 div 賦予了一個 animation: sprite .6s steps(6) infinite 動畫;
          2. 其中 steps(6) 的意思就是將設定的 @keyframes 動畫分為 6 次(6幀)執行,而整體的動畫時間是 0.6s,所以每一幀的停頓時長為 0.1s
          3. 動畫效果是由 background-position: 0 0background-position: -1536px 0,由于上述的 CSS 代碼沒有設置 background-repeat,所以其實 background-position: 0 0 是等價于 background-position: -1536px 0,就是圖片在整個動畫過程中推進了一輪,只不過每一幀停在了特點的地方,一共 6 幀;

          將上述 1、2、3,3 個步驟畫在圖上簡單示意:

          從上圖可知,其實在動畫過程中,background-position 的取值其實只有 background-position: 0 0background-position: -256px 0background-position: -512px 0 依次類推一直到 background-position: -1536px 0,由于背景的 repeat 的特性,其實剛好回到原點,由此又重新開始新一輪同樣的動畫。

          所以,整個動畫就會是這樣,每一幀停留 0.1s 后切換到下一幀(注意這里是個無限循環動畫),:

          完整的代碼你可以戳這里 -- CodePen Demo -- Sprite Animation with steps()

          animation-duration 動畫長短對動畫的影響

          在這里再插入一個小章節,animation-duration 動畫長短對動畫的影響也是非常明顯的。

          在上述代碼的基礎上,我們再修改 animation-duration,縮短每一幀的時間就可以讓步行的效果變成跑步的效果,同理,也可以增加每一幀的停留時間。讓每一步變得緩慢,就像是在步行一樣。

          需要提出的是,上文說的每一幀,和瀏覽器渲染過程中的 FPS 的每一幀不是同一個概念。

          看看效果,設置不同的 animation-duration 的效果(這里是 0.6s -> 0.2s),GIF 錄屏丟失了一些關鍵幀,實際效果會更好點:

          當然,在 steps() 中,還有 steps(6, start)steps(6, end) 的差異,也就是其中關鍵字 startend 的差異。對于上述的無限動畫而言,其實基本是可以忽略不計的,它主要是控制動畫第一幀的開始和持續時長,比較小的一個知識點但是想講明白需要比較長的篇幅,限于本文的內容,在這里不做展開,讀者可以自行了解。

          同個動畫效果的補間動畫和逐幀動畫演繹對比

          上述的三次貝塞爾曲線緩動和步驟緩動,其實就是對應的補間動畫和逐幀動畫。

          對于同個動畫而言,有的時候兩種緩動都是適用的。我們在具體使用的時候需要具體分析選取。

          假設我們用 CSS 實現了這樣一個圖形:

          現在想利用這個圖形制作一個 Loading 效果,如果利用補間動畫,也就是三次貝塞爾曲線緩動的話,讓它旋轉起來,得到的效果非常的一般:

          .g-container{
              animation: rotate 2s linear infinite;
          }
          @keyframes rotate {
              0% {
                  transform: rotate(0);
              }
              100% {
                  transform: rotate(360deg);
              }
          }
          

          動畫效果如下:

          但是如果這里,我們將補間動畫換成逐幀動畫,因為有 20 個點,所以設置成 steps(20),再看看效果,會得到完全不一樣的感覺:

          .g-container{
              animation: rotate 2s steps(20) infinite;
          }
          @keyframes rotate {
              0% {
                  transform: rotate(0);
              }
              100% {
                  transform: rotate(360deg);
              }
          }
          

          動畫效果如下:

          整個 loading 的圈圈看上去好像也在旋轉,實際上只是 20 幀關鍵幀在切換,整體的效果感覺更適合 Loading 的效果。

          因此,兩種動畫效果都是很有必要掌握的,在實際使用的時候靈活嘗試,選擇更適合的。

          上述 DEMO 效果完整的代碼:CodePen Demo -- Scale Loading steps vs linear

          animation-play-state

          接下來,我們講講 animation-play-state,顧名思義,它可以控制動畫的狀態 -- 運行或者暫停。類似于視頻播放器的開始和暫停。是 CSS 動畫中有限的控制動畫狀態的手段之一。

          它的取值只有兩個(默認為 running):

          {
              animation-play-state: paused | running;
          }
          

          使用起來也非常簡單,看下面這個例子,我們在 hover 按鈕的時候,實現動畫的暫停:

          <div class="btn stop">stop</div>
          <div class="animation"></div>
          
          .animation {
              width: 100px;
              height: 100px;
              background: deeppink;
              animation: move 2s linear infinite alternate;
          }
          
          @keyframes move {
              100% {
                  transform: translate(100px, 0);
              }
          }
          
          .stop:hover ~ .animation {
              animation-play-state: paused;
          }
          

          一個簡單的 CSS 動畫,但是當我們 hover 按鈕的時候,給動畫元素添加上 animation-play-state: paused

          animation-play-state 小技巧,默認暫停,點擊運行

          正常而言,按照正常思路使用 animation-play-state: paused 是非常簡單的。

          但是,如果我們想創造一些有意思的 CSS 動畫效果,不如反其道而行之。

          我們都知道,正常情況下,動畫應該是運行狀態,那如果我們將一些動畫的默認狀態設置為暫停,只有當鼠標點擊或者 hover 的時候,才設置其 animation-play-state: running,這樣就可以得到很多有趣的 CSS 效果。

          看個倒酒的例子,這是一個純 CSS 動畫,但是默認狀態下,動畫處于 animation-play-state: paused,也就是暫停狀態,只有當鼠標點擊杯子的時,才設置 animation-play-state: running,讓酒倒下,利用 animation-play-state 實現了一個非常有意思的交互效果:

          完整的 DEMO 你可以戳這里:CodePen Demo -- CSS Beer!

          在非常多 Web 創意交互動畫我們都可以看到這個技巧的身影。

          1. 頁面 render 后,無任何操作,動畫不會開始。只有當鼠標對元素進行 click ,通過觸發元素的 :active 偽類效果的時候,賦予動畫 animation-play-state: running,動畫才開始進行;
          2. 動畫進行到任意時刻,鼠標停止點擊,偽類消失,則動畫停止;

          animation-fill-mode 控制元素在各個階段的狀態

          下一個屬性 animation-fill-mode,很多人會誤認為它只是用于控制元素在動畫結束后是否復位。這個其實是不準確的,不全面的。

          看看它的取值:

          {
              // 默認值,當動畫未執行時,動畫將不會將任何樣式應用于目標,而是使用賦予給該元素的 CSS 規則來顯示該元素的狀態
              animation-fill-mode: none;
              // 動畫將在應用于目標時立即應用第一個關鍵幀中定義的值,并在 `animation-delay` 期間保留此值,
              animation-fill-mode: backwards; 
              // 目標將保留由執行期間遇到的最后一個關鍵幀計算值。 最后一個關鍵幀取決于 `animation-direction` 和 `animation-iteration-count`
              animation-fill-mode: forwards;    
              // 動畫將遵循 `forwards` 和 `backwards` 的規則,從而在兩個方向上擴展動畫屬性
              animation-fill-mode: both; 
          }
          

          對于 animation-fill-mode 的解讀,我在 Segment Fault 上的一個問答中(SF - 如何理解 animation-fill-mode)看到了 4 副很好的解讀圖,這里借用一下:

          假設 HTML 如下:

          <div class="box"></div>
          

          CSS如下:

          .box{
              transform: translateY(0);
          }
          .box.on{
              animation: move 1s;
          }
          
          @keyframes move{
              from{transform: translateY(-50px)}
              to  {transform: translateY( 50px)}
          }
          

          使用圖片來表示 translateY 的值與 時間 的關系:

          • 橫軸為表示 時間,為 0 時表示動畫開始的時間,也就是向 box 加上 on 類名的時間,橫軸一格表示 0.5s
          • 縱軸表示 translateY 的值,為 0 時表示 translateY 的值為 0,縱軸一格表示 50px
          1. animation-fill-mode: none 表現如圖:

          一句話總結,元素在動畫時間之外,樣式只受到它的 CSS 規則限制,與 @keyframes 內的關鍵幀定義無關。

          1. animation-fill-mode: backwards 表現如圖:

          一句話總結,元素在動畫開始之前(包含未觸發動畫階段及 animation-delay 期間)的樣式為動畫運行時的第一幀,而動畫結束后的樣式則恢復為 CSS 規則設定的樣式。

          1. animation-fill-mode: forwards 表現如圖:

          一句話總結,元素在動畫開始之前的樣式為 CSS 規則設定的樣式,而動畫結束后的樣式則表現為由執行期間遇到的最后一個關鍵幀計算值(也就是停在最后一幀)。

          1. animation-fill-mode: both 表現如圖:

          一句話總結,綜合了 animation-fill-mode: backwardsanimation-fill-mode: forwards 的設定。動畫開始前的樣式為動畫運行時的第一幀,動畫結束后停在最后一幀。

          animation-iteration-count/animation-direction 動畫循環次數和方向

          講到了 animation-fill-mode,我們就可以順帶講講這個兩個比較好理解的屬性 -- animation-iteration-countanimation-direction

          • animation-iteration-count 控制動畫運行的次數,可以是數字或者 infinite,注意,數字可以是小數
          • animation-direction 控制動畫的方向,正向、反向、正向交替與反向交替

          在上面講述 animation-fill-mode 時,我使用了動畫運行時的第一幀替代了@keyframes 中定義的第一幀這種說法,因為動畫運行的第一幀和最后一幀的實際狀態還會受到動畫運行方向 animation-directionanimation-iteration-count 的影響。

          在 CSS 動畫中,由 animation-iteration-countanimation-direction 共同決定動畫運行時的第一幀和最后一幀的狀態。

          1. 動畫運行的第一幀由 animation-direction 決定
          2. 動畫運行的最后一幀由 animation-iteration-countanimation-direction 決定

          動畫的最后一幀,也就是動畫運行的最終狀態,并且我們可以利用 animation-fill-mode: forwards 讓動畫在結束后停留在這一幀,這個還是比較好理解的,但是 animation-fill-mode: backwardsanimation-direction 的關系很容易弄不清楚,這里簡答講解下。

          設置一個 100px x 100px 的滑塊,在一個 400px x 100px 的容器中,其代碼如下:

          <div class="g-father">
              <div class="g-box"></div>
          </div>
          
          .g-father {
              width: 400px;
              height: 100px;
              border: 1px solid #000;
          }
          .g-box {
              width: 100px;
              height: 100px;
              background: #333;
          }
          

          表現如下:

          那么,加入 animation 之后,在不同的 animation-iteration-countanimation-direction 作用下,動畫的初始和結束狀態都不一樣。

          如果設置了 animation-fill-mode: backwards,則元素在動畫未開始前的狀態由 animation-direction 決定:

          .g-box {
              ...
              animation: move 4s linear;
              animation-play-state: paused;
              transform: translate(0, 0);
          }
          @keyframes move {
              0% {
                  transform: translate(100px, 0);
              }
              100% {
                  transform: translate(300px, 0);
              }
          }
          

          注意這里 CSS 規則中,元素沒有設置位移 transform: translate(0, 0),而在動畫中,第一個關鍵幀和最后一個關鍵的 translateX 分別是 100px300px,配合不同的 animation-direction 初始狀態如下。

          下圖假設我們設置了動畫默認是暫停的 -- animation-play-state: paused,那么動畫在開始前的狀態為:

          動畫的分治與復用

          講完了每一個屬性,我們再來看看一些動畫使用過程中的細節。

          看這樣一個動畫:

          <div></div>
          
          div {
              width: 100px;
              height: 100px;
              background: #000;
              animation: combine 2s;
          }
          @keyframes combine {
              100% {
                  transform: translate(0, 150px);
                  opacity: 0;
              }
          }
          

          這里我們實現了一個 div 塊下落動畫,下落的同時產生透明度的變化:

          對于這樣一個多個屬性變化的動畫,它其實等價于:

          div {
              animation: falldown 2s, fadeIn 2s;
          }
          
          @keyframes falldown {
              100% {
                  transform: translate(0, 150px);
              }
          }
          @keyframes fadeIn {
              100% {
                  opacity: 0;
              }
          }
          

          在 CSS 動畫規則中,animation 是可以接收多個動畫的,這樣做的目的不僅僅只是為了復用,同時也是為了分治,我們對每一個屬性層面的動畫能夠有著更為精確的控制。

          keyframes 規則的設定

          我們經常能夠在各種不同的 CSS 代碼見到如下兩種 CSS @keyframes 的設定:

          1. 使用百分比
          @keyframes fadeIn {
              0% {
                  opacity: 1;
              }
              100% {
                  opacity: 0;
              }
          }
          
          1. 使用 fromto
          @keyframes fadeIn {
              from {
                  opacity: 1;
              }
              to {
                  opacity: 0;
              }
          }
          

          在 CSS 動畫 @keyframes 的定義中,from 等同于 0%,而 to 等同于 100%

          當然,當我們的關鍵幀不止 2 幀的時,更推薦使用百分比定義的方式。

          除此之外,當動畫的起始幀等同于 CSS 規則中賦予的值并且沒有設定 animation-fill-mode0%from 這一幀是可以刪除的。

          動畫狀態的高優先級性

          我曾經在這篇文章中 -- 深入理解 CSS(Cascading Style Sheets)中的層疊(Cascading) 講過一個很有意思的 CSS 現象。

          這也是很多人對 CSS 優先級的一個認知誤區,在 CSS 中,優先級還需要考慮選擇器的層疊(級聯)順序

          只有在層疊順序相等時,使用哪個值才取決于樣式的優先級。

          那什么是層疊順序呢?

          根據 CSS Cascading 4 最新標準:

          CSS Cascading and Inheritance Level 5(Current Work)

          定義的當前規范下申明的層疊順序優先級如下(越往下的優先級越高,下面的規則按升序排列):

          • Normal user agent declarations
          • Normal user declarations
          • Normal author declarations
          • Animation declarations
          • Important author declarations
          • Important user declarations
          • Important user agent declarations
          • Transition declarations

          簡單翻譯一下:

          按照上述算法,大概是這樣:

          過渡動畫過程中每一幀的樣式 > 用戶代理、用戶、頁面作者設置的!important樣式 > 動畫過程中每一幀的樣式優先級 > 頁面作者、用戶、用戶代理普通樣式。

          然而,經過多個瀏覽器的測試,實際上并不是這樣。(尷尬了)

          舉個例子,我們可以通過這個特性,覆蓋掉行內樣式中的 !important 樣式:

          <p class="txt" style="color:red!important">123456789</p>
          
          .txt {
              animation: colorGreen 2s infinite;
          }
          @keyframes colorGreen {
              0%,
              100% {
                  color: green;
              }
          }
          

          在 Safari 瀏覽器下,上述 DEMO 文本的顏色為綠色,也就是說,處于動畫狀態中的樣式,能夠覆蓋掉行內樣式中的 !important 樣式,屬于最最高優先級的一種樣式,我們可以通過無限動畫、或者 animation-fill-mode: forwards,利用這個技巧,覆蓋掉本來應該是優先級非常非常高的行內樣式中的 !important 樣式。

          我在早兩年的 Chrome 中也能得到同樣的結果,但是到今天(2022-01-10),最新版的 Chrome 已經不支持動畫過程中關鍵幀樣式優先級覆蓋行內樣式 !important 的特性。

          對于不同瀏覽器,感興趣的同學可以利用我這個 DEMO 自行嘗試,CodePen Demo - the priority of CSS Animation

          CSS 動畫的優化

          這也是非常多人非常關心的一個重點。

          我的 CSS 動畫很卡,我應該如何去優化它?

          動畫元素生成獨立的 GraphicsLayer,強制開始 GPU 加速

          CSS 動畫很卡,其實是一個現象描述,它的本質其實是在動畫過程中,瀏覽器刷新渲染頁面的幀率過低。通常而言,目前大多數瀏覽器刷新率為 60 次/秒,所以通常來講 FPS 為 60 frame/s 時動畫效果較好,也就是每幀的消耗時間為 16.67ms。

          頁面處于動畫變化時,當幀率低于一定數值時,我們就感覺到頁面的卡頓。

          而造成幀率低的原因就是瀏覽器在一幀之間處理的事情太多了,超過了 16.67ms,要優化每一幀的時間,又需要完整地知道瀏覽器在每一幀干了什么,這個就又涉及到了老生常談的瀏覽器渲染頁面。

          到今天,雖然不同瀏覽器的渲染過程不完全相同,但是基本上大同小異,基本上都是:

          簡化一下也就是這個圖:

          這兩張圖,你可以在非常多不同的文章中看到。

          回歸本文的重點,Web 動畫很大一部分開銷在于層的重繪,以層為基礎的復合模型對渲染性能有著深遠的影響。當不需要繪制時,復合操作的開銷可以忽略不計,因此在試著調試渲染性能問題時,首要目標就是要避免層的重繪。那么這就給動畫的性能優化提供了方向,減少元素的重繪與回流

          這其中,如何減少頁面的回流與重繪呢,這里就會運用到我們常說的** GPU 加速**。

          GPU 加速的本質其實是減少瀏覽器渲染頁面每一幀過程中的 reflow 和 repaint,其根本,就是讓需要進行動畫的元素,生成自己的 GraphicsLayer

          瀏覽器渲染一個頁面時,它使用了許多沒有暴露給開發者的中間表現形式,其中最重要的結構便是層(layer)。

          在 Chrome 中,存在有不同類型的層: RenderLayer(負責 DOM 子樹),GraphicsLayer(負責 RenderLayer 的子樹)。

          GraphicsLayer ,它對于我們的 Web 動畫而言非常重要,通常,Chrome 會將一個層的內容在作為紋理上傳到 GPU 前先繪制(paint)進一個位圖中。如果內容不會改變,那么就沒有必要重繪(repaint)層。

          而當元素生成了自己的 GraphicsLayer 之后,在動畫過程中,Chrome 并不會始終重繪整個層,它會嘗試智能地去重繪 DOM 中失效的部分,也就是發生動畫的部分,在 Composite 之前,頁面是處于一種分層狀態,借助 GPU,瀏覽器僅僅在每一幀對生成了自己獨立 GraphicsLayer 元素層進行重繪,如此,大大的降低了整個頁面重排重繪的開銷,提升了頁面渲染的效率。

          因此,CSS 動畫(Web 動畫同理)優化的第一條準則就是讓需要動畫的元素生成了自己獨立的 GraphicsLayer,強制開始 GPU 加速,而我們需要知道是,GPU 加速的本質是利用讓元素生成了自己獨立的 GraphicsLayer,降低了頁面在渲染過程中重繪重排的開銷。

          當然,生成自己的獨立的 GraphicsLayer,不僅僅只有 transform3d api,還有非常多的方式。對于上述一大段非常繞的內容,你可以再看看這幾篇文章:

          • 【Web動畫】CSS3 3D 行星運轉 && 瀏覽器渲染原理
          • Accelerated Rendering in Chrome

          除了上述準則之外,還有一些提升 CSS 動畫性能的建議:

          減少使用耗性能樣式

          不同樣式在消耗性能方面是不同的,改變一些屬性的開銷比改變其他屬性要多,因此更可能使動畫卡頓。

          例如,與改變元素的文本顏色相比,改變元素的 box-shadow 將需要開銷大很多的繪圖操作。box-shadow 屬性,從渲染角度來講十分耗性能,原因就是與其他樣式相比,它們的繪制代碼執行時間過長。這就是說,如果一個耗性能嚴重的樣式經常需要重繪,那么你就會遇到性能問題。

          類似的還有 CSS 3D 變換、mix-blend-modefilter,這些樣式相比其他一些簡單的操作,會更加的消耗性能。我們應該盡可能的在動畫過程中降低其使用的頻率或者尋找替代方案。

          當然,沒有不變的事情,在今天性能很差的樣式,可能明天就被優化,并且瀏覽器之間也存在差異。

          因此關鍵在于,我們需要針對每一起卡頓的例子,借助開發工具來分辨出性能瓶頸所在,然后設法減少瀏覽器的工作量。學會 Chrome 開發者工具的 Performance 面板及其他渲染相關的面板非常重要,當然這不是本文的重點。大家可以自行探索。

          使用 will-change 提高頁面滾動、動畫等渲染性能

          will-change 為 Web 開發者提供了一種告知瀏覽器該元素會有哪些變化的方法,這樣瀏覽器可以在元素屬性真正發生變化之前提前做好對應的優化準備工作。 這種優化可以將一部分復雜的計算工作提前準備好,使頁面的反應更為快速靈敏。

          值得注意的是,用好這個屬性并不是很容易:

          • 不要將 will-change 應用到太多元素上:瀏覽器已經盡力嘗試去優化一切可以優化的東西了。有一些更強力的優化,如果與 will-change 結合在一起的話,有可能會消耗很多機器資源,如果過度使用的話,可能導致頁面響應緩慢或者消耗非常多的資源。
          • 有節制地使用:通常,當元素恢復到初始狀態時,瀏覽器會丟棄掉之前做的優化工作。但是如果直接在樣式表中顯式聲明了 will-change 屬性,則表示目標元素可能會經常變化,瀏覽器會將優化工作保存得比之前更久。所以最佳實踐是當元素變化之前和之后通過腳本來切換 will-change 的值。
          • 不要過早應用 will-change 優化:如果你的頁面在性能方面沒什么問題,則不要添加 will-change 屬性來榨取一丁點的速度。 will-change 的設計初衷是作為最后的優化手段,用來嘗試解決現有的性能問題。它不應該被用來預防性能問題。過度使用 will-change 會導致大量的內存占用,并會導致更復雜的渲染過程,因為瀏覽器會試圖準備可能存在的變化過程。這會導致更嚴重的性能問題。
          • 給它足夠的工作時間:這個屬性是用來讓頁面開發者告知瀏覽器哪些屬性可能會變化的。然后瀏覽器可以選擇在變化發生前提前去做一些優化工作。所以給瀏覽器一點時間去真正做這些優化工作是非常重要的。使用時需要嘗試去找到一些方法提前一定時間獲知元素可能發生的變化,然后為它加上 will-change 屬性。

          有人說 will-change 是良藥,也有人說是毒藥,在具體使用的時候,可以多測試一下。

          最后

          好了,本文從多個方面,由淺入深地描述了 CSS 動畫我認為的一些比較重要、值得一講、需要注意的點。當然很多地方點到即止,或者限于篇幅沒有完全展開,很多細節還需要讀者進一步閱讀規范或者自行嘗試驗證,實踐出真知,紙上得來終覺淺。

          OK,本文到此結束,希望本文對你有所幫助 :)


          主站蜘蛛池模板: 国产午夜精品一区二区三区不卡| 国产一区二区三区四| 无码人妻一区二区三区在线| 国产在线观看一区二区三区精品| 国产av成人一区二区三区| 日韩精品人妻一区二区中文八零 | 亚洲欧洲一区二区| 91久久精品午夜一区二区| 欧美日韩一区二区成人午夜电影| 无码人妻精一区二区三区| 精品天海翼一区二区| 国产一区二区三区在线视頻| 无码少妇一区二区性色AV | 激情综合一区二区三区| 国产主播一区二区| 中文字幕乱码人妻一区二区三区 | 日本一区二区不卡视频| 亚洲精品精华液一区二区| 国产aⅴ精品一区二区三区久久| 中文字幕一区二区三区在线播放| 亚洲AV无码一区二区三区人| 精品一区二区三区在线播放| 色欲综合一区二区三区| 国产精品一区二区无线| 久久免费区一区二区三波多野| 国产精品视频一区国模私拍 | 波多野结衣一区二区三区| 欧洲精品码一区二区三区免费看 | 国产成人高清亚洲一区久久| 国产亚洲一区二区三区在线| 国产精品成人免费一区二区| 成人精品一区二区三区不卡免费看| 亚洲高清一区二区三区 | 日本免费一区二区三区最新vr| 中文国产成人精品久久一区| 韩国精品一区视频在线播放| 无码国产精品一区二区免费模式| 美女免费视频一区二区| 毛片一区二区三区| 无码国产精品一区二区免费模式| 午夜肉伦伦影院久久精品免费看国产一区二区三区 |