# 新手 JS 地下城- 2F 時鐘 ###### tags: `JS地下城` ## 思路歷程 1. 指針該轉幾度? 1. 時鐘有12小時、60分鐘、60秒 2. 每次秒針動一格,360度/60秒=旋轉6度 3. 每次分針動一格,也等於旋轉6度 4. 時針因為只要動12格,每一格間距比較大,所以在分針走時候,時針也要微微的朝下一格前進,分針走一格=時針走360度/12格/60分=旋轉0.5度。 2. 時間跟指針的關聯性? 如果說現在時間是 3 : 15 : 30 的話 1. 時針的旋轉角度: ((360/12格)x 3小時) + (0.5度 x 15分) =97.5度 2. 分針的旋轉角度: 15 x 6 = 90度 3. 秒針的旋轉角度: 30 x 6 = 180度 3. 取得現在時間的語法:`getDate()` 4. 取得現在時間之後,用`transform: rotate();`旋轉指針的角度 5. 每秒更新的語法:`setTimeout()` 或 `setInterval()` ## 過程 ### 首先先取得現在的時間 `getDate()`這個語法我只在jQuery的套件中有看到,沒實際使用過,所以我先研究如何用`getDate()`取得我要的資料。 因為是時鐘,沒有需要顯示年月日,所以我所需要取得的資訊有:時、分、秒。 `getDate()`其實是JS Date的取值方法之一,撈出來的是日期,要用之前得要先定義一個變數為`new Date();`,之後才能從中間撈出你要的時間資料。 ```javascript= var now = new Date(); console.log(now.getDate());//回傳今天日期 ``` 而我需要取得的值分別為: * 時:`getHours()` * 分:`getMinutes()` * 秒:`getSeconds()` 到此,這個程式架構已經明朗化,所以我打算先刻時鐘的外觀,再來把程式碼套進去驗證結果。 ### 大概刻一下時鐘外觀 時鐘的主要架構有:外殼、刻度、時針、分針、秒針,這些都要靠`position: absolute;`去堆疊,而且為了要讓大家都置中,指針的部分又要用`transform: rotate();`去旋轉,所以我將置中的寫法,從我比較常用的 ```css= transform: translate(-50%,-50%); top: 50%; left: 50%; ``` 改成 ```css= transform-origin: center; top: 0; left: 0; ``` ### JS初步驗證(還不會每秒更新) 我把除了「刻度」之外的部分都刻好之後,就先跑去寫JS的部分,細刻這種事情,等到都確認邏輯沒問題之後再來修。 總之先把指針的DOM元素都抓起來 ```javascript= var hourPointer = document.querySelector('.hour'); var minutePointer = document.querySelector('.minute'); var secondPointer = document.querySelector('.second'); ``` 然後來測測看旋轉指針有沒有作用 ```javascript= hourPointer.style.transform = "rotate(7deg)"; ``` 再來測測看指針的角度能不能另外用變數帶入 ```javascript= var hourAngle = 10; hourPointer.style.transform = `rotate(${angel}deg)`; ``` 把「時間」跟「角度」的相互關係運算式,套用進去看看 ```javascript= var hourAngle = (now.getHours() * (360/12))+(now.getMinutes() * (360/12/60)); var minuteAngle = now.getMinutes() * (360/60); var secondAngle = now.getSeconds() * (360/60); hourPointer.style.transform = `rotate(${hourAngle}deg)`; minutePointer.style.transform = `rotate(${minuteAngle}deg)`; secondPointer.style.transform = `rotate(${secondAngle}deg)`; ``` 看起來是沒有問題,只是在console的時候,我發現`getHours()`撈出來原來是24小時制呢。 不過不影響運算結果,我這邊就不特別轉成12小時制了。 ### 讓時鐘動起來(每秒更新) 接下來就要進入到重頭戲了,`setTimeout()` 我比較常看到,雖然原理沒有很熟, `setInterval()`就沒看過了,我打算先研究一下這兩者的差別,再來看看我想要用哪種。 #### setTimeout() 網頁載入後或某個function執行後,過了指定秒數之後,執行一次 寫法如下: ```javascript= setTimeout(function(){ alert("Hello"); }, 3000); // setTimeout( [ 要做什麼 ] , 秒數 ); ``` #### setInterval() 網頁載入後或某個function執行後,每間隔指定秒數,執行 是一種會重複執行的語法 寫法如下: ```javascript= setInterval(function(){ alert("Hello"); }, 3000); // setInterval( [ 要做什麼 ] , 秒數 ); ``` 看起來`setInterval()`比較符合我的需求 接下來把取得現在時間的功能打包成一個function ```javascript= function nowTime() { hourPointer.style.transform = `rotate(${hourAngle}deg)`; minutePointer.style.transform = `rotate(${minuteAngle}deg)`; secondPointer.style.transform = `rotate(${secondAngle}deg)`; } ``` 然後用`setInterval()`每秒更新一次 ```javascript= setInterval(function () { nowTime(); }, 1000); ``` 結果不會動?!Why~~ 原來`var now = new Date();`抓到一次之後就丟著不管了,不會因為我每次撈取他都幫我再抓一次現在的時間,所以只好把整組都包進function中 ## 最終程式碼如下: ```javascript= function nowTime() { var now = new Date(); // console.log(now.getDate()); var hourPointer = document.querySelector(".hour"); var minutePointer = document.querySelector(".minute"); var secondPointer = document.querySelector(".second"); var hourAngle = now.getHours() * (360 / 12) + now.getMinutes() * (360 / 12 / 60); var minuteAngle = now.getMinutes() * (360 / 60); var secondAngle = now.getSeconds() * (360 / 60); hourPointer.style.transform = `rotate(${hourAngle}deg)`; minutePointer.style.transform = `rotate(${minuteAngle}deg)`; secondPointer.style.transform = `rotate(${secondAngle}deg)`; } setInterval(function () { nowTime(); }, 1000); ``` 只是看起來要經過很多流程,好像有點蠢,也許未來有機會學得更深的時候,再來把他優化吧..... ## codepen <iframe height="265" style="width: 100%;" scrolling="no" title="時鐘" src="https://codepen.io/joy-cheng/embed/YzNggmg?height=265&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/joy-cheng/pen/YzNggmg'>時鐘</a> by Joy Cheng (<a href='https://codepen.io/joy-cheng'>@joy-cheng</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe>