canvas自適應圓形時鐘繪製

来源:http://www.cnblogs.com/xiaohuochai/archive/2017/08/29/7447652.html
-Advertisement-
Play Games

[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>

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • DIV+CSS clear both清除產生浮動 我們知道有時使用了css float浮動會產生css浮動,這個時候就需要清理清除浮動,我們就用clear樣式屬性即可實現。 接下來我們來認識與學習css clear知識與用法。 一、clear語法與結構 - TOP 1、clear語法:clear : ...
  • spectre.css是一個輕量級的css框架,只要使用過bootstrap,那麼這個使用起來就不是問題,不過現在網路上的文檔大都是英文,而且寫的比較概括,所以查閱起來不是很方便,今天是我第一天學習這個框架,把所學到的知識點記錄下來。 使用步驟: 1.引用方法 下載spectre.css,地址:ht ...
  • 網上關於閉包的文章一搜一大堆,但是我還是要來說一下我的理解。 我理解的閉包,其實就是 訪問了外部變數的函數 : 可能和同學們平常看到的理解不太一樣,但 "維基百科" )的確是這樣描述的: a closure is a record storing a function together with a ...
  • ImageData對象 ImageData對象包含了一個區域內的canvas的像素信息。它包含以下可讀屬性: width canvas的寬度,單位是像素。 height canvas的高度,單位是像素。 data 一個Uint8ClampedArray的一維數組,包含了每個像素的RGBA值。 什麼是 ...
  • 一個函數可以看成一個類,原型是所有類都有的一個屬性,原型的作用就是給這個類的每一個對象都添加一個統一的方法 <script> //聲明一個類 function Person(name,age) { this.name=name; this.age=age; } //使用原型給類添加方法 Person ...
  • javascript中如何實現繼承 ...
  • 對象和變數的區別 假如你叫張三,“變數”和“對象”的區別就是“張三”和“你”的區別 再比如: Var st = [40,25]; 上述的完整版是: Var st =new Array(); St[0]=40; St[1]=25; 這意思是將一個數組類型的對象賦值給一個var類型的變數。也可以理解為v ...
  • 我們一般使用webpack熱載入開發SPA應用,但工作中難免會遇到一些多頁面的demo或項目。 故參考 kingvid-chan 的代碼,搭了一個使用HRM開發多頁面web應用的腳手架,剛好也進一步學習webpack。 項目結構很簡單,需要註意的是: 因使用fs等api,所以每次添加新頁面之後,需要 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...