JavaScript 2/30: JS & CSS Clock

JavaScript30 爲Wes Bos推出的一項爲期30天的挑戰,旨在幫助人們用純JavaScript來實現效果,初學者若想在JS方面快速精進,不妨一試。javascript

實現效果

利用JS及CSS模擬時鐘,時、分、秒針實時擺動,例如如今是14:36,時鐘效果以下:css

查看個人 Demo代碼html

解題思路

  • 獲取時、分、秒針;
  • 獲取當前時間;
  • 計算當前時間下,時、分、秒針分別所需旋轉角度;
  • 調整當前時間下,指針在錶盤的位置。

頁面基礎佈局

<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部分代碼

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;
      /* 適用於flex容器,彈性盒子元素在該行的側軸(縱軸)上居中放置。 */
    }

    .clock {
      width: 30rem;
      height: 30rem;
      border: 20px solid white;
      border-radius: 50%;
      /*向 div 元素添加圓角邊框:*/
      margin: 50px auto;
      position: relative;
      padding: 2rem;
      box-shadow: /*向div元素添加一個或多個陰影,逗號分隔*/
      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%;/* 該屬性能夠用來更改元素變形的原點,在這裏可設置旋轉的中心點,初始值爲【50%,50%,0】。*/
      /* transform: rotate(90deg); */
      transition: all 0.05s;
      transition-timing-function: ease-in-out;
      /* 根據時間的推動去改變屬性值的變換速率。*/
    }
複製代碼
備註:

涉及的小知識點已經在註釋中說起,不展開解釋。有關CSS3 Transition的疑問,詳見該網站。有關transform-origin的疑問,可參見該網站屬性演示,能夠幫助你更好理解。java

JS部分代碼

const HourHand = document.querySelector('.hour-hand');
    const MinHand = document.querySelector('.min-hand');
    const SecondHand = document.querySelector('.second-hand');
    
    function setDate() {
      const now = new Date();

      const sec = now.getSeconds();
      const secdeg = (sec / 60) * 360 + 90;
      SecondHand.style.transform = `rotate(${secdeg}deg)`;

      const min = now.getMinutes();
      const mindeg = ((min / 60) * 360) + ((sec / 60) * 6 )+ 90;
      MinHand.style.transform = `rotate(${mindeg}deg)`;

      const hour = now.getHours();
      const hourdeg = ((hour / 12) * 360) + ((min / 60) * 30) + 90;
      HourHand.style.transform =`rotate(${hourdeg}deg)`;
    }
    
     setDate() ;
     setInterval(setDate,1000);
複製代碼

JS部分解析思路(以秒針爲例)

  • 獲取秒針節點
const SecondHand = document.querySelector('.second-hand');
複製代碼
  • 獲取當前時間爲幾秒
const now = new Date();
      const sec = now.getSeconds();
複製代碼
  • 計算當前時間下,秒針旋轉角度
const secdeg = (sec / 60) * 360 + 90;
複製代碼
  • 利用transform: rotate(deg)特性,調整秒針在表面內擺動位置
SecondHand.style.transform = `rotate(${secdeg}deg)`;
複製代碼
  • 設置定時器,每秒調用一次setDate()函數
setDate();
     setInterval(setDate, 1000); 
複製代碼

延伸思考

觀察時鐘,咱們會發現一個小bug,當秒針轉動一圈完畢,59秒至60秒時,會出現一次大回彈,角度值的變化爲 444°-> 90°,逆時針回彈,0秒至1秒時,恢復順時針旋轉,角度值變化爲90° ->96°,再恢復原狀,這是transition特性帶來的偏差。css3

改進方法以下

第一種方法:

在發生跳頓的角度值處,將 CSS 的 transition 屬性去掉,逆時針回彈的過程將在瞬間完成,可忽略不計。git

const HourHand = document.querySelector('.hour-hand');
    const MinHand = document.querySelector('.min-hand');
    const SecondHand = document.querySelector('.second-hand');

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

      const sec = now.getSeconds();
      const secdeg = (sec / 60) * 360 + 90;

      const min = now.getMinutes();
      const mindeg = ((min / 60) * 360) + ((sec / 60) * 6) + 90;

      const hour = now.getHours();
      const hourdeg = ((hour / 12) * 360) + ((min / 60) * 30) + 90;

      if (secdeg === 90) {
        SecondHand.style.transition = 'all 0s'
      }
      // 在指針即將出現大擺動的那一刻,去除transition屬性
      else {
        SecondHand.style.transition = 'all 0.05s'
      }

      SecondHand.style.transform = `rotate(${secdeg}deg)`;
      MinHand.style.transform = `rotate(${mindeg}deg)`;
      HourHand.style.transform = `rotate(${hourdeg}deg)`;
    }
    
    setDate();
    setInterval(setDate, 1000);
複製代碼
第二種方法是:

解決思路是,按照以前的代碼所寫,每秒都會new一個Date,若是讓秒針角度保持一直增加的狀態,而非每次從新計算,就不會出現這樣的問題了。github

const HourHand = document.querySelector('.hour-hand');
    const MinHand = document.querySelector('.min-hand');
    const SecondHand = document.querySelector('.second-hand');
    let secdeg = 0; let mindeg = 100; let hourdeg = 1;
    
    function setDate() {
      const now = new Date();

      const sec = now.getSeconds();
      secdeg = (sec / 60) * 360 + 90;

      const min = now.getMinutes();
      mindeg = ((min / 60) * 360) + ((sec / 60) * 6) + 90;

      const hour = now.getHours();
      hourdeg = ((hour / 12) * 360) + ((min / 60) * 30) + 90;}
   
      function updateDate() {
        secdeg += (1 / 60) * 360;
        mindeg += ((1 / 60) / 60) * 360;
        hourdeg += (((1 / 60) / 60) / 12);

        SecondHand.style.transform = `rotate(${secdeg}deg)`;
        MinHand.style.transform = `rotate(${mindeg}deg)`;
        HourHand.style.transform = `rotate(${hourdeg}deg)`;
      }

      setDate();
      setInterval('updateDate()', 1000);
複製代碼
相關文章
相關標籤/搜索