新手 JS 地下城 2F — 時鐘

3 個月前(已編輯)紀錄

六角學院 JS 地下城 - 2F 時鐘

新手 JS 地下城 2F  —  時鐘#

新手 JS 地下城 2F  —  時鐘

2F — 時鐘

這篇是六角學院的 JavaScript 題目篇- 🐲 新手 JS 地下城2F Boss 關卡「時鐘」攻略過程心得。

順利攻略完第一關後,決定馬上來挑戰一下第二關,這一次還多了特定技術的考驗,既然都踏入地下城了,當然要來挑戰看看囉!


BOSS 弱點#

  1. 【特定技術】需使用 JS 原生語法的 getDate() 撈取時間,不可用套件
  2. 【特定技術】需使用 JS 原生語法的 setTimeout()setInterval(),持續讓秒針、分針、時針能夠以台北時區移動
  3. 【特定技術】介面請全部用 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 以外,其他屬性的使用皆實驗階段。

詳細請看 MDN 文件

這樣數字就會跑出來了~

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 就完成了挑戰了(灑花)

Github repo

Demo


心得#

Photo by Samantha Gades on Unsplash

這次的版面比第一關難上許多,除了要使用 transform 來完成,還第一次用上了 attr()CSS變數 來完成綁定,算是很實用且意外的收穫!

我絕對不會說,我今天才知道原來偽元素的 content 屬性,原來是可以拿來插入內容