本文為挑戰六角學院 JS 新手地下城 F2 「時鐘」的筆記。
作品
Lynn 的 JS 新手地下城 作品列表 / 2F:時鐘
關卡重點
問題解決流程
整體結構
拆解的方式如下:
- 背景
- 時鐘(圓角矩形)
- 鐘面(有邊線的圓)
- 時針(白色矩形)
- 分針(橘色圓角矩形)
- 秒針
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <main class="main"> <div class="clock"> <div class="plate"></div> <div class="hour-hand"><div class="dark"></div></div> <div class="second-hand"> <div class="green-circle"><div class="black-circle"></div></div> <div id="line1" class="line"></div> <div id="line2" class="line"></div> <div id="line3" class="line"></div> <div id="line4" class="line"></div> <div id="line5" class="line"></div> </div> <div class="minute-hand"><div class="line"></div><div class="circle"></div> </div> </div> </main>
|
時鐘
.clock 的功能除了當個圓角矩形以外,也是鐘面和三個指針用來定位的父元素。
1 2 3 4 5 6 7 8 9 10 11 12
| .clock { width: 350px; height: 350px; background-color: #3d5a45; border-radius: 72px; display: flex; justify-content: center; align-items: center; position: relative; }
|
鐘面
底的部分是一個有邊線的圈圈:
1 2 3 4 5 6 7 8
| .clock .plate { width: 310px; height: 310px; border: 3px solid #212f0b; background-color: #293B29; border-radius: 50%; position: relative; }
|
鐘面上的點點、星星、線及文字,因為是持續旋轉的關係,用 JavaScript 產生(雖然也可以用 CSS 暴力製作)。
但在旋轉前要在 CSS 裡做好準備工作:
- 先利用絕對定位將元素放在 0 度的位置
- 設定正確的 transform-origin
- 用 transform: rotate() 旋轉時,預設會以元素的中心點當軸心,但我需要以時鐘的圓心來旋轉
點點和線的部分比較容易:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| .clock .plate .dot { width: 2px; height: 2px; background-color: #fff; border-radius: 50%; position: absolute; top: 31px; left: 154px; transform-origin: 1px 124px; }
.clock .plate .line { width: 1px; height: 24px; background-color: #ff7600; position: absolute; top: 20px; left: 154.5px; transform-origin: 0.5px 135px; font-family: Helvetica, sans-serif; }
|
星星的部分,由於不規則,這一關的設計稿也只有提供各個部件的 SVG 或圖檔,所以無法直接抓規格。
我找到這篇文章:運用 clip-path 的純 CSS 形狀變換,利用裡面提供的 Clippy 這個網站,拉出差不多的星星圖案。
1 2 3 4 5 6 7 8 9 10 11 12
| .clock .plate .star { width: 8px; height: 8px; background-color: #ff7600; clip-path: polygon(50% 0, 66% 33%, 100% 50%, 66% 66%, 50% 100%, 33% 66%, 0 50%, 33% 33%); position: absolute; top: 28px; left: 151px; transform-origin: 4px 127px; }
|
接著就可以用 JavaScript 一邊旋轉,一邊放入新的元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| let plate = document.querySelector('.clock .plate');
for (let i = 1; i <= 72; i++) {
let node = document.createElement('div');
let degree = 360 / 72 * i; node.style.transform = `rotate(${degree}deg)`;
if (i % 6 == 0) { node.classList.add('line'); } else if (i % 3 == 0) { node.classList.add('star'); } else { node.classList.add('dot'); } plate.appendChild(node); }
|
再來還有文字的部分。
文字很明顯是跟線對齊,所以可以放在線的元素裡面;由於放線的時候,字會跟著線一起轉,所以要逆向轉回來。
1 2 3 4 5 6 7 8
| if (i % 6 == 0) { node.classList.add('line'); node.innerHTML = ` <span class="pm" style="transform:rotate(${-degree}deg);">${i / 6 + 12}</span> <span class="am" style="transform:rotate(${-degree}deg);">${i / 6}</span>`; }
|
再利用絕對定位,把文字放到線的上下位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| .clock .plate .line .pm { width: 12px; height: 12px; display: flex; justify-content: center; align-items: center; color: #FFF; font-size: 10px; position: absolute; transform-origin: 6px 6px; left: -5.5px; top: -14px; }
.clock .plate .line .am { width: 12px; height: 12px; display: flex; justify-content: center; align-items: center; color: #FFF; font-size: 10px; position: absolute; transform-origin: 6px 6px; left: -5.5px; bottom: -14px; }
|
這樣鐘面的部分就完成了。
指針
指針的部分也是先個別作出外型,然後設定好位置和軸心。
時針和分針的部分比較容易,只是內部包含一些小裝飾;其中時針的深色部分原本應該是透明的效果,但我讓時針在最底層,用與背景同樣的顏色,就看不出差異。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| .clock .hour-hand { width: 8px; height: 72px; background-color: #FFF; border-radius: 0 0 4px 4px; position: absolute; left: 171px; top: 107px; transform-origin: 4px 68px; }
.clock .hour-hand .dark { background-color: #293B29; width: 2px; height: 24px; margin: 3px; }
.clock .minute-hand { width: 8px; height: 96px; border-radius: 4px; background-color: #ff7600; position: absolute; left: 171px; top: 83px; transform-origin: 4px 92px; display: flex; justify-content: center; align-content: flex-end; flex-wrap: wrap; }
.clock .minute-hand .line { width: 1px; height: 32px; background-color: #FFF; }
.clock .minute-hand .circle { margin: 0px 2px 2px 2px; width: 4px; height: 4px; border-radius: 50%; background-color: #FFF; }
|
秒針的長相就有點複雜,其中圓的部分還好,可以從 SVG 看出尺寸;但是 path 的部分想看懂的話,可能要作到天荒地老。
1 2 3 4 5 6 7 8 9
| <g xmlns="http://www.w3.org/2000/svg" transform="translate(646.618 400) rotate(180)"> <g class="a" transform="translate(636 274)"> <circle class="d" cx="4" cy="4" r="4"/><circle class="e" cx="4" cy="4" r="3.5"/> </g> <g class="b" transform="translate(638 276)"> <circle class="d" cx="2" cy="2" r="2"/><circle class="e" cx="2" cy="2" r="1.5"/> </g> <path class="c" d="M0,3.61V50l6,8L-6,75l6,7v40" transform="translate(640 278)"/> </g>
|
於是我決定隨興製作(?),線的部分只要有接起來,長得像就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| .clock .second-hand { width: 14px; height: 126px; position: absolute; left: 168px; top: 50px; transform-origin: 7px 125px; }
.clock .second-hand .green-circle { width: 8px; height: 8px; border-radius: 50%; background-color: #b1ff00; display: flex; justify-content: center; align-items: center; position: absolute; left: 3px; }
.clock .second-hand .green-circle .black-circle { width: 4px; height: 4px; border-radius: 50%; border: 1px solid #293b29; }
.clock .second-hand .line { width: 2px; border-radius: 1px; background-color: #b1ff00; position: absolute; left: 6px; transform-origin: 1px 1px; }
.clock .second-hand #line1 { height: 41px; top: 7px; border-radius: 0px 0px 1px 1px; }
.clock .second-hand #line2 { height: 10px; top: 47px; transform: rotate(-45deg); }
.clock .second-hand #line3 { height: 20px; top: 53px; transform: translateX(6px) rotate(45deg); }
.clock .second-hand #line4 { height: 12px; top: 66px; transform: translateX(-7px) rotate(-45deg); }
.clock .second-hand #line5 { height: 53px; top: 73px; border-radius: 1px 1px 0px 0px; }
|
時間
最後才是處理時間的部分,只要抓到現在的時間,再計算角度,讓指針隨著時間旋轉即可( CSS 部分已經設定過軸心)。
其中要注意的是大單位要加上小單位的量才是指針的正確位置(例如 11:30 時的時針應該在 11 和 12 之間)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| let hourHand = document.querySelector('.clock .hour-hand'); let minuteHand = document.querySelector('.clock .minute-hand'); let secondHand = document.querySelector('.clock .second-hand'); setInterval(function () { let currentTime = new Date(); let second = currentTime.getSeconds(); let minute = currentTime.getMinutes() + second / 60; let hour = currentTime.getHours() + minute / 60; hourHand.style.transform = `rotate(${(hour % 12) / 12 * 360}deg)`; minuteHand.style.transform = `rotate(${minute / 60 * 360}deg)`; secondHand.style.transform = `rotate(${second / 60 * 360}deg)`; }, 1000);
|
完成。