JS地下城-2F時鐘

新手JS-地下城:2F-時鐘

紀錄一下這次使用的Canvas
See the Pen
2F-Clock
by yanennn (@yennnnn)
on CodePen.


不知道Canvas如何使用的人可以參考六角學院-使用Canvas製作遊戲並搭配MDN會加深很大的印象

前置作業:

首先在html使用canvas標籤,像是再瀏覽器產生一個固定大小的繪圖畫布,當瀏覽器抓到canvas了,再來就是要告知它要使用何種方式渲染環境,在這邊我們是使用平面繪圖,所以getContext就要輸入2d。
接著輸入你畫布的尺寸,在這邊是要全部瀏覽器都是畫布,所以使用widow.innerWidthwindow.innerHeight

 var mycanvas=document.querySelector('#canvas');
 var ctx = mycanvas.getContext("2d"); 
 var ww=window.innerWidth;
 var wh=window.innerHeight;
 mycanvas.width=ww;
 mycanvas.height=wh;


這邊要提醒,繪製一個圖形我們需要以下步驟:
1.ctx.save();儲存
2.ctx.beginPath();開始繪圖
3.ctx.restore();復原上一階段

當開始繪畫後,首先我們要先儲存當前狀態,畫完後使用ctx.restore()還原狀態,才不會導致之後的圖會因為剛剛的動作導致重複或者飄移,再來每當要畫一個地方就要使用beginPath產生一個新路徑,告訴JS說我要開始畫圖了,那有一個ctx.closePath()關閉路徑,這個方法會在現在所在點到起始點間畫一條直線以閉合圖形,如果圖形已經閉合或是只含一個點,這個方法不會有任何效果。


在這邊介紹幾個常見功能

1.填色:
fillStyle實心顏色、SrokeStyle空心顏色,有使用填色後面要記得使用fill()stroke()表示結束

2.lineWidth(線寬):
lineWidth=2表示你要使用的線寬是2

3.moveTo(x,y):移動到哪個座標

4.lineTo(x,y):畫一條線到哪個座標

5.arc(x,y,半徑,起點位置,終點位置,true):畫圓(弧度)
x,y為中心座標
起點位置是你想從圓的哪個位置開始畫,通常會以0開始
中點位置:假如你要畫個圓可以使用Math.PI*2 (Math.PI是180度所以乘2就是360度)
最後的true表示為順時鐘畫圓,假如要使用逆時鐘可以改成false

6.rect(x, y, 寬度, 高度):畫矩形

7.ctx.rotate(角度):旋轉角度
以上面時鐘為範例,當你有個物件想要重複執行變成圓圈就可以使用ctx.rotate()


製作鐘錶:
我們分三個階段
1.外層
2.四方圓角
3.圓形

 ctx.fillStyle="#293B29";
  ctx.fillRect(0,0,ww,wh);
  //儲存
  ctx.save();
  //移至中心點
  ctx.translate(ww/2,wh/2);

  //開始繪圖
  ctx.beginPath();
  //填滿顏色
  ctx.fillStyle="#3d5a45";
  ctx.moveTo(145,175);//移動到145,175
  ctx.quadraticCurveTo(175,175,175,145);//二次曲線
  ctx.lineTo(175,-145);//畫條線到175,-145
  ctx.quadraticCurveTo(175,-175,145,-175);//二次曲線
  ctx.lineTo(-145,-175);
  ctx.quadraticCurveTo(-175,-175,-175,-145);
  ctx.lineTo(-175,145);
  ctx.quadraticCurveTo(-175,175,-145,175);
  ctx.lineTo(145,175);
  ctx.fill();
  
  //圓形鐘面
  ctx.beginPath();//開始繪圖
  ctx.fillStyle="#293b29";//顏色
  ctx.arc(0,0,150,0,Math.PI*2,true);//圓形
  ctx.fill();//填滿
  ctx.strokeStyle="#212f0b";//框顏色
  ctx.lineWidth=3;//寬度
  ctx.stroke();//填滿

首先第一步:外表我們只要單色填滿所以使用ctx.fillStyle="#293B29";接下來使用方形把你要填色的大小框出,在這邊因為是整個瀏覽器都要填滿所以使用ctx.fillRect();

第二步:移到瀏覽器中心點製作四方型圓角,這邊我們使用二次曲線製作ctx.quadraticCurveTo(cp1x, cp1y, x, y)不了解貝茲曲線和二次曲線的可以查貝茲曲線和AI、PS軟體的貝茲曲線一樣cp1x,cp1y是給你設定無形的控制點,而x,y則指定的終點

第三部:設定顏色會,在0,0的座標為圓點中心畫一個圓,使用半徑150,起點從0轉Math.PI*2度也就是360度,之後上色,而圓框的部分則是使用strokeStyle勾勒框圖形,給予寬度,最後上色


刻度:
這邊介紹白色點點

  for(var i=0;i<72 ath.pi="" code="" ctx.arc="" ctx.beginpath="" ctx.fill="" ctx.fillstyle="white" ctx.rotate="" else="" i="" if="" true="">
以範例來說以小時來說,一個小時會有4個點點,中間還參雜橘色部分,所以首我們要先計算一個點點和另一個點點的距離是多少,我們假如全部都是白色點點的話一共有72個點點,所以就是Math*2/72,接著我們發現只要是三的倍數都是橘色區域,所以我們可以使用if假設若能被3整除就轉角度,若不行則是填上白色點點並轉角度


橘色線條和星星也是這樣操作,星星製作則是使用貝茲曲線ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

時間數字則是:

 ctx.fillStyle="white";
 ctx.font = '10px serif';
 var ang;
 let innum=[6,7,8,9,10,11,12,1,2,3,4,5];
 for(var num = 0; num < innum.length; num++){
   
   ang=Math.PI*2/12*num;
    ctx.rotate(ang);//旋轉角度
    ctx.translate(0,90);//從原點移動半徑距離r
    ctx.rotate(-ang);//為了防止打印出來的數字跟著轉,這時候將座標轉回來
    ctx.fillText(innum[num].toString(),0,0);//使用原點位置當成打印座標,把數字印出來
    ctx.textBaseline = "middle";//基線對齊設置
    ctx.textAlign="center";//文字對齊設置
    ctx.rotate(ang);//接著轉回原位
    ctx.translate(0,-90);//倒退回到時鐘圓心
    ctx.rotate(-ang);//轉回最開始的角度
  }
 

指針:
這邊最主要的是製作指針樣式前要加入ctx.rotate(item);告知需要多少角度

 //時針
 function hourHand(item){
  ctx.save();//儲存狀態
  ctx.translate(ww/2,wh/2);//移到中心
  ctx.rotate(item);//旋轉角度
  ctx.lineWidth=7;
  ctx.strokeStyle="white";
  ctx.beginPath();
  ctx.moveTo(0,0);
  ctx.lineTo(0,-55);
  ctx.stroke();
  
  //白色內的黑線
  ctx.lineWidth=2;
  ctx.strokeStyle="black";
  ctx.beginPath();
  ctx.moveTo(0,-30);
  ctx.lineTo(0,-50);
  ctx.stroke();
  ctx.restore();//復原
 }
  
 
以時針為例子,首先因為是額外開啟一個function所以要增加ctx.save(),接著移動到中心,告知你的角度(參數),接著設定顏色、樣式,最後復原狀態

再來是重頭戲時鐘的指針走動:

 function update(){
  var now=new Date();
  var sec=now.getSeconds();
  var min=now.getMinutes();
  var hour=now.getHours();
  hour=hour>12?hour-12:hour;//計算小時是否大於12,是的話扣12小時,否正常輸出
  var secrot=Math.PI*2/60*sec;
  var minrot=(Math.PI*2/60*min)+(Math.PI*2/60)/60*sec;//計算一分鐘可以走6度,在乘已經走了幾分鐘,加上在一分鐘以內一秒走了0.2度,再乘走了60秒
  var hourrot=(Math.PI*2/12*hour)+(Math.PI*2/12/60*min)+(Math.PI*2/12/60/60*sec);//計算12小時一小時需要走30度,乘已走了幾個小時,加上一小時需走60分鐘(一小時一分鐘需要0.5度),乘共走了幾分鐘,加上一分鐘需要走60秒(一小時一分鐘一秒需要0.00833333333度),乘已共走了幾秒
  ctx.clearRect(0,0,500,500);
  draw();
  hourHand(hourrot);
  secHand(secrot);
  minuteHand(minrot);
  }

 
首先抓取我們的目前時間,之後抓去秒數,分數,時數,接下來計算每秒、每分、每小時都需要走的角度,畢竟沒有一個時鐘是直接大幅度跳角度的吧,所以當秒針走到這裡時,分針和時針也要移動

接著為了避免前面移動的會殘留所以要使用ctx.clearRect(0,0,width,height);清除指定位置矩形內的內容,使他變透明。

最後把參數放進控制指針的function就大功造成

最後我們在外面放setInterval(update,1000);讓我們的時鐘能1000毫秒跑一次流程以及使用requestAnimationFrame(draw);通知瀏覽器我們想要產生動畫,並且要求瀏覽器在下次重繪畫面前呼叫特定函數更新動畫。這個方法接受一個引數作為下次重繪前調用的回呼函數。


以上第一次使用canvas還有很多不清楚也沒做好的部分,也參考許多前輩,但不得不說還蠻好玩的~
只是邏輯要很夠(笑)


參考來源:
https://developer.mozilla.org/zh-TW/docs/Web/API/Canvas_API/Tutorial/Basic_usage
https://medium.com/@ruby30336/%E4%BD%BF%E7%94%A8canvas%E8%88%87js%E5%81%9A%E5%9C%93%E5%BD%A2%E6%99%82%E9%90%98-c400e0f51d3f
https://www.youtube.com/watch?v=sOHcx9jekzs
https://medium.com/@cos214159/canvas-%E7%AD%86%E8%A8%98-8-%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9Abeziercurveto-93b8f5257c86

留言