Day02 - JavaScript + CSS Clock

Day02 - JavaScript + CSS Clock

做者:©liyuechun
簡介:JavaScript30Wes Bos 推出的一個 30 天挑戰。項目免費提供了 30 個視頻教程、30 個挑戰的起始文檔和 30 個挑戰解決方案源代碼。目的是幫助人們用純 JavaScript 來寫東西,不借助框架和庫,也不使用編譯器和引用。如今你看到的是這系列指南的第 2 篇。完整指南在 從零到壹全棧部落javascript

簡介

次日的練習是用JS+CSS模擬時鐘效果。css

效果以下:html

clock

實現以上模擬時鐘的效果,大體思路和解決方案以下:java

  • 分別獲取到當前時間的時、分、秒。git

  • 經過時分秒對一圈360度,進行映射,肯定每個指針所需旋轉的角度。github

  • 經過CSS的transform:rotate(deg),來實時的調整指針在鍵盤中的位置。微信

頁面佈局

<div class="clock">
    <div class="clock-face">
      <div class="hand hour-hand"></div>
      <div class="hand min-hand"></div>
      <div class="hand second-hand"></div>
    </div>
  </div>

CSS樣式

<style>
    html {
      background: #018DED url(http://unsplash.it/1500/1000?image=881&blur=50);
      background-size: cover;
      font-family: 'helvetica neue';
      text-align: center;
      font-size: 10px;
    }

    body {
      margin: 0;
      font-size: 2rem;
      display: flex;
      flex: 1;
      min-height: 100vh;
      align-items: center;
    }

    .clock {
      width: 30rem;
      height: 30rem;
      border: 20px solid white;
      border-radius: 50%;
      margin: 50px auto;
      position: relative;
      padding: 2rem;
      box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.1),
      inset 0 0 0 3px #EFEFEF,
      inset 0 0 10px black,
      0 0 10px rgba(0, 0, 0, 0.2);
    }

    .clock-face {
      position: relative;
      width: 100%;
      height: 100%;
      transform: translateY(-3px);
      /* account for the height of the clock hands */
    }

    .hand {
      width: 50%;
      height: 6px;
      background: black;
      position: absolute;
      top: 50%;
      transform-origin: 100%;
      transform: rotate(90deg);
      transition: all 0.05s;
      transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);
    }
  </style>

涉及到的特性:框架

  • transform-oragin函數

調整指針的初始位置以及旋轉的軸點:transform-oragin佈局

transform-oragin: 100%; //初始化使三個指針所有指向12時
  • transform: rotate()

設置旋轉角度

  • transition

transition: all //0.05s;設置動畫時間爲0.05秒
  • transition-timing-function: cubic-bezier(x, x, x, x)

設置 transition-time-function 的值,以實現秒針「滴答滴答」的效果。此外注意 transform 中的 rotate (旋轉)屬性由角度來控制,能夠試着在頁面上修改這個參數來查看效果。

JS代碼

<script>
    const secondHand = document.querySelector('.second-hand');
    const minsHand = document.querySelector('.min-hand');
    const hourHand = document.querySelector('.hour-hand');

    function setDate() {
      const now = new Date();

      const seconds = now.getSeconds();
      const secondsDegrees = ((seconds / 60) * 360) + 90;
      secondHand.style.transform = `rotate(${secondsDegrees}deg)`;

      const mins = now.getMinutes();
      const minsDegrees = ((mins / 60) * 360) + ((seconds / 60) * 6) + 90;
      minsHand.style.transform = `rotate(${minsDegrees}deg)`;

      const hour = now.getHours();
      const hourDegrees = ((hour / 12) * 360) + ((mins / 60) * 30) + 90;
      hourHand.style.transform = `rotate(${hourDegrees}deg)`;
    }

    setInterval(setDate, 1000);

    setDate();
  </script>
  • 獲取秒針、分鐘、小時節點

const secondHand = document.querySelector('.second-hand');
    const minsHand = document.querySelector('.min-hand');
    const hourHand = document.querySelector('.hour-hand');
  • 獲取當前時間秒、分、小時

const now = new Date();
const seconds = now.getSeconds();
const mins = now.getMinutes();
const hour = now.getHours();
  • 計算秒、分、小時角度

const secondsDegrees = ((seconds / 60) * 360) + 90;
const minsDegrees = ((mins / 60) * 360) + ((seconds / 60) * 6) + 90;
const hourDegrees = ((hour / 12) * 360) + ((mins / 60) * 30) + 90;
  • 根據角度設置樣式

secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
minsHand.style.transform = `rotate(${minsDegrees}deg)`;
hourHand.style.transform = `rotate(${hourDegrees}deg)`;
  • 設置定時器,每秒調用一次setDate函數

setInterval(setDate, 1000);

延伸思考

此處存在一個小瑕疵,當秒針旋轉一圈以後回到初始位置,開始第二圈旋轉,角度值的變化時 444° → 90° → 96° .... 這個過程當中,指針會先逆時針從 444° 旋轉至 90°,再繼續咱們指望的順時針旋轉,因爲秒針變換時間只有 0.05s,因此呈現的效果就是秒針閃了一下,若是想要觀察細節,能夠將 .second 設爲 transition: all 1s,效果以下所示:

要解決這個問題,目前找到了兩種解決辦法:

  • 第一種

<script>
    const secHand = document.querySelector('.second-hand');
    const minHand = document.querySelector('.min-hand');
    const hourHand = document.querySelector('.hour-hand');

    function setDate() {
      const date = new Date();

      const second = date.getSeconds();
      const secondDeg = (90 + (second / 60) * 360);

      const min = date.getMinutes();
      const minDeg = (90 + (min / 60) * 360);

      const hour = date.getHours();
      const hourDeg = (90 + (hour / 12) * 360 + (min / 12 / 60) * 360); // 加入分鐘所佔的時間,使時針能夠緩慢地移動


      //解決指針跳頓問題【第一種方法】
      //在發生跳頓的角度值處,將 CSS 的 `transition` 屬性去掉
      if (secondDeg === 90) {
        secHand.style.transition = 'all 0s';
      } else {
        secHand.style.transition = 'all 0.05s';
      }

      if (minDeg === 90) {
        minHand.style.transition = 'all 0s';
      } else {
        minHand.style.transition = 'all 0.1s';
      }


      secHand.style.transform = `rotate(${ secondDeg }deg)`;
      minHand.style.transform = `rotate(${ minDeg }deg)`;
      hourHand.style.transform = `rotate(${ hourDeg }deg)`;

    }

    setInterval(setDate, 1000);

    setDate();
  </script>
  • 第二種

<script>
    const secondHand = document.querySelector('.second-hand');
    const minsHand = document.querySelector('.min-hand');
    const hourHand = document.querySelector('.hour-hand');

    let secondDeg = 0;
    let minDeg = 0;
    let hourDeg = 0;

    function initDate() {
      const date = new Date();
      const second = date.getSeconds();
      secondDeg = 90 + (second / 60) * 360;
      const min = date.getMinutes();
      minDeg = 90 + (min / 60) * 360 + ((second / 60) / 60) * 360;
      const hour = date.getHours();
      hourDeg = 90 + (hour / 12) * 360 + ((min / 60) / 12) * 360 + (((second / 60) / 60) / 12) * 360;
    }

    function updateDate() {
      secondDeg += (1 / 60) * 360;
      minDeg += ((1 / 60) / 60) * 360;
      hourDeg += (((1 / 60) / 60) / 12);

      secondHand.style.transform = `rotate(${ secondDeg }deg)`;
      minsHand.style.transform = `rotate(${ minDeg }deg)`;
      hourHand.style.transform = `rotate(${ hourDeg }deg)`;
    }

    initDate();
    setInterval(updateDate, 1000);
  </script>

既然引起問題的是角度的大小變化,那就能夠對這個值進行處理。此前的代碼中,每秒都會從新 new 一個 Date 對象,用來計算角度值,但若是讓這個角度值一直保持增加,也就不會出現逆時針迴旋的問題了。

源碼下載

Github Source Code

社羣品牌:從零到壹全棧部落
定位:尋找共好,共同窗習,持續輸出全棧技術社羣
業界榮譽:IT界的邏輯思惟
文化:輸出是最好的學習方式
官方公衆號:全棧部落
社羣發起人:春哥(從零到壹創始人,交流微信:liyc1215)
技術交流社區:全棧部落BBS
全棧部落完整系列教程:全棧部落完整電子書學習筆記

關注全棧部落官方公衆號,每晚十點接收系列原創技術推送
相關文章
相關標籤/搜索