3

CSS 動畫練習 - 點擊元素改變背景色再逐漸淡化

 1 year ago
source link: https://blog.darkthread.net/blog/css-animation-lab-810/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

CSS 動畫練習 - 點擊元素改變背景色再逐漸淡化-黑暗執行緒

想在網頁加上動畫效果,資料表格中在某列資料變化時改變背景色吸引注意,再使用淡化效果恢復原本背景色。

以此目標我設計了一個綜合練習題,在網頁上放五個 DIV,點擊後使用 CSS 動畫效果將背景色改為紫色再逐漸淡化,1.5 秒後恢復原來顏色。若連續點擊同一 DIV,需停止現有動畫,重頭播放。另外,設計一按鈕,按下時會連續隨機點擊 DIV。

先看成果:

Fig1_638272789038039756.gif

網頁以原生 CSS + JavaScript 實現,使用到以下技巧:

  1. @keyframes 定義起始及結束狀態之 CSS 樣式
  2. animation: <keyframes-name> 1.5s ease-out; 定義 .hit 時播放背景色漸變動畫(由藍紫轉淺藍),長度 1.5 秒,採先快後慢(ease-out)配速
    延伸閱讀:CSS transition 各種速率 by 卡斯伯
  3. 在容器掛 click 事件但操控 e.target 元素,相當於 jQuery .on('click', '子元素選取器', function() {...})
  4. 點擊 DIV 時移除 .hit,稍後再加上 .hit 以停止目前動畫重新播放
  5. 稍後加上 .hit 使用 requestAnimationFrame() 實現,精準抓住下次繪製時觸發。傳統使用 setTimeout 要自己抓延遲,可能過長可能過短
  6. 動畫播放完畢靠 animationend 事件移除 .hit
  7. animationend 只需執行一次,在 jQuery 可用 .one(),香草 JavaScirpt 的話可寫成 addEventListener(fn, )
  8. ocument.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>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK