絢麗的倒計時效果(Canvas)

1. 案例介紹

最近在慕課網(wǎng)上看了一門有關(guān)Canvas的課程案例,炫麗的倒計時效果Canvas繪圖與動畫基礎(chǔ),學(xué)習(xí)完這門課程之后,對Canvas繪圖有了基本的了解。

效果展示.gif

2. 案例理解

2.1 如何表示各個數(shù)字

引入的 digit.js文件
每一個數(shù)字是由10行7列的0,1點陣表示,冒號是由10行4列的0,1點陣表示,1的位置表示小球。

digit =
    [
        [
            [0,0,1,1,1,0,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,0,1,1,0],
            [0,0,1,1,1,0,0]
        ],//0
        [
            [0,0,0,1,1,0,0],
            [0,1,1,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [1,1,1,1,1,1,1]
        ],//1
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,1,1],
            [1,1,1,1,1,1,1]
        ],//2
        ......//從0-9數(shù)字的矩陣排列
      [
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0]
        ]//第10個元素表示冒號
]

2.2 倒計時的表示

getCurrentShowTimeSeconds函數(shù) 獲得截止時間到當(dāng)前時間的秒數(shù).

function getCurrentShowTimeSeconds() {
    var curTime = new Date();//當(dāng)前的時間
    var ret = endTime.getTime() - curTime.getTime();
    //截止時間的毫秒數(shù)-當(dāng)前時間的毫秒數(shù)

    ret = Math.round(ret / 1000);//毫秒轉(zhuǎn)化成秒(四舍五入)
    return ret >= 0 ? ret : 0;
}

2.3 繪制數(shù)字(狀態(tài))

render函數(shù) 繪制數(shù)字的狀態(tài)

數(shù)字.png

數(shù)字由10行7列的方格組成,小球半徑為 R, 方格半徑為 R+1

function render(cxt) {
   cxt.clearRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);//刷新掉原來的圖像,否則新圖像會疊加在上面

   var hours = parseInt(curShowTimeSeconds / 3600);
   var minutes = parseInt((curShowTimeSeconds - hours * 3600) / 60);
   var seconds = parseInt(curShowTimeSeconds % 60);

   //繪制數(shù)字
   renderDigit(MARGIN_LEFT, MARGIN_TOP, parseInt(hours / 10), cxt);//繪制小時的十位數(shù)
   renderDigit(MARGIN_LEFT + 15 * (RADIUS + 1), MARGIN_TOP, parseInt(hours % 10), cxt);//(//繪制小時的個位數(shù))本來是14個(R+1),為了有點空隙,取15個
   renderDigit(MARGIN_LEFT + 30 * (RADIUS + 1), MARGIN_TOP, 10, cxt);//冒號只有4列, 8個(R+1),多取一個9個(R+1)
   renderDigit(MARGIN_LEFT + 39 * (RADIUS + 1), MARGIN_TOP, parseInt(minutes / 10), cxt);//繪制分鐘的十位數(shù)
   renderDigit(MARGIN_LEFT + 54 * (RADIUS + 1), MARGIN_TOP, parseInt(minutes % 10), cxt);//繪制分鐘的個位數(shù)
   renderDigit(MARGIN_LEFT + 69 * (RADIUS + 1), MARGIN_TOP, 10, cxt);//繪制冒號
   renderDigit(MARGIN_LEFT + 78 * (RADIUS + 1), MARGIN_TOP, parseInt(seconds / 10), cxt);;//繪制秒數(shù)的十位數(shù)
   renderDigit(MARGIN_LEFT + 93 * (RADIUS + 1), MARGIN_TOP, parseInt(seconds % 10), cxt);;//繪制秒數(shù)的個位數(shù)
}

2.4 繪制數(shù)字(canvas繪制)

renderDigit函數(shù) 是完成數(shù)字的canvas繪制,遍歷digit中指定數(shù)字的行列中每一個元素,如果該元素為1,繪制一個小球.

繪制弧,曲線,圓的方法

context.arc(圓的x坐標(biāo),圓的y坐標(biāo),圓的半徑,起始角,結(jié)束角,逆時針或順時針繪圖。
false = 順時針,true = 逆時針,若不填寫則默認(rèn)逆時針)
圖片.png

繪制的小球圓心計算公式

x坐標(biāo):x + j * 2 * (RADIUS + 1) + (RADIUS + 1);j表示列數(shù)
y坐標(biāo):y + i * 2 * (RADIUS + 1) + (RADIUS + 1);i表示行數(shù)
function renderDigit(x, y, num, cxt) {//數(shù)字最左端橫坐標(biāo),數(shù)字最左端縱坐標(biāo),繪制的數(shù)字,繪制的上下文環(huán)境
    cxt.fillStyle = "rgb(0,102,153)";//設(shè)置小球的填充顏色
    for (var i = 0; i < digit[num].length; i++) {
        for (var j = 0; j < digit[num][i].length; j++) {
            if (digit[num][i][j] == 1) {
                cxt.beginPath();
                cxt.arc(x + j * 2 * (RADIUS + 1) + (RADIUS + 1), y + i * 2 * (RADIUS + 1) + (RADIUS + 1), RADIUS, 0, 2 * Math.PI);
                cxt.closePath();
                cxt.fill();
            }
        }
    }
}

2.5 每秒更新數(shù)字并增加彩色小球

update函數(shù) 更新時間并增加小球

function update() {
    var nextShowTimeSeconds = getCurrentShowTimeSeconds();
    var nextHours = parseInt(nextShowTimeSeconds / 3600);
    var nextMinutes = parseInt((nextShowTimeSeconds - nextHours * 3600) / 60);
    var nextSeconds = parseInt(nextShowTimeSeconds % 60);

    var curHours = parseInt(curShowTimeSeconds / 3600);
    var curMinutes = parseInt((curShowTimeSeconds - curHours * 3600) / 60);
    var curSeconds = parseInt(curShowTimeSeconds % 60);

    if (nextSeconds != curSeconds) {//時間變化時,增加彩色小球
        if (parseInt(curHours / 10) != parseInt(nextHours / 10)) {//如果小時的的十位數(shù)不同,增加小球
            addBalls(MARGIN_LEFT, MARGIN_TOP, parseInt(curHours / 10));
        }
        if (parseInt(curHours % 10) != parseInt(nextHours % 10)) {//如果小時的的個位數(shù)不同,增加小球
            addBalls(MARGIN_LEFT + 15 * (RADIUS + 1), MARGIN_TOP, parseInt(curHours % 10));
        }
        if (parseInt(curMinutes / 10) != parseInt(nextMinutes / 10)) {//如果分鐘的的十位數(shù)不同,增加小球
            addBalls(MARGIN_LEFT + 39 * (RADIUS + 1), MARGIN_TOP, parseInt(curMinutes / 10));
        }
        if (parseInt(curMinutes % 10) != parseInt(nextMinutes % 10)) {//如果分鐘的的個位數(shù)不同,增加小球
            addBalls(MARGIN_LEFT + 54 * (RADIUS + 1), MARGIN_TOP, parseInt(curMinutes % 10));
        }
        if (parseInt(curSeconds / 10) != parseInt(nextSeconds / 10)) {//如果秒數(shù)的的十位數(shù)不同,增加小球
            addBalls(MARGIN_LEFT + 78 * (RADIUS + 1), MARGIN_TOP, parseInt(curSeconds / 10));
        }
        if (parseInt(curSeconds % 10) != parseInt(nextSeconds / 10)) {//如果秒數(shù)的的個位數(shù)不同,增加小球
            addBalls(MARGIN_LEFT + 93 * (RADIUS + 1), MARGIN_TOP, parseInt(nextSeconds % 10));
        }
        curShowTimeSeconds = nextShowTimeSeconds;
    }
    updateBalls();
}

addBalls函數(shù) 增加彩色小球,定義每個彩色小球的屬性,并且把生成的彩色小球放在balls數(shù)組中.

  • x (圓心x坐標(biāo))
  • y (圓心縱坐標(biāo))
  • g (重力加速度)
  • vx (水平方向的速度)
  • vy (垂直方向的速度)
  • color (小球的顏色)
function addBalls(x, y, num) {
    for (var i = 0; i < digit[num].length; i++) {
        for (var j = 0; j < digit[num][i].length; j++) {
            if (digit[num][i][j] == 1) {
                var aBall = {
                    x: x + j * 2 * (RADIUS + 1) + (RADIUS + 1),
                    y: y + i * 2 * (RADIUS + 1) + (RADIUS + 1),
                    g: 1.5 + Math.random(),
                    vx: Math.pow(-1, Math.ceil(Math.random() * 1000)) * 4,//[-4,4]
                    vy: -5,
                    color: colors[Math.floor(Math.random() * colors.length)]
                };
                balls.push(aBall);
            }
        }
    }

}

2.6 更新小球

updateBalls函數(shù) 更新小球的位置,碰撞檢測以及控制小球數(shù)量


function updateBalls() {
    for(var i = 0; i< balls.length;i++) {
        balls[i].x += balls[i].vx;
        balls[i].y += balls[i].vy;
        balls[i].vy += balls[i].g;
        //碰撞檢測
        if(balls[i].y >= WINDOW_HEIGHT-RADIUS) {//小球碰到畫布底部
            balls[i].y = WINDOW_HEIGHT-RADIUS;
            balls[i].vy = -balls[i].vy*0.75;//0.75摩擦系數(shù),使得小球的彈跳更符合實際
        }
    }
    //控制小球數(shù)量
    var cnt = 0;
    for(var i = 0; i< balls.length;i++) {
        if(balls[i].x+RADIUS >0 && balls[i].x-RADIUS < WINDOW_WIDTH) {//留在畫布中的小球
            balls[cnt++] = balls[i];//cnt <= i,前cnt個小球符合要求,cnt后面的小球可以刪掉
        }
    }
    while(balls.length > cnt) {
        balls.pop();//刪除病返回數(shù)組最后一個元素
    }
}

完整代碼請戳Canvas倒計時效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內(nèi)容