用HTML5制作數(shù)字時(shí)鐘的教程

就是這個(gè)數(shù)字時(shí)鐘,當(dāng)時(shí)覺得這個(gè)創(chuàng)意不錯(cuò),但是也沒去折騰。直到昨天同事又在網(wǎng)上看到這個(gè)案例,他覺得很酷炫,就跑過來問我,這個(gè)是怎么實(shí)現(xiàn)的,然后我大概想了一下實(shí)現(xiàn)方法后也來了點(diǎn)興趣,就花了一點(diǎn)時(shí)間模仿做出來了一個(gè)。不同的是,岑安用的是div來做的。而我就是用canvas來實(shí)現(xiàn)的。用canvas來做性能方面會(huì)更好,因?yàn)榫蛦螁尾倏孛總€(gè)點(diǎn)的運(yùn)動(dòng),用js控制dom的style屬性跟用js控制canvas繪圖相比性能方面肯定是有所欠缺的。
先上個(gè)我做的DEMO吧,然后再簡述一下做這個(gè)的方法: 看DEMO請(qǐng)戳我 。
做這個(gè)思路很簡單,就是通過字符串保存各個(gè)數(shù)字的位置:
復(fù)制代碼
- var numData = [
- "1111/1001/1001/1001/1001/1001/1111", //0
- "0001/0001/0001/0001/0001/0001/0001", //1
- "1111/0001/0001/1111/1000/1000/1111", //2
- "1111/0001/0001/1111/0001/0001/1111", //3
- "1010/1010/1010/1111/0010/0010/0010", //4
- "1111/1000/1000/1111/0001/0001/1111", //5
- "1111/1000/1000/1111/1001/1001/1111", //6
- "1111/0001/0001/0001/0001/0001/0001", //7
- "1111/1001/1001/1111/1001/1001/1111", //8
- "1111/1001/1001/1111/0001/0001/1111", //9
- "0000/0000/0010/0000/0010/0000/0000", //:
- ]
0代表沒像素,1代表有像素,/是為了更好看些,就是分行嘛,簡單說起來:比如0就是:
- 1 1 1 1
- 1 0 0 1
- 1 0 0 1
- 1 0 0 1
- 1 0 0 1
- 1 0 0 1
- 1 1 1 1
這樣就很清楚了吧。從0到9還有一個(gè):號(hào)都用字符串表示好。
然后就寫個(gè)粒子對(duì)象,也就是像素點(diǎn):
- var P_radius = 8,Gravity = 9.8;
- var Particle = function(){
- this.x = 0;
- this.y = 0;
- this.vx = 0;
- this.vy = 0;
- this.color = "";
- this.visible = false;
- this.drop = false;
- }
- Particle.prototype = {
- constructors:Particle,
- paint:function(){ //繪制自身
- ctx.fillStyle = this.color;
- ctx.beginPath();
- ctx.arc(this.x,this.y,P_radius,0,2*Math.PI);
- ctx.fill();
- },
- reset:function(x,y,color){ //重置
- this.x = x;
- this.y = y;
- this.vx = 0;
- this.vy = 0;
- this.color = color;
- this.visible = true;
- this.drop = false;
- },
- isDrop:function(){ //落下
- this.drop = true;
- var vx = Math.random()*20+15
- this.vx = Math.random()>=0.5?-vx : vx;
- },
- update:function(time){ //每一幀的動(dòng)作
- if(this.drop){
- this.x += this.vx*time;
- this.y += this.vy*time;
- var vy = this.vy+Gravity*time;
- if(this.y>=canvas.height-P_radius){
- this.y = canvas.height-P_radius
- vy = -vy*0.7;
- }
- this.vy = vy;
- if(this.x<-P_radius||this.x>canvas.width+P_radius||this.y<-P_radius||this.y>canvas.height+P_radius){
- this.visible = false;
- }
- }
- }
- }
粒子對(duì)象的屬性比較簡單,就位置,速度,以及是否可視化。方法的話,paint是繪制方法,reset是重置(因?yàn)榱W右h(huán)利用的,提升性能),isDrop是粒子落下方法,update就是每一幀更新粒子的動(dòng)作,update中當(dāng)粒子運(yùn)動(dòng)超出canvas的繪制區(qū)域時(shí),就把它的可視化置為false,在粒子容器中保存起來等待下一次調(diào)用。
寫好粒子對(duì)象后,就要考慮如何讓粒子按照位置畫上去,同時(shí)當(dāng)粒子不需要用時(shí)能夠讓他做自由落體的動(dòng)畫了。
先畫背景(也就是那沒有像素的白點(diǎn)):
- function drawBg(){
- var tx = (canvas.width-((P_radius*2+X_J)*4*8+7*xjg))/2;
- for(var i=0;i<8;i++){
- var ty = (canvas.height-((P_radius+yjg)*6))/2;
- for(var j=0;j<numData[0].length;j++){
- var tt = numData[0].charAt(j);
- if(tt==="/"){
- ty+=yjg;
- }else {
- var x = tx+j%5*(P_radius*2+X_J),
- y = ty;
- bgctx.fillStyle = "#FFF";
- bgctx.beginPath();
- bgctx.arc(x,y,P_radius,0,2*Math.PI);
- bgctx.fill();
- }
- }
- tx+=xjg+4*(P_radius*2+X_J);
- }
- }
先把背景畫到一個(gè)離屏canvas中緩存起來,接下來每一幀重畫的時(shí)候就不需要邏輯計(jì)算了,直接把那個(gè)離屏canvas畫上去就行了。上面的邏輯應(yīng)該不難理解,就是通過兩個(gè)循環(huán),循環(huán)8個(gè)數(shù)字,然后再對(duì)每個(gè)數(shù)字一個(gè)點(diǎn)一個(gè)點(diǎn)進(jìn)行繪制,當(dāng)遇到“/”時(shí),就說明要換行了,把繪制的ty加個(gè)換行間隔,再把tx重置,再進(jìn)行繪制。就這樣,點(diǎn)就可以都畫出來了。效果圖如下:
背景畫好了,就開始根據(jù)每一秒的時(shí)間,畫數(shù)字像素吧。方法主要是這個(gè):
- function setTime(time){
- var h = time.getHours()+"",
- m = time.getMinutes()+"",
- s = time.getSeconds()+"";
- hh = h.length===1?"0"+h:h;
- mm = m.length===1?"0"+m:m;
- ss = s.length===1?"0"+s:s;
- var nowdate = h+":"+m+":"+s;
- var tx = (canvas.width-((P_radius*2+X_J)*4*8+7*xjg))/2,color = "";
- for(var i=0;i<nowdate.length;i++){
- var n = nowdate.charAt(i)===":"?10:parseInt(nowdate.charAt(i)),
- text = numData[n];
- var ty = (canvas.height-((P_radius+yjg)*6))/2;
- switch(i){
- case 0:color = "#4DCB74";break;
- case 2:color = "#4062E0";break;
- case 3:color = "#D65050";break;
- case 5:color = "#4062E0";break;
- case 6:color = "#797C17";break;
- }
- for(var j=0;j<text.length;j++){
- var tt = text.charAt(j);
- if(tt==="/"){
- ty+=yjg;
- }else{
- var x = tx+j%5*(P_radius*2+X_J),
- y = ty,
- pp = null,
- usefullp = null;
- particles.forEach(function(p){
- if(p.visible&p.x===x&p.y===y){
- ppp = p;
- }else if(!p.visible&usefullp===null){
- usefullp = p;
- }
- });
- if(pp!==null&tt==="0"){
- pp.isDrop();
- }else if(pp===null&tt==="1"){
- usefullp.reset(x , y , color);
- }
- }
- }
- tx+=xjg+4*(P_radius*2+X_J);
- }
- }
原理也不難,也是跟上面畫背景差不多,遍歷所有點(diǎn),然后根據(jù)當(dāng)前時(shí)間的數(shù)字轉(zhuǎn)換成的字符串來判斷,當(dāng)前點(diǎn)是否應(yīng)該有像素,如果有像素就再判斷當(dāng)前這個(gè)點(diǎn)是否已經(jīng)有粒子對(duì)象在了,如果已經(jīng)有粒子對(duì)象在了,就直接跳出不處理,如果沒有粒子對(duì)象在,就再粒子容器中找一個(gè)沒有被使用的粒子reset到這個(gè)位置。還有一種情況,就是當(dāng)前這個(gè)點(diǎn)是不應(yīng)該有像素的,但是卻有粒子,那就獲取這個(gè)粒子,讓這個(gè)粒子進(jìn)行自由落體。
時(shí)間設(shè)置也寫好了,就可以寫舞臺(tái)更新的代碼了:
- var timeCount_0 = 0,timeCount_1 = 0,particles = [];
- function initAnimate(){
- for(var i=0;i<200;i++){
- var p = new Particle();
- particles.push(p);
- }
- timeCount_0 = new Date();
- timeCount_1 = new Date();
- drawBg();
- setTime(timeCount_0)
- animate();
- }
- function animate(){
- ctx.clearRect(0,0,canvas.width,canvas.height);
- ctx.drawImage(bgcanvas,0,0);
- var timeCount_2 = new Date();
- if(timeCount_1-timeCount_0>=1000){
- setTime(timeCount_1);
- timeCount_0 = timeCount_1;
- }
- particles.forEach(function(p){
- if(p.visible){
- p.update((timeCount_2-timeCount_1)/70);
- p.paint();
- }
- });
- timeCount_1 = timeCount_2;
- RAF(animate)
- }
在initAnimate進(jìn)行動(dòng)畫初始化,初始化也就是先實(shí)例化兩百個(gè)粒子對(duì)象放到粒子容器中保存起來,再更新時(shí)間戳,緩存背景,設(shè)置當(dāng)前時(shí)間,然后調(diào)用animate動(dòng)畫循環(huán)主體開始動(dòng)畫。
animate中的邏輯也很簡單了,獲取時(shí)間戳,如果兩個(gè)時(shí)間戳之間的時(shí)間差大于或等于1秒,就進(jìn)行setTime。而再下面的就是對(duì)粒子容器里的所有可視化的粒子進(jìn)行遍歷循環(huán)重繪了。
然后就做好啦:
個(gè)效果還是有很多可以優(yōu)化的地方的,因?yàn)闀r(shí)鐘和分鐘都是動(dòng)的比較少的,所以可以把這兩個(gè)緩存起來,當(dāng)沒有動(dòng)作的時(shí)候就直接將緩存數(shù)據(jù)畫上去就行了,這樣就可以減少舞臺(tái)每一幀的繪圖API調(diào)用量,肯定是能提高性能的。不過現(xiàn)在畢竟粒子不多,兩三百個(gè)粒子對(duì)象也就夠用了,如果不去做優(yōu)化,動(dòng)畫也還是能很流暢的運(yùn)行的。所以樓主就偷個(gè)小懶啦。
源碼地址:https://github.com/whxaxes/canvas-test/blob/gh-pages/src/Funny-demo/coolClock/index.html
相關(guān)文章
Html5 canvas實(shí)現(xiàn)粒子時(shí)鐘的示例代碼
這篇文章主要介紹了Html5 canvas實(shí)現(xiàn)粒子時(shí)鐘的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-06HTML寫一個(gè)網(wǎng)頁動(dòng)態(tài)時(shí)鐘
本文通過實(shí)例代碼給大家介紹了HTML寫一個(gè)網(wǎng)頁動(dòng)態(tài)時(shí)鐘效果,需要的的朋友參考下吧2017-08-30HTML5實(shí)現(xiàn)可縮放時(shí)鐘代碼
本文通過實(shí)例代碼給大家介紹了基于html5實(shí)現(xiàn)的可縮放時(shí)鐘代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧2017-08-28用HTML5的canvas實(shí)現(xiàn)一個(gè)炫酷時(shí)鐘效果
下面小編就為大家?guī)硪黄肏TML5的canvas實(shí)現(xiàn)一個(gè)炫酷時(shí)鐘效果。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-20使用html5 canvas 畫時(shí)鐘代碼實(shí)例分享
這篇文章主要介紹了使用html5 canvas 畫時(shí)鐘代碼實(shí)例分享的相關(guān)資料,需要的朋友可以參考下2015-11-11- 這篇文章主要介紹了html5繪制時(shí)鐘動(dòng)畫,需要的朋友可以參考下2014-12-15
- html5下實(shí)現(xiàn)的鬧鐘代碼,喜歡的朋友可以參考下。2010-10-22
- 看著有點(diǎn)黑科技的感覺就跟前段時(shí)間比較火的抖音動(dòng)態(tài)時(shí)間那個(gè)一個(gè)效果,本文主要介紹了HTML 羅盤式時(shí)鐘的實(shí)現(xiàn),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們2021-05-20