CSS 動畫練習 - 點擊元素改變背景色再逐漸淡化
2 |
想在網頁加上動畫效果,資料表格中在某列資料變化時改變背景色吸引注意,再使用淡化效果恢復原本背景色。
以此目標我設計了一個綜合練習題,在網頁上放五個 DIV,點擊後使用 CSS 動畫效果將背景色改為紫色再逐漸淡化,1.5 秒後恢復原來顏色。若連續點擊同一 DIV,需停止現有動畫,重頭播放。另外,設計一按鈕,按下時會連續隨機點擊 DIV。
先看成果:
網頁以原生 CSS + JavaScript 實現,使用到以下技巧:
- 以 @keyframes 定義起始及結束狀態之 CSS 樣式
animation: <keyframes-name> 1.5s ease-out;
定義 .hit 時播放背景色漸變動畫(由藍紫轉淺藍),長度 1.5 秒,採先快後慢(ease-out)配速
延伸閱讀:CSS transition 各種速率 by 卡斯伯- 在容器掛 click 事件但操控 e.target 元素,相當於 jQuery
.on('click', '子元素選取器', function() {...})
- 點擊 DIV 時移除 .hit,稍後再加上 .hit 以停止目前動畫重新播放
- 稍後加上 .hit 使用 requestAnimationFrame() 實現,精準抓住下次繪製時觸發。傳統使用 setTimeout 要自己抓延遲,可能過長可能過短
- 動畫播放完畢靠 animationend 事件移除 .hit
- animationend 只需執行一次,在 jQuery 可用 .one(),香草 JavaScirpt 的話可寫成 addEventListener(fn, )
- document.querySelectorAll('.list div') 傳回型別為 NodeList,類似陣列但 API 較少(例如沒有 map()、filter()),我習慣用
[...nodeListVar]
轉為標準陣列 (... 為 Spread Syntax 展開語法,可用於函式參數、展開併入陣列、合併物件屬性,好用!)
完整程式碼如下,想試玩這裡有線上展示。
<!DOCTYPE html>
<html>
<head>
<title>Animation Test</title>
<style>
.list {
display: flex;
}
.list div {
width: 32px;
height: 32px;
text-align: center;
font-size: 16pt;
border-radius: 15px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 6px;
cursor: pointer;
background-color: #eee;
user-select: none;
}
.list div.hit {
animation: fade 1.5s ease-out;
}
@keyframes fade {
from {
background-color: blueviolet;
}
to {
background-color: #ddf;
}
}
button {
margin: 12px;
width: 100px;
}
</style>
</head>
<body>
<div class="list">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
<button id="b">Play</button>
<input type="range" id="r" min="50" max="1000" value="450">
<script>
document.querySelector('.list').addEventListener('click',
function (e) {
// 聽 .list click 事件再由 e.target 判斷是不是 div
// 相當於 $('.list').on('click', 'div', function(e) { ... });
const t = e.target;
if (t.tagName === 'DIV') {
// 移除 hit class 再加回去,若仍在播放動畫可停止現有動畫,觸發重新播放
console.log('remove');
t.classList.remove('hit');
// 這裡用 setTimeout 或 requestAnimationFrame 再加回 hit class
// 如果直接加回去,不會播放動畫
// requestAnimationFrame 比 setTimeout 好,會抓準在下次繪製時執行
// 不像 setTimeout 自己抓時間可能過長或過短
requestAnimationFrame(function () {
t.classList.add('hit');
// 動畫播放完後移除 hit class,這裡利用 animationend 事件
t.addEventListener('animationend', function () {
t.classList.remove('hit');
}, { // addEventListener 第三個參數可以設定 options, once 表示只執行一次
once: true
});
});
}
});
// 按鈕自動播放
let running = false;
document.getElementById('b').addEventListener('click', (e) => {
running = !running; // 切換播放狀態
e.target.textContent = running ? 'Stop' : 'Play';
if (running) play();
});
// 取得 .list div 陣列
// querySelectorAll 回傳的是 NodeList,不是 Array
// 這裡用 [...NodeList] 展開成 Array
let divs = [...document.querySelectorAll('.list div')];
function play() {
// 隨機點擊一個 div
let i = Math.floor(Math.random() * divs.length);
divs[i].click();
// 如果還在播放,就隨機時間後點擊一個 div,隨機長度範圍由 input range 決定
if (running)
setTimeout(play, Math.random() * document.getElementById('r').value);
}
</script>
</body>
</html>
A CSS + JavaScript exercise to add background color change and fade out animation on clicked div.
Comments
# by slash
document.querySelectorAll
# by Jeffrey
to slash, 3Q