ðÈ7新手 JS 地下城 - 2F æ™‚é˜ Clock | Alex Liu

新手 JS 地下城 - 2F æ™‚é˜ Clock

• 9 min read

這是由六角學院在 Udemy 推出的:JavaScript 題目篇 - 新手 JS 地下城,所出的題目。今天è¦ä¾†åˆ†äº«çš„æ˜¯ç¬¬äºŒé¡Œï¼šæ™‚é˜ï¼ˆClock)

題目說明

  • 需使用 JS 原生語法的 getDate() æ’ˆå–æ™‚間,ä¸å¯ç”¨å¥—ä»¶
  • 需使用 JS 原生語法的 setTimeout() 或 setInterval(),æŒçºŒè®“ç§’é‡ã€åˆ†é‡ã€æ™‚é‡èƒ½å¤ ä»¥å°åŒ—時å€ç§»å‹•

HTML / CSS / JavaScript

我給自己一點點å°å°çš„é™åˆ¶ï¼Œä¸€ä¾†æ˜¯ç‚ºäº†è®“è‡ªå·±å° js 的使用更熟悉,一種是...自è™ï¼š

  • ç›¡é‡æŒ‘戰使用 Vanilla JS
  • 盡å¯èƒ½ä¸ç”¨ ES6 的方法

HTML 部分

<div class="clockBody">
 <div class="clockFace">
  <div class="secHandler"></div>
  <div class="hrHandler"></div>
  <div class="minHandler"></div>
 </div>
</div>

CSS 部分

.secHandler, .minHandler, .minHandler {
 transform-origin: center 2px
}

CSS 的部分比較複雜 (而且當時沒有模組化的觀念,後來改用 BEM 管ç†ï¼‰

想看完整樣å¼çš„æ§æ²¹å¯ä»¥é»žä¸‹é¢çš„連çµ

簡單來說:內的 transform-origin 這個屬性å¯ä»¥è¨­å®šå…ƒç´ è®ŠåŒ–(transform)的原點。é è¨­çš„ä½ç½®ç‚ºå…ƒç´ çš„中心點也就是:transform-origin: 50% 50% çš„ä½ç½®ã€‚ ä¸Šé¢ transform-origin çš„ç¬¬ä¸€å€‹åƒæ•¸ç‚º X è»¸ï¼Œç¬¬äºŒå€‹åƒæ•¸ç‚º Y è»¸ï¼Œå¦‚æžœæœ‰ç¬¬ä¸‰å€‹åƒæ•¸çš„話則是 Z 軸

MDN 上有更詳細的範例說明,連çµä¹Ÿé™„在這裡:transform-origin - CSS | MDN

JS 部分

以下分æˆå››å€‹éƒ¨åˆ†ï¼šDOM é¸å–ã€ç”¢ç”Ÿåˆ»åº¦ã€ç”¢ç”Ÿæ•¸å­—,ä¸ç„¶ä¸€æ¬¡è²¼æœ‰é»žé•·å•Šï¼

DOM é¸å–

var clockFace = document.querySelector('.clockFace');
var hrHandler = document.querySelector('.hrHandler');
var minHandler = document.querySelector('.minHandler');
var secHandler = document.querySelector('.secHandler');

產生刻度

for (var i = 0; i < 60; i++){
  var timePoint = document.createElement('div');

  i % 5 === 0
    ? timePoint.classList.add('line') 
    : timePoint.classList.add('point');

  timePoint.style.transform = 'translate(-50%) rotate(' + i*6 + 'deg)';
  clockFace.appendChild(timePoint);
}

這裡的迴圈總共會產 60 個 div

(i % 5 === 0)? timePoint.classList.add('line') : timePoint.classList.add('point') 三元判斷å¼éƒ¨åˆ†ï¼Œå¦‚æžœ i ÷ 5 的餘數為 0 ,他的ä½ç½®å°±æ˜¯æ•´é»žåƒæ˜¯ï¼š0ã€5ã€10...,那這裡ä¾ç…§è¨­è¨ˆç¨¿å°±æœƒæ˜¯ä¸€æ¢ç¸£ï¼Œå…¶ä»–地方則會å¼ä¸€å€‹é»žï¼Œæˆ‘分別用了兩個ä¸åŒ class åšå€åˆ†ã€‚

timePoint.style.transform = 'translate(-50%) rotate(' + i*6 + 'deg)' 角度部分:360 ÷ 60 = 6 度。所以æ¯ä¸€å€‹ div é–“éš” 6 度(deg),至於è¦è®“ä»–å°æº–時中的政中心旋轉的部分則寫在 CSS 裡é¢ï¼Œä¾‹å¦‚: transform-origin: center 130px

這個部份我沒有按照設計稿,設計稿的間隔怪怪的

產生數字

for (var i = 0; i < 12; i++) {
  var amNum = document.createElement('div');
  var amNumText = document.createElement('span');
  var pmNum= document.createElement('div');
  var pmNumText = document.createElement('span');

  amNum.classList.add('amNum');
  pmNum.classList.add('pmNum');

  (i === 0) ? amNumText.innerText = 12 : amNumText.innerText = i;
  (i === 0) ? pmNumText.innerText = 24 : pmNumText.innerText = i + 12;

  amNumText.style.transform = 'rotate(' + (-i * 30) + 'deg)'
  pmNumText.style.transform = 'rotate(' + (-i * 30) + 'deg)'

  amNum.appendChild(amNumText);
  pmNum.appendChild(pmNumText);

  amNum.style.transform = 'translate(-50%) rotate(' + i * 30 + 'deg)';
  pmNum.style.transform = 'translate(-50%) rotate(' + i * 30 + 'deg)';

  clockFace.appendChild(amNum);
  clockFace.appendChild(pmNum);
}

amNumText 的範åœç‚º 1 ~ 12,pmNumText 的範åœç‚º 13 ~ 24,延伸上é¢é–“隔的部分,æ¯äº”格會出ç¾ä¸€æ¬¡æ•¸å­—,所以是 translate(-50%) rotate(' + i*30 + 'deg),而 rotate(' + (-i*30) + 'deg) çš„éƒ¨åˆ†å‰‡æ˜¯è¦æŠŠå°è‘—中心旋轉的數字在喬正。

function showTime(){
  var timeData = new Date();
  var hr = timeData.getHours();
  var min = timeData.getMinutes();
  var sec = timeData.getSeconds();
  var secMillis = timeData.getMilliseconds();
  var secRotate = sec * 6 + secMillis * 0.006 + 180;
  var minRotate = min * 6 + sec * 0.1 + 180 ;
  var hrRotate = hr * 30 + min * 0.5 + 180;
  secHandler.setAttribute('style','transform: translateX(-50%) rotate(' + secRotate + 'deg)');
  minHandler.setAttribute('style','transform: translateX(-50%) rotate(' + minRotate + 'deg)');
  hrHandler.setAttribute('style','transform: translateX(-50%) rotate(' + hrRotate + 'deg)');
  window.requestAnimationFrame(showTime);
}
window.requestAnimationFrame(showTime);

new Date() 進到é‡é»ž JavaScript 了(ç‘花)。關於 Date.prototype çš„ methods 下é¢åƒ…說明這個題目會用到的,他有超多方法å¯ä»¥ç”¨ï¼Œéœ€è¦æ™‚å¯ä»¥åŽ» MDN(Date - JavaScript | MDN) 查詢。

Date.prototype.getHours()
å›žå‚³æœ¬åœ°æ™‚é–“çš„å°æ™‚(0 ~ 23)。

Date.prototype.getMinutes()
å›žå‚³æœ¬åœ°æ™‚é–“çš„åˆ†é˜æ•¸ï¼ˆ0 ~ 59)。

Date.prototype.getSeconds()
回傳本地時間的秒數(0 ~ 59)。

Date.prototype.getMilliseconds()
回傳本地時間的毫秒數(0 ~ 999)。

有了上é¢çš„資料,接下來è¦é€²åˆ°æ•¸å­¸çš„部分啦ï¼ç®—角度拉 ~

var secRotate = sec*6 + secMillis*0.006 + 180;
var minRotate = min*6 + sec*0.1 + 180 ;
var hrRotate = hr*30 + min*0.5 + 180;
  • ç§’é‡ï¼šæ¯ä¸€å°æ ¼ 6 度,所以 秒數 × 6 度 得到到的就是秒é‡çš„角度,這邊加上了毫秒(0 ~ 999)× 0.006 是為了讓動畫看起來更順暢,這裡也å¯ä»¥ä¸åŠ ï¼Œç§’é‡çœ‹èµ·ä¾†å°±æ˜¯ä¸€æ ¼ä¸€æ ¼çš„è·³ï¼Œå–æ±ºæ–¼æƒ³è¦ä»€éº¼æ•ˆæžœå›‰ï¼
  • 分é‡ï¼šæ¯ä¸€å°æ ¼ 6 度,分 × 6 度 å³å¯å¾—到分é‡çš„角度。但多數時é˜è¨­è¨ˆï¼Œåˆ†é‡è·Ÿæ™‚é‡éƒ½æ˜¯ç·©ç·©ç§»å‹•ï¼Œä¸æ˜¯æ™‚間到瞬間彈到下一格,所以分é‡é€™é‚Šè¦åŠ ä¸Š 秒數 × 0.1 ï¼ˆæ¯ 60 ç§’å‰é€² 6 度)
  • 時é‡ï¼šæ¯ä¸€å¤§æ ¼ 30 度,時 × 30 度,加上 分 × 0.5 ï¼ˆæ¯ 60 ç§’å‰é€² 30 度)

上é¢å¦‚æžœä¸å¤ªèƒ½ç†è§£ï¼Œå¸¶æ•¸å­—進去算會比較有幫助喔~

接下來è¦å‹•起來囉ï¼ï¼ï¼~

這邊我使用了 requestAnimationFrame() 執行畫é¢çš„æ›´æ–°ã€‚雖然題目指定è¦ä½¿ç”¨ setTimeout() 或 setInterval()。 requestAnimationFrame() å¯ä»¥æƒ³åƒæ˜¯å„ªåŒ–版本的 setTimeout()ï¼Œä»–çš„æ›´æ–°é »çŽ‡æ˜¯å°æº–螢幕更新平率的,在有,當螢幕更新一次畫é¢å°±æœƒå‘¼å«ä¸€æ¬¡ requestAnimationFrame() 帶進去的 function。新方法總è¦çœ‹ä¸€ä¸‹ç€è¦½å™¨æ”¯æ´åº¦ã€‚

Can I use requestAnimationFrame Can I use requestAnimationFrame:Can I use requestAnimationFrame

基本上大多數ç€è¦½å™¨éƒ½å·²ç¶“有很高的支æ´åº¦ï¼Œå¦‚果真的在æ„的話å¯ä»¥åšä¸‹é¢çš„動作:

;(function(){
  var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
  window.requestAnimationFrame = requestAnimationFrame;
})()

這樣就å¯ä»¥æ¯”較放心地使用囉ï¼å¦‚果上é¢çš„æ–¹å¼éƒ½ä¸æ”¯æ´æœ€å¾Œé‚„是å¯ä»¥ç”¨ï¼š setTimeout(function, 16)

我自己大多數的時候是 requestAnimationFrame() / setTimeout() 的愛用者,但è¦è¨˜å¾—這兩這都知會呼å«ä¸€æ¬¡ function,如果è¦å®šæ™‚呼å«çš„話è¦åœ¨è‡ªè¢«å‘¼å«çš„ function ç«‹é¢å†å‘¼å«è‡ªå·±ä¸€æ¬¡ã€‚


çµèªž

以上就是é‡å° 新手 JS 地下城 - 2F æ™‚é˜ Clock 的分享。當時在寫這題時在角度計算部分å°å¡äº†ä¸€ä¸‹ï¼Œå…¶å¯¦åœ¨å‰ç«¯å·¥ä½œç¶“驗裡也常常需è¦é€²è¡Œç°¡å–®å»ç¹ç‘£çš„æ•¸å­¸é‹ç®—,這時就è¦è®“è‡ªå·±é ­è…¦ä¿æŒæ¸…楚,最好是還是拿出紙根比ç¨å¾®ç•«ä¸€ä¸‹æœƒæ¯”想破頭有幫助。希望這些內容能讓å„ä½çœ‹å®˜æ§æ²¹æœ‰é»žæ”¶ç©«ï¼æœ‰ä»»ä½•指教都歡迎在下方留言告訴我。

é¡å¤–推薦

這題其實在 Wes Bos çš„ JavaScript30 第二單元有教。JavaScript30 是一個完全å…è²» + 全英文字幕 çš„ JavaScript 教學課程。建議有點 JavaScript 基礎的å¯ä»¥åŽ»è½è½çœ‹ã€‚

如果想看更多關於 JavaScript 時é˜çš„製作,也推薦下é¢é€™æ”¯å½±ç‰‡ï¼š

Alex 宅幹嘛 👨â€ðŸ’» 深入淺出 Javascript30 快速導覽:Day 2:CSS + JS Clock(連çµï¼‰ 深入淺出 Javascript30 快速導覽:Day 2:CSS + JS Clock

Alex 大大的頻é“上有éžå¸¸è±å¯Œçš„ JavaScript 學習資æºï¼Œç‰¹åˆ¥å¦‚果喜歡 Vue.js çš„æ§æ²¹çµ•å°ä¸èƒ½éŒ¯éŽå•¦ï¼

è«‹æˆ‘å–æ¯å’–å•¡

如果這裡的內容有幫助到你的話,一æ¯å’–å•¡å°±æ˜¯å°æˆ‘最大的鼓勵。

è«‹æˆ‘å–æ¯å’–å•¡
ÿÿÿÿ