[1]效果演示 [2]功能分析 [3]靜態時鐘 [4]動態效果 [5]完整代碼 ...
前面的話
前面介紹過canvas粒子時鐘的繪製,本文將詳細介紹canvas自適應圓形時鐘繪製
效果演示
最終自適應圓形時鐘的效果如下所示
功能分析
下麵來分析一下該圓形時鐘的功能
【1】靜態背景
對於時鐘來說,背景是不變的,包括外層鐘框、內層圓點及數字、以及中心點的固定按扣
【2】動態時鐘
時態的動態,表現在秒針、分針、時針隨著當前時間的變化的變化。開啟一個每秒變化1次定時器,秒針與當前的時間的秒數保持一致,分針的變化與當前的秒數和分鐘數都有關,時針的變化與當前的分鐘數和小時數都有關
【3】自適應
要做到時鐘自適應,需要將時鐘內部的尺寸繪製與時鐘整體的寬高相關聯,而不能設置為固定值
下麵是一張時鐘的簡易分析圖

靜態時鐘
下麵來實現靜態的時鐘背景,包括外層鐘框、內層圓點及數字、以及中心點的固定按扣,以時鐘尺寸為200*200為基準,則半徑為100,通過translate()將圓心點調整為(0,0)點
【初始設置】
由於外面經常要用到R和cxt.lineWidth,所以將其保存為變數
var cxt = drawing.getContext('2d'); var W = drawing.width = 400; var H = drawing.height = 400; var R = W / 2; var cw = cxt.lineWidth = 0.1*R;
【外層鐘框】
為了將外層鐘框不超出canvas區域,則其半徑設置為R-cw/2,線條寬度與半徑成比例
cxt.translate(R,R); cxt.beginPath(); cxt.arc(0,0,R-cw/2,0,2*Math.PI,false); cxt.stroke();
【內層數字】
在距離圓心點0.8R-cw/2處,繪製12個數字,表示當前的分鐘數,數字的字體大小與半徑成比例
cxt.beginPath(); cxt.font = 0.2 * R + 'px 宋體'; cxt.textAlign = 'center'; cxt.textBaseline = 'middle'; var r1 = 0.8*R - cw/2; for(var i = 12; i > 0; i--){ var radius = 2*Math.PI/12 * i + 1.5*Math.PI; var x = Math.cos(radius) * r1; var y = Math.sin(radius) * r1; cxt.fillText(i,x,y); }
【內層原點】
在距離圓心點0.9R-cw/2處,繪製60個圓點,表示當前的秒數,當前秒數與分鐘數處於同一角度時,表示為大圓點(半徑為cx/5),否則為小圓點(半徑為cx/8)
cxt.beginPath(); var r2 = 0.9*R - cw/2; for(var i = 0; i < 60; i++){ var radius = 2*Math.PI/60*i + 1.5*Math.PI; var x = Math.cos(radius) * r2; var y = Math.sin(radius) * r2; cxt.beginPath(); if(i%5 === 0){ cxt.arc(x,y,cw/5,0,2*Math.PI,false); }else{ cxt.arc(x,y,cw/8,0,2*Math.PI,false); } cxt.fill(); }
【繪製中心點的固定按扣】
cxt.beginPath();
cxt.arc(0,0,cw/3,0,2*Math.PI,false);
cxt.fill();
最終,靜態背景封裝為函數drawStatics(),代碼如下
<canvas id="drawing" style="border:1px solid black"></canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var cxt = drawing.getContext('2d'); var W = drawing.width = 200; var H = drawing.height = 200; var R = W / 2; var cw = cxt.lineWidth = 0.1*R; function drawStatics(){ cxt.translate(R,R); cxt.beginPath(); cxt.lineWidth = 0.1*R; cxt.arc(0,0,R-cw/2,0,2*Math.PI,false); cxt.stroke(); cxt.beginPath(); cxt.font = 0.2 * R + 'px 宋體'; cxt.textAlign = 'center'; cxt.textBaseline = 'middle'; var r1 = 0.8*R - cw/2; for(var i = 12; i > 0; i--){ var radius = 2*Math.PI/12 * i + 1.5*Math.PI; var x = Math.cos(radius) * r1; var y = Math.sin(radius) * r1; cxt.fillText(i,x,y); } cxt.beginPath(); var r2 = 0.9*R - cw/2; for(var i = 0; i < 60; i++){ var radius = 2*Math.PI/60*i + 1.5*Math.PI; var x = Math.cos(radius) * r2; var y = Math.sin(radius) * r2; cxt.beginPath(); if(i%5 === 0){ cxt.arc(x,y,cw/5,0,2*Math.PI,false); }else{ cxt.arc(x,y,cw/8,0,2*Math.PI,false); } cxt.fill(); } cxt.beginPath(); cxt.arc(0,0,cw/3,0,2*Math.PI,false); cxt.fill(); } function draw(){ cxt.clearRect(0,0,W,H); drawStatics(); } draw(); } </script>
靜態效果如下
動態效果
下麵來分為時針、分針、秒針來進行動態效果
【秒針】
開啟一個每秒變化1次定時器,秒針與當前的時間的秒數保持一致
function drawSecond(second){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); var radius = 2*Math.PI/60 * second; cxt.rotate(radius); cxt.lineWidth = 2; cxt.moveTo(0,cw*2); cxt.lineTo(0,-0.8*R); cxt.strokeStyle = 'red'; cxt.stroke(); cxt.restore(); }
【分針】
分針的變化與當前的秒數和分鐘數都有關
function drawMinute(minute,second){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); var radius = 2*Math.PI/60 * minute; var sRaiuds = 2*Math.PI/60/60 * second; cxt.rotate(radius + sRaiuds); cxt.lineWidth = 4; cxt.lineCap = 'round'; cxt.moveTo(0,cw); cxt.lineTo(0,-(0.8*R - cw/2)); cxt.stroke(); cxt.restore(); }
【時針】
時針的變化與當前的分鐘數和小時數都有關
function drawHour(hour,minute){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); var radius = 2*Math.PI/12 * hour; var mRaiuds = 2*Math.PI/12/60 * minute; cxt.rotate(radius + mRaiuds); cxt.lineWidth = 6; cxt.lineCap = 'round'; cxt.moveTo(0,cw/2); cxt.lineTo(0,-(0.8*R - cw*2)); cxt.stroke(); cxt.restore(); }
完整代碼
現在,需要對代碼進行調整,因為canvas是按照代碼順序進行繪製的,所以代碼順序應該是,靜態背景(時鐘外框、圓點及數字) -> 動態效果(秒針、分針、時針) -> 中心按扣
因此,需要將中心按扣的代碼從靜態背景函數drawStatics()中分離出來,並重新安排代碼順序
由於瀏覽器的定時器存在誤差,因此設置為1000ms並不合適,由於系統卡頓等原因,可能會跳過某次效果,因此,設置為500ms
最終完整代碼如下所示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <canvas id="drawing"></canvas> <script> var drawing = document.getElementById('drawing'); if(drawing.getContext){ var cxt = drawing.getContext('2d'); var W = drawing.width = 200; var H = drawing.height = 200; var R = W / 2; var cw = cxt.lineWidth = 0.1*R; function drawStatics(){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); cxt.lineWidth = 0.1*R; cxt.arc(0,0,R-cw/2,0,2*Math.PI,false); cxt.stroke(); cxt.beginPath(); cxt.font = 0.2 * R + 'px 宋體'; cxt.textAlign = 'center'; cxt.textBaseline = 'middle'; var r1 = 0.8*R - cw/2; for(var i = 12; i > 0; i--){ var radius = 2*Math.PI/12 * i + 1.5*Math.PI; var x = Math.cos(radius) * r1; var y = Math.sin(radius) * r1; cxt.fillText(i,x,y); } cxt.beginPath(); var r2 = 0.9*R - cw/2; for(var i = 0; i < 60; i++){ var radius = 2*Math.PI/60*i + 1.5*Math.PI; var x = Math.cos(radius) * r2; var y = Math.sin(radius) * r2; cxt.beginPath(); if(i%5 === 0){ cxt.arc(x,y,cw/5,0,2*Math.PI,false); }else{ cxt.arc(x,y,cw/8,0,2*Math.PI,false); } cxt.fill(); } cxt.restore(); } function drawDot(){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); cxt.arc(0,0,cw/3,0,2*Math.PI,false);
cxt.fillStyle = '#fff';
cxt.fill(); cxt.restore(); } function drawSecond(second){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); var radius = 2*Math.PI/60 * second; cxt.rotate(radius); cxt.lineWidth = 2; cxt.moveTo(0,cw*2); cxt.lineTo(0,-0.8*R); cxt.strokeStyle = 'red'; cxt.stroke(); cxt.restore(); } function drawMinute(minute,second){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); var radius = 2*Math.PI/60 * minute; var sRaiuds = 2*Math.PI/60/60 * second; cxt.rotate(radius + sRaiuds); cxt.lineWidth = 4; cxt.lineCap = 'round'; cxt.moveTo(0,cw); cxt.lineTo(0,-(0.8*R - cw/2)); cxt.stroke(); cxt.restore(); } function drawHour(hour,minute){ cxt.save(); cxt.translate(R,R); cxt.beginPath(); var radius = 2*Math.PI/12 * hour; var mRaiuds = 2*Math.PI/12/60 * minute; cxt.rotate(radius + mRaiuds); cxt.lineWidth = 6; cxt.lineCap = 'round'; cxt.moveTo(0,cw/2); cxt.lineTo(0,-(0.8*R - cw*2)); cxt.stroke(); cxt.restore(); } function draw(){ cxt.clearRect(0,0,W,H); drawStatics(); var now = new Date(); drawHour(now.getHours(),now.getMinutes()); drawMinute(now.getMinutes(),now.getSeconds()); drawSecond(now.getSeconds()); drawDot(); } draw(); setInterval(draw,500); } </script> </body> </html>