iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Modern Web

30天30個前端任務系列 第 11

#11. Color theme switcher + Clock(原生JS版)

#11. 白天/夜間模式切換+時鐘顯示

這次作品的靈感是來自這個dribbble
CodePen:
https://codepen.io/zyrxdkoz/pen/dyRWKZM

實作邏輯

clock部分

  1. 在一個div當中切出時針、分針、秒針、中心位置(包含外圈和圓心)。
  2. 運用trasform rotate方法轉動上時鐘元件。
  3. 運用date API取出所有時間單位,帶入2.的css屬性來渲染

色彩模式切換

  1. 在root建立css變數
  2. 運用dom api在中加上class選擇器'dark',看是否要套用dark mode的css變數。

讓我們來看程式碼(值得特別注意的地方會加上備註):

Html部分

<!DOCTYPE html>
// 會在html加上dark選擇器
<html lang="en">
  <head>
    // 略
  </head>
  <body>

    <button class="toggle">Dark mode</button>

    <div class="clock-container">
    // 就跟真正的時鐘一樣有不同的元件。
      <div class="clock">
        <div class="needle hour"></div>
        <div class="needle minute"></div>
        <div class="needle second"></div>
        <div class="center-point"></div>
      </div>

      <div class="time"></div>
      <div class="date"></div>
    </div>

    <script src="script.js"></script>
  </body>
</html>

CSS部分 (節錄部分)


:root {
  --primary-color: #000;
  --secondary-color: #fff;
}

html {
  transition: all 0.5s ease-in;
}

html.dark {
  --primary-color: #fff;
  --secondary-color: #333;
  background-color: #111;
  color: var(--primary-color);
}

.clock-container {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}

.clock {
  position: relative;
  width: 200px;
  height: 200px;
}

.needle {
  background-color: var(--primary-color);
  position: absolute;
  top: 50%;
  left: 50%;
  height: 65px;
  width: 3px;
  // 設定元素變化的原點:底部中間位置
  transform-origin: bottom center;
  transition: all 0.5s ease-in;
}
// 運用trasform屬性控制指針的定位和旋轉行為
.needle.hour {
  transform: translate(-50%, -100%) rotate(0deg);
}

.needle.minute {
  transform: translate(-50%, -100%) rotate(0deg);
  height: 100px;
}

.needle.second {
  transform: translate(-50%, -100%) rotate(0deg);
  height: 100px;
  background-color: #e74c3c;
}

// 指針中心
.center-point {
  background-color: #e74c3c;
  width: 10px;
  height: 10px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}

// 用偽元素做出時鐘的中心部件(會依照色彩模式變換顏色)
.center-point::after {
  content: '';
  background-color: var(--primary-color);
  width: 5px;
  height: 5px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}

Javascript部分

const hourEl = document.querySelector('.hour')
const minuteEl = document.querySelector('.minute')
const secondEl = document.querySelector('.second')
const timeEl = document.querySelector('.time')
const dateEl = document.querySelector('.date')
const toggle = document.querySelector('.toggle')

const days = [
  'Sunday',
  // 略
]
const months = [
  'Jan',
  // 略
  'Dec',
]

// 白天/夜晚模式切換,單純用classList的remove、add來實現
toggle.addEventListener('click', (e) => {
  const html = document.querySelector('html')
  if (html.classList.contains('dark')) {
    html.classList.remove('dark')
    e.target.innerHTML = 'Dark mode'
  } else {
    html.classList.add('dark')
    e.target.innerHTML = 'Light mode'
  }
})

// 這個函式可以在一個範圍內劃定要跑的階數。
const scale = (num, in_min, in_max, out_min, out_max) => {
  return ((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min
}

function setTime() {
  // 用new運算子建立一個Date物件,
  const time = new Date()
  const month = time.getMonth()
  const day = time.getDay()
  const date = time.getDate()
  const hours = time.getHours()
  // 讓小時單位為12小時制
  const hoursForClock = hours >= 13 ? hours % 12 : hours
  const minutes = time.getMinutes()
  const seconds = time.getSeconds()
  // 顯示PM或AM
  const ampm = hours >= 12 ? 'PM' : 'AM'
  
  // 以下時針、分針、秒針都帶入scale函式來rotate。
  hourEl.style.transform = `translate(-50%, -100%) rotate(${scale(
    hoursForClock, // 參考的數值
    0,  // 參考數值的變化範圍(最小)
    12, // 參考數值的變化範圍(最大)
    0, // 輸出數值的變化範圍(最小)
    360 // 輸出數值的變化範圍(最大)
  )}deg)`
  
  minuteEl.style.transform = `translate(-50%, -100%) rotate(${scale(
    minutes,
    0,
    60,
    0,
    360
  )}deg)`
  
  secondEl.style.transform = `translate(-50%, -100%) rotate(${scale(
    seconds,
    0,
    60,
    0,
    360
  )}deg)`

  // 讓分鐘單位的顯示為十進位,1 => 01
  timeEl.innerHTML = `${hoursForClock}:${
    minutes < 10 ? `0${minutes}` : minutes
  } ${ampm}`
  
  dateEl.innerHTML = `${days[day]}, ${months[month]} <span class="circle">${date}</span>`
}

setTime()

// 每一秒鐘執行一次setTime函式,啟動時鐘。
setInterval(setTime, 1000)


上一篇
#10. Drag & Drop(原生JS版)
下一篇
#12. Drawing App(原生JS版)
系列文
30天30個前端任務19

尚未有邦友留言

立即登入留言