先介紹CSS3動畫如何使用?兩步即可搞定。
-
定義動畫規則
@keyframe animationName{ timeStamp{ attr1:value1; attr2:value2; ... attrn,valuen; } } 複製程式碼
說明:
- animationName表示動畫名稱;
- timeStamp表示時間點,可取值from,to或0-100%,from表示0,to表示100%;
- 每個時間點可以指定多個樣式屬性attr與對應值value。
-
元素應用動畫
elemSelector{ animation: animationName animationDuration; } 複製程式碼
說明:
- elemSelector表示元素選擇器
- 執行一個動畫至少指定動畫名稱animationName和動畫時長animationDuration
- animation屬性是以下屬性的簡寫形式,每個屬性的含義說明如下表,
屬性名稱 | 含義 |
animation-name | @keyframe動畫的名稱 |
animation-duration | 動畫的時長,需指定單位如s或ms,預設值為0 |
animation-timing-function | 動畫的速度曲線,預設值為ease,其他取值有ease-in、ease-out、ease-in-out、linear、step-start、step-end、貝塞爾曲線函式cubic-bezier、步進函式steps |
animation-delay | 動畫延時多久開始,需指定指定單位如s或ms,預設為0,,取正值表示延時,負值表示超前 |
animation-iteration-count | 動畫播放次數,預設為1 |
animation-direction | 動畫是否在下一週期逆向播放,預設是normal,其他取值有reverse、alternate、alternate-reverse |
animation-play-state | 動畫的播放狀態,是執行還是暫停,預設是running,其他取值有paused |
animation-fill-mode | 動畫執行前、後是否應用目標狀態,預設是none,其他取值有forwards、backwards、both |
重點來了,一個倒數計時效果是如何實現的,先看效果。由於是壓縮生成的gif,所以看起來會很快。
下面對主要程式碼進行說明,
-
HTML部分
<!--用於呈現數字--> <div id="number"></div> <!--用於重新倒數計時--> <button class="button">再來一次</button> <!--用於最後的聲音播放。--> <audio ></audio> 複製程式碼
-
CSS部分
/*初始化數字div樣式*/ #number { position: absolute; top: 50%; left: 50%; text-align: center; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } /*繫結動畫*/ .number-anim { -webkit-animation: animation_in 10s linear; -moz-animation: animation_in 10s linear; /*動畫時長是10s,且動畫的速度是線性的*/ animation: animation_in 10s linear; } /*定義動畫規則*/ @keyframes animation_in { 0 {} 10% { /*由於動畫時長是10s,此時是1s時刻,字型大小變為100px,z方向距離主螢幕為300px*/ font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 11% { /*隨後字型大小變為300px,z方向距離主螢幕為0,後面每到整秒數都會重複進行*/ font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 20% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 21% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 30% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 31% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 40% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 41% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 50% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 51% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 60% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 61% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 70% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 71% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 80% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 81% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 90% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } 91% { font-size: 300px; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } 100% { font-size: 100px; -webkit-transform: translate3d(-50%, -50%, -300px); -moz-transform: translate3d(-50%, -50%, -300px); transform: translate3d(-50%, -50%, -300px); } } 複製程式碼
-
JS部分
window.onload = function() { //第一步:定義全域性變數 num = 10;//數字內容 isCounting = true;//是否正在計數 timer = null;//定時器 numberDiv = document.querySelector("#number"); audio = document.querySelector("audio"); button = document.querySelector(".button"); //第二步:初始化 init(); //第三步:開始倒數計時 timer = setInterval(count, 1000); } 複製程式碼
初始化函式宣告如下,
function init() { numberDiv.innerHTML = num; numberDiv.style.color = getRandomColor(); numberDiv.style.fontSize = "300px"; numberDiv.className = "number-anim"; button.addEventListener("click", function() { if (isCounting) { return; } isCounting = true; num = 10; numberDiv.innerHTML = num; numberDiv.style.color = getRandomColor(); numberDiv.style.fontSize = "300px"; //先解綁,再使用setTimeout使瀏覽器重新渲染頁面,重新繫結 //setTimeout只是一種方式,只要能使瀏覽器重新渲染即可 numberDiv.className = null; setTimeout(function() { numberDiv.className = "number-anim"; timer = setInterval(count, 1000); }, 30); }, false); } 複製程式碼
倒計數函式宣告如下,
function count() { numberDiv.innerHTML = --num; numberDiv.style.color = getRandomColor(); if (num == 0) { clearInterval(timer); audio.src="./audio/readygo.mp3"; audio.play(); numberDiv.innerHTML = "Ready Go!"; numberDiv.style.color = getRandomColor(); numberDiv.style.fontSize = "100px"; numberDiv.style.opacity = 0.8; numberDiv.style.filter = "alpha(opacity=80)"; CompatibleFunc(numberDiv, "Transition", "opacity 1s"); isCounting = false; } } 複製程式碼
這裡有一個地方需要特別注意,避免以後踩坑。
關於動畫樣式的解綁與繫結。
動畫樣式animation一執行結束,就不再起作用了,要使其重新生效,需要重新繫結。我試了4種方式。
-
第一種是直接解綁,然後再繫結。
numberDiv.className = null; numberDiv.className = "number-anim"; 複製程式碼
結果是不能使動畫重新生效。
-
第二種是使用classlist屬性進行解繫結。
numberDiv.classList.toggle("number-anim"); numberDiv.classList.toggle("number-anim"); 複製程式碼
classList返回類名列表物件,呼叫toggle方法,若類名存在則刪除,返回false,若類名不存在則新增,返回true,所以要呼叫兩次,第一次刪除類名,第二次新增類名。但是結果依然是不能使動畫重新生效。
-
第三種是開啟定時器。
先解綁,再利用定時器使瀏覽器重新渲染頁面,重新繫結。setTimeout只是一種方式,只要能使瀏覽器重新渲染即可,最終動畫重新生效。
numberDiv.className = null; setTimeout(function() { numberDiv.className = "number-anim"; }, 30); 複製程式碼
在mozilla官方文件介紹了另一種重新渲染方式,
https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Animations/Tips 複製程式碼
應用在這裡的話,寫法是,
numberDiv.className = null; window.requestAnimationFrame(function(){ window.requestAnimationFrame(function(){ numberDiv.className = "number-anim"; }); }); 複製程式碼
-
第四種是藉助其他重新渲染途徑,如顏色、內容、大小的變化。
numberDiv.className = null; numberDiv.innerHTML = num; numberDiv.style.color = getRandomColor(); numberDiv.style.fontSize = "300px"; numberDiv.className = "number-anim"; 複製程式碼
結果IE和Microsoft Edge 瀏覽器是支援的,但是FireFox和Chrome不支援, 但這是不是也說明了Chrome和FireFox已經對瀏覽器渲染做了優化?
簡而言之,要使動畫重新生效,需要觸發瀏覽器重新渲染。
好了,附上原始碼連結。
https://github.com/muzhidong/frontend-demo/tree/master/countdown
複製程式碼