新手 JS 地下城 2F — 時鐘
六角學院 JS 地下城 - 2F 時鐘
新手 JS 地下城 2F — 時鐘#
2F — 時鐘
這篇是六角學院的 JavaScript 題目篇- 🐲 新手 JS 地下城,2F Boss 關卡「時鐘」攻略過程心得。
順利攻略完第一關後,決定馬上來挑戰一下第二關,這一次還多了特定技術的考驗,既然都踏入地下城了,當然要來挑戰看看囉!
BOSS 弱點#
- 【特定技術】需使用 JS 原生語法的
getDate()
撈取時間,不可用套件 - 【特定技術】需使用 JS 原生語法的
setTimeout()
或setInterval()
,持續讓秒針、分針、時針能夠以台北時區移動 - 【特定技術】介面請全部用 CSS2、CSS3 手寫繪製,什麼…?你說太強人所難??那..用圖片也不是不行辣, 點選一下元素,右側控制列會有個藍色按鈕,點選 [下載] 即可。
解題思路#
- 使用 getDate() 來抓取現在時間
- 使用 setInterval() 來持續呼叫函式
- 利用 Vue 概念來加速完成關卡
這一次在挑戰前看了不少其他同學的作品,一部分是對這次的關卡沒甚麼掌握度,另一部分也就是那邪惡的版面...意外發現大多同學都推薦 Alex 老師先前的的 CSS + JS Clock 的教學影片,不僅觀念講得很詳細,甚至可以直接完成這關的挑戰呢~
Alex 老師的頻道有好多 JS 和 Vue 的影片,看起來又發現了新大陸呢!
畫面處理#
2F — 時鐘 設計稿
雖然這次有提供了設計稿可以直接使用,不過還是決定來挑戰一下自己!
2F — 時鐘 BackGround
首先需要處理的時鐘中的背景,最麻煩的也就是中間的刻度,這裡我使用元件的方式,將內外數字和點拆成三部分來完成。
已複製!app.component('clock-text', { data() { return { textCss: { rotate: '', content: '', textRotate: '', }, } }, props: ['x'], template: `<div class="clock-text" :style="{ 'transform': rotate, '--textRotate': textRotate }" :data-text='text'></div>`, created() { this.rotate = `rotate(${this.x * 30}deg)` this.text = this.x + 12 + '' this.textRotate = `rotate(${this.x * -30}deg)` }, })
2F — 時鐘刻度 Component
已複製!app.component('clock-stext', { data() { return { textCss: { rotate: '', stextRotate: '', }, } }, props: ['y'], template: `<div class="clock-stext" :style="{ 'transform': rotate, '--stextRotate': stextRotate }" :data-stext='y'></div>`, created() { this.rotate = `rotate(${this.y * 30}deg)` this.stextRotate = `rotate(${this.y * -30}deg)` }, })
2F — 時鐘刻度 Component
文字部分比較單純,可以直接用 v-for 快速處理,再將值用 props 傳入進行運算,這裡都是各印出 12 個,比較需要注意的有兩個地方。
- 數字間橘線的部分需要處理
- 每個數字位置要計算清楚,以免對不上秒針
- 數字呈現時要調整角度讓數字擺正
首先橘線的部分我是這樣完成的~
2F — 時鐘 BackGround CSS
接著再使用偽元素來完成文字,一個圓有 360 度,而我們需要放上 12 個數字,所以每放上一個就要增加 60 度,如果不懂的話可以看 Alex 老師講解的影片喔!
比較特別的是,這裡我使用了 attr() 的語法,由於橘線會重複 12 條,使用 v-for 後,將值 * 12 後加在標籤中。
已複製!template: `<div class="clock-stext" :style="{ 'transform': rotate, '--stextRotate': stextRotate }" :data-stext='y'></div>`,
使用 v-bind 綁定 data-text 的值
接著在 CSS 中用 content: attr(data-text) 來動態切換 content 的值!
已複製!.clock-text::before { content: attr(data-text); position: absolute; transform: translate(-6px, -20px) var(--textRotate); }
只要將 data 值 放入 attr() 內就可以使用!
attr()目前根據 MDN 文件記載,除了 content 以外,其他屬性的使用皆實驗階段。
這樣數字就會跑出來了~
2F — 時鐘 BackGround
接下來要做的是將數字擺正,這裡我使用 CSS 變數來做動態更換。
已複製!.clock-text { width: 1px; height: 250px; position: absolute; left: 154.5px; bottom: 30px; color: #fff; background-color: #ff7600; font-size: 10px; --textRotate: rotate(-30deg); } .clock-text::before { content: attr(data-text); position: absolute; transform: translate(-6px, -20px) var(--textRotate); }
一樣使用 v-bind 的方式來綁定 style 就可以達到效果。
而內層數字的方式跟外層不太一樣,我使用在裡投新增一個內圓的方式。
2F — 時鐘 BackGround
這樣除了能將文字放到對應的地方,還可以調整圓的背景色來抵銷多餘的橘線!!!
至於其他部分都和外數字一樣處理就歐給了。
貼心提醒 : 裡頭一樣會新增 12 個數字,也就是 12 個內圓,而這每一個內圓會不斷的蓋上前一個圓,所以請另外使用 CSS 選取器來調整第一個圓背景色~如果直接加在每一個圓的 CSS 裡面的話,文字可是會被蓋掉的喔!
以上是卡在這個地方長達一個小時差點懷疑人生的過來人經驗。
點的部分就沒什麼問題了,只要注意每一個點的角度跟和星星與數字的位置,透過 JS 判斷就可以完成。
已複製!app.component('clock-point', { data() { return { textCss: { pointRotate: '', startRotate: '', }, } }, props: ['z'], template: `<div v-if='(z % 3 !== 0)' class="clock-point" :style="{ 'transform': pointRotate }"></div> <div v-else-if='(z % 6 !== 0)' class='star' :style="{ 'transform': pointRotate }"> <div class='star-top'></div> <div class='star-bottom'></div> </div>`, created() { this.pointRotate = `rotate(${this.z * 5}deg)` if (this.z === 1) { this.startRotate = `rotate(${this.z * 15}deg)` } else { this.startRotate = `rotate(${this.z * 30}deg)` } }, })
2F — 時鐘刻度 Component
針的部分按照設計稿,使用偽元素就可以了,秒針的部分比較困難,網上有很多大神同學的解法,弱弱的我表示看不懂@@,我是強行拼湊出來的,就不獻醜了~
時間處理#
已複製!const app = Vue.createApp({ data() { return { secondDeg: 0, minDeg: 0, hourDeg: 0, } }, methods: { setTime() { const data = new Date() // 抓取時間 const second = data.getSeconds() const min = data.getMinutes() const hour = data.getHours() this.setClock(second, min, hour) }, setClock(second, min, hour) { this.secondDeg = second * 6 // 360度 / 60秒 this.minDeg = min * 6 + (second * 6) / 60 // 360度 / 60秒 this.hourDeg = hour * 30 + (min * 30) / 60 // 360度 / 12小時 }, }, mounted() { this.setTime() setInterval(this.setTime, 1000) }, })
2F — 時鐘 Vue Code
這裡我使用兩個 function,抓取時間後再呼叫另一個function 將時間換成角度,最後在 created 的地方使用 setInterval() 設定間隔1秒不斷執行,就大功告成了~
這裡將 setInterval() 放在 mounted() 中是要確保程式是在元件掛載後再開始執行。
除了原本的計算度數以外,額外再增加秒針、分針的執行程度,來讓分針和時針慢慢移動,可以讓時鐘看起來更仿真。
除了 setTimeout() 和 setInterval() 兩種方式以外,還可以使用這種方式Window.requestAnimationFrame() 來完成,不需要設定時間,會隨著使用者硬體的支援頻率來更新畫面~詳細介紹可以參考 Alex 老師的講解和 MDN文件
最後只要傳上 GigHub Page 就完成了挑戰了(灑花)
心得#
Photo by Samantha Gades on Unsplash
這次的版面比第一關難上許多,除了要使用 transform 來完成,還第一次用上了 attr() 和 CSS變數 來完成綁定,算是很實用且意外的收穫!
我絕對不會說,我今天才知道原來偽元素的 content 屬性,原來是可以拿來插入內容。