canvas自適應圓形時鍾繪制


前面的話

  前面介紹過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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM