JavaScript中的canvas?實現(xiàn)一個圓環(huán)漸變倒計時效果
前言
內容:
- 效果圖
- 需求分析
- 實現(xiàn)技術
- 實現(xiàn)過程
- 全部源碼
1、效果圖展示
隨著時間的減少, 圓環(huán)的紅黃色部分會慢慢的減少,圓環(huán)中的數(shù)字會變小,一直到0停止.
2、需求分析
- 可以自定義倒計時結束的時間
- 圓環(huán)的顏色是漸變的
- 倒計時的動畫在視覺上是流暢運行, 而不是一格一格的減少
3、實現(xiàn)的技術
語言 | 框架 |
---|---|
HTML5 | 無 |
CSS3 | BootStrap SASS |
JavaScript | 無 |
4、實現(xiàn)的過程
1. HTML 部分
主要用到的是 HTML5 中的畫布元素 canvas, 一共4個 canvas 元素拍成一排, 每個元素都是高144px, 寬144px的正方形, 兩個圓環(huán)中的兩個小點部分可以使用偽元素 after 和 before 來實現(xiàn)
源碼如下:
<div class="count-down"> <div class="container"> <div class="row"> <div class="days col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="days" width="144" height="144"></canvas> </div> </div> <div class="hours col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="hours" width="144" height="144"></canvas> </div> </div> <div class="minutes col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="minutes" width="144" height="144"></canvas> </div> </div> <div class="seconds col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="seconds" width="144" height="144"></canvas> </div> </div> </div> </div> </div>
用 BootStrap 提供的柵格布局快速的把 canvas 元素排成一排 為每個 cnavas 元素設置對應的 id, 方便用 JavaScript 獲取到該元素.
2. SASS部分
css 的部分是用 Sass 來寫的, Sass 是一個 css 的 擴展語言, html 文件中引用的是寫好的 Sass 文件編譯生成 css 文件.
源碼如下:
.count-down { width: 100%; .row { padding: 0 145px; } .col-lg-3 { padding: 0; position: relative; .colck { width: 144px; height: 144px; background-color: #fff; margin: 0 auto; } } .days, .hours, .minutes { &:after, &:before { content: ""; display: block; width: 12px; height: 12px; background-color: #fff; position: absolute; right: -6px; } &:after { bottom: 45px; } &:before { top: 45px; } } }
控制圓環(huán)的運動主要靠 JavaScript 代碼, 這里 sass 的作用是用來把把元素的位置排列好, 把兩個圓環(huán)中間的點畫出來,
因為背景色是黑色的 為了看的更加直觀, 我先把包裹 canvas 的元素背景色設為白色,
此時的效果:
3. JavaScript部分
最終的運動效果靠 js 代碼來實現(xiàn):
- 我們先來獲取到這4個 canvas 元素
// 獲取4個 canvas 元素 var days_canvas = document.getElementById('days'); var hours_canvas = document.getElementById('hours'); var minutes_canvas = document.getElementById('minutes'); var seconds_canvas = document.getElementById('seconds');
- 為了可以自定義指定倒計時結束時間, 寫一個設置結束時間的函數(shù), 返回值為一個 Date 對象.
// 設置倒計時時間:年 月 日 小時 分鐘 秒 毫秒 endTime = setEndTime(2020, 11, 30, 15, 0, 0); // 設置到期時間 function setEndTime(year, month, day, hour, minute, millisecond) { return new Date(year, month - 1, day, hour, minute, millisecond); }
js 中創(chuàng)建 Date 對象的5種方式 new Date("month dd,yyyy hh:mm:ss"); new Date("month dd,yyyy"); new Date(yyyy,mth,dd,hh,mm,ss); new Date(yyyy,mth,dd); new Date(ms);
注意: Date 對象中的月份取值是 0 - 11, 0 就表示 1 月, 用變量 endTime 保存這個設置好的 Date 對象
- 根據(jù)現(xiàn)在的時間和設置好的到期時間計算現(xiàn)在到到期時間還有多少秒,多少分鐘, 多少小時, 多少天.
// 計算距離到期時間還剩下多少 days, hours, minutes function getLeftTimeObj() { var date = new Date(); var millisecond = date.getTime() var end_millisecond = endTime.getTime(); if (end_millisecond < millisecond) { return { days: 0, hours: 0, minutes: 0, seconds: 0 } } // 距離結束時間的秒數(shù) var left_seconds = (( end_millisecond - millisecond ) / 1000); var seconds = (left_seconds % 60); var left_minutes = (left_seconds - seconds) / 60; var minutes = (left_minutes % 60); var left_hours = (left_minutes - minutes) / 60; var hours = (left_hours % 24); var left_days = ((left_hours - hours) / 24); return { days: left_days, hours: hours, minutes: minutes, seconds: seconds } } var dateObj = getLeftTimeObj();
思路:
- Date 對象中有一個方法 getTime(), 返回值是從 1970 年 1 月 1 日至今的毫秒數(shù)。
- 用兩個 Date 對象的 getTime() 返回值相減值再 ÷ 1000 獲得兩者相差的秒數(shù)
- 用兩者相差的秒數(shù)換算出兩者相差多少天,多少小時,多少分鐘,多少秒
- 最后返回一個對象 { days: 還有多少天, hours: 還有多少個小時, minutes: 還有多少分鐘, secondsL: 還有多少秒 }
我們定義一個變量 dateObj 保存這個對象, 然后根據(jù)這個對象去在相應的 canvas 元素上畫圖
- 繪圖
我們先不考慮怎么讓圓環(huán)動起來, 而是先考慮怎么把圖畫出來
觀察單個圖形的樣子如下圖所示:
可以把這個圖形分成4個部分:
- 灰色的整個圓環(huán)
- 紅黃漸變的不完整圓環(huán)
- 中間的數(shù)字 54
- 下面的字符串 Seconds
我們可以為寫四個方法分別完成每一個步驟:
// 畫整個圓 function drawCricle(canvas) { // 獲取 context 對象 var context = canvas.getContext('2d'); // 獲取 canvas 的中心點的 x 坐標 var centerX = canvas.width / 2; // 獲取 canvas 的中心點的 y 坐標 var centerY = canvas.height / 2; context.save(); context.beginPath(); context.lineWidth = 7; context.strokeStyle = "#636363"; // 圓環(huán)的寬度為 7, canvas 元素的長和寬是 144, 所以圓半徑應為 (144 - 7) / 2, 也就是 canvas.width / 2 - 3.5 context.arc(centerX, centerY, canvas.width / 2 - 3.5 , 0, Math.PI*2, false); context.stroke(); context.closePath(); context.restore(); } // 畫數(shù)字下的字符 function drawStr(canvas, str) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; context.save(); context.fillStyle = '#fff'; context.font = "14px Arial"; //設置字體大小和字體 context.textAlign = 'center'; context.fillText(str, centerX, centerY+54); context.restore(); } // 畫數(shù)字 function drawNumber(canvas, num) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; num = num.toFixed(); num = num < 10 ? '0' + num : num; context.save(); context.fillStyle = '#fff'; context.font = "bolder 60px Arial"; //設置字體大小和字體 context.textAlign = 'center'; // context.fontWeight = 'bold'; context.fillText(num, centerX, centerY+10); context.restore(); } // 畫進度條 -0.5為起點 // percentage 為進度條的百分比(0<percentage<1), 1 表示整個圓 function drawProgress(canvas, percentage) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; var end_cricle = (percentage * 2) - 0.5; end_cricle = Number(end_cricle.toFixed(4)) // 設置垂直漸變 var linearGrad = context.createLinearGradient(canvas.width / 2, 0, canvas.width / 2, canvas.height); linearGrad.addColorStop(0, '#ff0503'); linearGrad.addColorStop(1, '#ff8200'); context.save(); context.strokeStyle = linearGrad; //設置描邊樣式 context.lineWidth = 7; //設置線寬 context.beginPath(); //路徑開始 context.arc(centerX, centerY, canvas.width / 2 -3.5 , -0.5 * Math.PI, end_cricle * Math.PI, false); //用于繪制圓弧context.arc(x坐標,y坐標,半徑,起始角度,終止角度,順時針/逆時針) context.stroke(); //繪制 context.closePath(); //路徑結束 context.restore(); }
// 為秒計數(shù)的 canvas 繪圖, 畫圖依賴 dateObj.seconds drawCricle(seconds_canvas); drawStr(seconds_canvas, 'Seconds'); // 每一分鐘有60秒, 用 dateObj.seconds / 60 獲得 當前進度 drawProgress(seconds_canvas, (dateObj.seconds / 60).toFixed(4)); drawNumber(seconds_canvas, dateObj.seconds);
寫一個繪制 4 個 canvas 的方法 draw(); 然后執(zhí)行
// 繪圖 function draw() { drawCricle(days_canvas); drawStr(days_canvas, 'Days'); drawProgress(days_canvas, dateObj.days / 10); drawNumber(days_canvas, dateObj.days); drawCricle(hours_canvas); drawStr(hours_canvas, 'Hours'); drawProgress(hours_canvas, (dateObj.hours / 24).toFixed(4)); drawNumber(hours_canvas, dateObj.hours); drawCricle(minutes_canvas); drawStr(minutes_canvas, 'Minutes'); drawProgress(minutes_canvas, (dateObj.minutes / 60).toFixed(4)); drawNumber(minutes_canvas, dateObj.minutes); drawCricle(seconds_canvas); drawStr(seconds_canvas, 'Seconds'); drawProgress(seconds_canvas, (dateObj.seconds / 60).toFixed(4)); drawNumber(seconds_canvas, dateObj.seconds); } drwa();
然后先記得把之前設置的白色背景色去掉
執(zhí)行了 draw() 之后效果如下圖:
但是現(xiàn)在還沒有實現(xiàn)讓他動起來,
- 讓倒計時動起來
先明白動起來的邏輯:
- 用定時器每過 16ms 清除一次所有 canvas 畫的圖像
- 重新獲取所剩下的時間的對象 dateObj
- 再次繪圖 draw();
// 清除 canvas 內容 function clear() { var days_ctx = days_canvas.getContext('2d'); var hours_ctx = hours_canvas.getContext('2d'); var minutes_ctx = minutes_canvas.getContext('2d'); var seconds_ctx = seconds_canvas.getContext('2d'); days_ctx.clearRect(0, 0, days_canvas.width, days_canvas.height); hours_ctx.clearRect(0, 0, hours_canvas.width, hours_canvas.height); minutes_ctx.clearRect(0, 0, minutes_canvas.width, minutes_canvas.height); seconds_ctx.clearRect(0, 0, seconds_canvas.width, seconds_canvas.height); } // 讓倒計時動起來 // 可以直接用定時器 setInterval(function() { clear(); dateObj = getLeftTimeObj(); draw(); }, 16) // 也可以考慮使用 requestAnimationFrame 這個方法 (function count() { requestAnimationFrame(count); clear(); dateObj = getLeftTimeObj(); draw(); })();
到現(xiàn)在為止整個完整的功能全部實現(xiàn)了。
5、全部源碼
1.index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="./bootstrap.min.css" rel="external nofollow" > <link rel="stylesheet" href="./style.css" rel="external nofollow" > <style> .count-down { background: #000; } </style> </head> <body> <div class="count-down"> <div class="container"> <div class="row"> <div class="days col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="days" width="144" height="144"></canvas> </div> </div> <div class="hours col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="hours" width="144" height="144"></canvas> </div> </div> <div class="minutes col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="minutes" width="144" height="144"></canvas> </div> </div> <div class="seconds col-lg-3 col-md-3 col-sm-3 col-xs-6"> <div class="colck"> <canvas id="seconds" width="144" height="144"></canvas> </div> </div> </div> </div> </div> <script src="./index.js"></script> </body> </html>
2. style.scss
.count-down { position: absolute; bottom: 90px; left: 0; width: 100%; .row { padding: 0 145px; } .col-lg-3 { padding: 0; position: relative; .colck { width: 144px; height: 144px; // background-color: #fff; margin: 0 auto; } } .days, .hours, .minutes { &:after, &:before { content: ""; display: block; width: 12px; height: 12px; background-color: #fff; position: absolute; right: -6px; } &:after { bottom: 45px; } &:before { top: 45px; } } }
3. index.js
// 倒計時 // 到期時間 // 設置倒計時時間:年 月 日 小時 分鐘 秒 毫秒 endTime = setEndTime(2020, 11, 30, 15, 0, 0); // 設置到期時間 function setEndTime(year, month, day, hour, minute, millisecond) { return new Date(year, month - 1, day, hour, minute, millisecond); } // 獲取4個 canvas 元素 var days_canvas = document.getElementById('days'); var hours_canvas = document.getElementById('hours'); var minutes_canvas = document.getElementById('minutes'); var seconds_canvas = document.getElementById('seconds'); // 計算距離到期時間還剩下多少 days, hours, minutes function getLeftTimeObj() { var date = new Date(); var millisecond = date.getTime() var end_millisecond = endTime.getTime(); if (end_millisecond < millisecond) { return { days: 0, hours: 0, minutes: 0, seconds: 0 } } // 距離結束時間的秒數(shù) var left_seconds = (( end_millisecond - millisecond ) / 1000); var seconds = (left_seconds % 60); var left_minutes = (left_seconds - seconds) / 60; var minutes = (left_minutes % 60); var left_hours = (left_minutes - minutes) / 60; var hours = (left_hours % 24); var left_days = ((left_hours - hours) / 24); return { days: left_days, hours: hours, minutes: minutes, seconds: seconds } } var dateObj = getLeftTimeObj(); // 畫整個圓 function drawCricle(canvas) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; context.save(); context.beginPath(); context.lineWidth = 7; context.strokeStyle = "#636363"; context.arc(centerX, centerY, canvas.width / 2 - 3.5 , 0, Math.PI*2, false); context.stroke(); context.closePath(); context.restore(); } // 畫數(shù)字下的字符 function drawStr(canvas, str) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; context.save(); context.fillStyle = '#fff'; context.font = "14px Arial"; //設置字體大小和字體 context.textAlign = 'center'; context.fillText(str, centerX, centerY+54); context.restore(); } // 畫數(shù)字 function drawNumber(canvas, num) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; num = num.toFixed(); num = num < 10 ? '0' + num : num; context.save(); context.fillStyle = '#fff'; context.font = "bolder 60px Arial"; //設置字體大小和字體 context.textAlign = 'center'; // context.fontWeight = 'bold'; context.fillText(num, centerX, centerY+10); context.restore(); } // 畫進度條 -0.5為起點 // percentage 為進度條的百分比(0<percentage<1), 1 表示整個圓 function drawProgress(canvas, percentage) { var context = canvas.getContext('2d'); var centerX = canvas.width / 2; var centerY = canvas.height / 2; var end_cricle = (percentage * 2) - 0.5; end_cricle = Number(end_cricle.toFixed(4)) // 設置垂直漸變 var linearGrad = context.createLinearGradient(canvas.width / 2, 0, canvas.width / 2, canvas.height); linearGrad.addColorStop(0, '#ff0503'); linearGrad.addColorStop(1, '#ff8200'); context.save(); context.strokeStyle = linearGrad; //設置描邊樣式 context.lineWidth = 7; //設置線寬 context.beginPath(); //路徑開始 context.arc(centerX, centerY, canvas.width / 2 -3.5 , -0.5 * Math.PI, end_cricle * Math.PI, false); //用于繪制圓弧context.arc(x坐標,y坐標,半徑,起始角度,終止角度,順時針/逆時針) context.stroke(); //繪制 context.closePath(); //路徑結束 context.restore(); } // 清除 canvas 內容 function clear() { var days_ctx = days_canvas.getContext('2d'); var hours_ctx = hours_canvas.getContext('2d'); var minutes_ctx = minutes_canvas.getContext('2d'); var seconds_ctx = seconds_canvas.getContext('2d'); days_ctx.clearRect(0, 0, days_canvas.width, days_canvas.height); hours_ctx.clearRect(0, 0, hours_canvas.width, hours_canvas.height); minutes_ctx.clearRect(0, 0, minutes_canvas.width, minutes_canvas.height); seconds_ctx.clearRect(0, 0, seconds_canvas.width, seconds_canvas.height); } // 繪圖 function draw() { drawCricle(days_canvas); drawCricle(hours_canvas); drawCricle(minutes_canvas); drawCricle(seconds_canvas); drawStr(days_canvas, 'Days'); drawStr(hours_canvas, 'Hours'); drawStr(minutes_canvas, 'Minutes'); drawStr(seconds_canvas, 'Seconds'); drawProgress(days_canvas, dateObj.days / 10 >= 1 ? 1 : dateObj.days / 10); drawNumber(days_canvas, dateObj.days); drawProgress(hours_canvas, (dateObj.hours / 24).toFixed(4)); drawNumber(hours_canvas, dateObj.hours); drawProgress(minutes_canvas, (dateObj.minutes / 60).toFixed(4)); drawNumber(minutes_canvas, dateObj.minutes); drawProgress(seconds_canvas, (dateObj.seconds / 60).toFixed(4)); drawNumber(seconds_canvas, dateObj.seconds); } draw(); (function count() { requestAnimationFrame(count); clear(); dateObj = getLeftTimeObj(); draw(); })();
到此這篇關于JavaScript中的canvas 實現(xiàn)一個圓環(huán)漸變倒計時效果的文章就介紹到這了,更多相關JavaScript canvas 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Autocomplete Textbox Example javascript實現(xiàn)自動完成成功
Autocomplete Textbox Example javascript實現(xiàn)自動完成成功...2007-08-08關于前后端json數(shù)據(jù)的發(fā)送與接收詳解
這篇文章主要給大家介紹了關于前后端json數(shù)據(jù)發(fā)送與接收的相關資料,文中通過示例代碼詳細介紹了關于flask中的json數(shù)據(jù)接收和前端發(fā)送json數(shù)據(jù)等內容,需要的朋友可以參考借鑒,下面來一起看看吧。2017-07-07基于JavaScript實現(xiàn)網(wǎng)頁倒計時自動跳轉代碼
這篇文章主要介紹了基于JavaScript實現(xiàn)網(wǎng)頁倒計時自動跳轉代碼 的相關資料,需要的朋友可以參考下2015-12-12js print打印網(wǎng)頁指定區(qū)域內容的簡單實例
下面小編就為大家?guī)硪黄猨s print打印網(wǎng)頁指定區(qū)域內容的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11