籃球比分幾比幾——純js實現的數字輪盤轉動動效

籃球比分幾比幾——純js實現的數字輪盤轉動動效

原諒我此次標題黨了哈,這其實就是一個數字的翻牌器的動畫效果,只不過,咱們能夠本身徹底去用js來實現而不須要用到其餘東西。

先來看看效果:
javascript

(ps:1.爲了響應題目特地作了紅黑的籃球比分牌的樣子;2.gif是循環的哦~~)css

這種效果讓我作的話,我一開始是用會四列的<ul><li>來實現。轉動的列表嘛,最直觀的就是這樣了。
這種方法最麻煩就是要作兩三層的包裹,而後各類設置overflow,特別是有些時候還有噁心的滾動條出現,滾動條不只不美觀,還會讓咱們算出來的每一個元素寬度有偏差,其實不算是很好的方法。前端

可是這是過去的事情了。之前我沒得選,如今我想作個好人background-positionjava

Σ(っ°Д°;)っwhat?! background-position能寫這玩意?jquery

沒錯,就是能寫。若是想不到怎麼寫的朋友能夠先去回憶一下雪碧圖這個知識點。git

(ΩДΩ)誒,雪碧圖?這個嗎?github

不對,咱們這裏的雪碧圖是指Image sprites技術,也能夠稱之爲 CSS 貼圖定位、圖像精靈(sprite,意爲精靈),被運用於衆多使用大量小圖標的網頁應用之上。它可取圖像的一部分來使用,使得使用一個圖像文件替代多個小文件成爲可能。相較於一個小圖標一個圖像文件,單獨一張圖片所需的 HTTP 請求更少,對內存和帶寬更加友好。
詳情能夠查看MDN的介紹canvas

這裏的雪碧圖可以實現的緣由就是css上有background-position的屬性,在一張圖片上取不一樣的位置做爲背景,就能夠獲得不一樣背景。瀏覽器

o( ̄▽ ̄)d 好,這個我懂了,那它和咱們今天要講的動效有什麼關係呢?緩存

想一下~~若是background-position動起來了,會有什麼樣子呢?

⊙(・◇・)?會上下左右動...這有什麼特別的嗎...

沒錯,只能上下左右動。可是這樣動起來的話,不就能夠實現咱們今天的動效了嗎?

若是,設計獅大大給到這樣一張圖片:

再結合咱們剛討論的background-position,是否是就知道要怎麼作了?

(゜-゜)上下移動的話...

沒錯,咱們能夠經過一幀幀向上或者向下來改變background-position的值來達到數字輪盤的效果!

(`⌒´メ)哼!

嗯? 怎麼了?

(`⌒´メ)說好的純js實現呢?因此你還不是要有圖才能作?若是設計獅大大不給圖你還不是什麼都作不了!

你這還真的說到點上了,要圖的每作一款動效都要畫圖,這豈不是很麻煩?

別急,咱們但是前端er,圖這東西只要是不復雜,咱們還能本身畫嘛~~

接下來,讓咱們掏出canvas,畫畫兒~~:

const canvas = document.createElement('CANVAS'); // 首先咱們先建立一個canvas
    canvas.height = (30 + 5) * 10; // 而後設定canvas的長寬,這個跟咱們要設定的字體有關 
    canvas.width = 40; // 咱們假設須要作一個30px的字,距離上下左右的間距爲5px的圖
    const ctx = canvas.getContext('2d');
    ctx.font = '30px Impact'; // 設置字體大小和字體樣式
    ctx.fillStyle = 'red'; // 設置字體樣式
    for (let i = 0; i < 10; i++) { // 開始循環寫數字
        ctx.fillText(i, 5, 30 + (i * 35));
    }

這小段代碼就能夠給咱們畫出用於作動畫的簡單數字圖:

在這裏咱們設定了數字的大小爲30px,上下間距爲5px,因此相應的canvas的總高度爲:(30 + 5) 10;所以每個數字i的對應的高度就爲i 35(這個數字很重要,後面會說到);

在這裏圈個重點,你會發現咱們代碼中開始在canvas寫文字是從座標(5, 30)開始畫起的,你可能就要問了:爲何不是從(5,0)的位置畫?你從(5,30)開始畫不就頂上留有30px的空隙了嗎?
還真沒有空隙。由於 canvas的文字的座標是和通常的不一樣的。咱們通常canvas或者dom的元素,都是以左上角的點爲對齊座標的標準點,可是canvas的文字不是,它是以左下角的點爲標準點的;更確切的說,是以文字的baseline的最左端做爲標準點。不信?咱們在(5, 30)處畫個點和一條線就能夠看到:

因此這裏就要稍微注意一下。

作出來以後咱們徹底不須要加到dom中去,能夠直接從canvas上導出來:

const bgimg = canvas.toDataURL();

這樣咱們就有了一條能夠用的圖了~~~

(o゚▽゚)而後呢而後呢!

而後咱們就能夠繼續寫下去了——把背景圖設置到dom上,而後使用以前提到的rAF和Tween來編寫動畫:

首先是rAF模塊,此次使用了回調函數使得rAF能夠從函數中獨立出來,達到讓rAF和其餘動畫模塊的解耦的效果。

/**
* rAFPart
* @param duration 動畫的執行時間
* @param callback 回調函數,用來改變dom樣式,會得到progress當前時間進程和duration動畫執行做爲參數,方便調用TWEEN
*/
function rAFPart(duration, callback) {
        let startTime = 0;
        function rolllingStep(timestamp) {
            if (!startTime) {
                startTime = timestamp;
            }
            const progress = timestamp - startTime;
            callback(progress,duration);
            if (progress <= duration) {
                requestAnimationFrame(rolllingStep);
            }
        }
        requestAnimationFrame(rolllingStep);
    }

其次是TWEEN函數,用於計算區間插值:(使用了quadEaseOut的緩動函數,參數就再也不重複註釋啦)

function Tween(t, b, c, d) {
        if ((t /= d / 2) < 1) return c / 2 * t * t + b;
        return -c / 2 * ((--t) * (t - 2) - 1) + b;
    }

而後咱們就能夠來寫一下效果:

let cacheRed1 = 0; // 設置四個全局變量來緩存目前四個數字的background-position,這個是紅色的個位數
let cacheRed2 = 0;  // 紅色 十位
let cacheBlack1= 0; // 黑色 個位
let cacheBlack2 = 0; // 黑色 十位
function rolling() {
        const cr1 = cacheRed1++;
        const br1 = cacheBlack1++; // 緩存執行滾動前的background-position數據
        rAFPart(1000, (progress, duration) => { // 執行個位數的翻牌滾動
            const red1BgPos = Math.ceil(Tween(progress, cr1 * 35, 35, duration).toFixed(2));
            const blk1BgPos = Math.ceil(Tween(progress, br1 * 35, 35, duration).toFixed(2));
            BlackNumber[1].style.backgroundPosition = '0 -' + blk1BgPos + 'px';
            RedNumber[1].style.backgroundPosition = '0 -' + red1BgPos + 'px';
        });
        if (cacheBlack1 >= (cacheBlack2 + 1) * 10) { // 判斷黑色十位上的數是否須要翻牌
            const br2 = cacheBlack2++; // 緩存而後自增
            rAFPart(1000, (progress, duration) => { // 執行翻牌動做
                const pos = Math.ceil(Tween(progress, br2 * 35, 35, duration).toFixed(2));
                BlackNumber[0].style.backgroundPosition = '0 -' + pos + 'px';

            });
        }
        if (cacheRed1 >= (cacheRed2 + 1) * 10) { // 判斷黑色十位上的數是否須要翻牌
            const cr2 = cacheRed2++;
            rAFPart(1000, (progress, duration) => {
                const pos = Math.ceil(Tween(progress, cr2 * 35, 35, duration).toFixed(2));
                RedNumber[0].style.backgroundPosition = '0 -' + pos + 'px';

            });
        }
        setTimeout(() => { // 重複執行
            rolling();
        }, 1500)

    }

這裏面有兩個點要解釋一下,首先是咱們沒有詳細去記具體的background-position,而是用了0-9的數字來表示,是由於咱們上面畫數字圖的時候就已經知道一個數字所佔的高度是多少,因此每一個數字在哪一個位置咱們都知道的清清楚楚;並且若是咱們後面要跳轉到指定數字的時候,用0-9顯然更好設計程序來判斷。而後是要注意緩存,rAF模塊不是一次就執行完的,它每次執行回來調取回調函數的時候都要用到上一次自增以前的background-position來加入到TWEEN中計算,因此要先把值給緩存下來,否則會出現bug。

好,到此爲止,效果就作好啦!再次放一下效果圖:

最後加料: 在瀏覽器調試的時候發現了background-position的位置若是超過了圖片大小瀏覽器就會去自動repeat來延長,也就是我上面的長350的數字圖片,若是把background-position的高度設置爲350px,瀏覽器就會顯示repeat的圖片的0px的位置做爲背景,因此要作多圈輪轉動畫的朋友,知道該怎麼作了嗎?(斜眼笑)

最後的最後囉嗦一下: 這個效果用來動效是沒問題的,由於它不須要交互。可是須要交互狀況下,這個效果就估計不適用了哦~~

Homer 2019.04.21

參考

jQuery之家的一個付費效果

不,我沒有買這個效果的源碼,我只是按了一下F12,而後作了一下分析而已~~

在 CSS 中實現圖像合併

本文爲原創,未經容許,不得轉載~~

有錯漏之處,敬請指正~~

附上github博客上地址(上有可運行demo):https://github.com/homerious/animationShare/blob/master/main/homer/turnningNumber/README.md

感興趣的能夠上去看看哦~~

END