React 實現一個時鍾


最終效果

其實主要難點在於最左邊的小時鍾

指針的實現方式很簡單,就是通過絕對定位將指針移到中間,然后以下邊中間的位置為圓心旋轉即可。代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
    .clock-wrapper {
        background-color: blue;
        height: 200px;
        width: 200px;
        position: relative;
    }
    .pointer {
        height: 80px;
        width: 6px;
        background-color: silver;
        position: absolute;
        top: 20px;
        left: 97px; /* 100 - 6/2 */
        transform: rotateZ(30deg);
        transform-origin: center bottom;
    }
    </style>
</head>
<body>
    <div class="clock-wrapper">
        <div class="pointer"></div>
    </div>
</body>
</html>

效果

 

秒針轉起來的效果也很簡單,通過定時器setInterval每隔一秒更新秒針的角度。

setInterval(() => {
            let secAngle = new Date().getSeconds() * 6
            let pointer = document.getElementsByClassName('pointer')[0]
            pointer.style.transform = `rotateZ(${secAngle}deg)`
        }, 1000)

現在就可以看到指針一跳一跳的了。但是呢,我希望指針平緩的走,那么可以設置CSS的 transition 屬性

transition: all linear 1s;

 

安靜的等待1s 會發現,當秒針從59到60的時候,會反向旋轉。因為此時角度是變小的,360->0,所以考慮當指針剛好走一圈的那一秒,去除 transition 屬性。

這樣雖然不會倒轉了,但是那一秒還是會蹦一下。

於是又想到每100ms更新一次,這樣到360度時蹦的那一下就不明顯了。感覺沒有直接解決問題,是繞開了。。

這樣一個會圍繞圓心轉的指針就做完了。代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
    .clock-wrapper {
        background-color: blue;
        height: 200px;
        width: 200px;
        position: relative;
    }
    .pointer {
        height: 80px;
        width: 6px;
        background-color: silver;
        position: absolute;
        top: 20px;
        left: 97px; /* 100 - 6/2 */
        transform-origin: center bottom;
    }
    </style>
</head>
<body>
    <div class="clock-wrapper">
        <div class="pointer"></div>
    </div>
    <script>
        function getAngle() {
            let date = new Date()
            let secAngle = date.getSeconds() * 6 + date.getMilliseconds() * 6 / 1000;
            return secAngle;
        }
        window.onload = () => {
            let pointer = document.getElementsByClassName('pointer')[0];
            pointer.style.transform = `rotateZ(${getAngle()}deg)`;
            let timer = setInterval(() => {
                let secAngle = getAngle();
                pointer.style.transform = `rotateZ(${getAngle()}deg)`;
                if (!secAngle) {
                    pointer.style.transition = null;
                } else {
                    pointer.style.transition = 'all linear 100ms';
                }
            }, 100);
        }
    </script>
</body>
</html>

 

現在的問題是 表盤的刻度。實現12個小豎線,然后分別旋轉。雖然我沒有less不可以使用for循環,但是react可以循環啊……定位還是絕對定位,和指針一樣。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Hello World</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  <style>
  .clock-wrapper {
    background-color: blue;
    height: 200px;
    width: 200px;
    border-radius: 100px;
    position: relative;
  }
  .grad {
    height: 10px;
    width: 4px;
    background-color: #fff;
    position: absolute;
    left: 98px;
    top: 5px;
    transform-origin: center 95px;
  }
  </style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
  class Clock extends React.Component {
    render() {
      let hourArr = [...new Array(12).keys()]
      let grad = hourArr.map((item) => {
        return <div key={item} className="grad" style={{transform: `rotateZ(${item*30}deg)`}}></div>
      })

      return (
        <div className="clock-wrapper">
          {grad}
        </div>
      )
    }
  }

  ReactDOM.render(
    <Clock />,
    document.getElementById('root')
  );

</script>
</body>
</html>

效果:

 

這樣完全沒有難點了(本來就沒有好吧……

完整代碼如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Hello World</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  <style>
    .deTime {
      position: relative;
      height: 50px;
      width: 300px;
      padding: 5px;
      background: linear-gradient(to bottom, #0071ff, #00b1ff);
      display: flex;
      font-family: TrebuchetMS,Rotobo,"Microsoft YaHei",sans-serif;
    }

    .deTime .container {
      position: relative;
      height: 50px;
      width: 50px;
      border-radius: 150px;
      box-shadow: #353535 0px 0px 1px 0px;
      background: radial-gradient(#0040ff, #6adbff);
    }

    .deTime .second {
      height: 20px;
      width: 1px;
      top: 5px;
      left: 24px;
      background-color: #ff6363;
    }

    .deTime .minute {
      height: 16px;
      width: 2px;
      top: 9px;
      left: 24px;
      background-color: #8e8e8e;
    }

    .deTime .hour {
      height: 12px;
      width: 2px;
      top: 13px;
      left: 24px;
      background-color: #8e8e8e;
    }

    .deTime .second, .deTime .minute, .deTime .hour {
      position: absolute;
      transform-origin: center bottom;
      box-shadow: 0px 0px 2px 0px #000;
    }

    .deTime .center {
      width: 2px;
      height: 2px;
      border-radius: 1px;
      background-color: #ffffff;
      box-shadow: 0px 0px 3px 1px #8c8c8c;
      position: absolute;
      top: 24px;
      left: 24px;
    }

    .deTime .time {
      line-height: 50px;
      font-size: 36px;
      color: #fff;
      margin-left: 15px;
    }

    .deTime .time span {
      font-size: 22px;
    }

    .deTime .date {
      font-size: 13px;
      color: #fff;
      display: flex;
      flex-flow: column;
      margin-left: 15px;
      padding: 6px 0;
    }

    .deTime .date > div {
      flex-basis: 50%;
    }

    .grad {
      height: 2px;
      width: 1px;
      background-color: #fff;
      position: absolute;
      left: 25px;
      top: 1px;
      transform-origin: center 24px;
    }
  </style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
  class DeTime extends React.Component {
  TRANSITION = '100ms linear';
  NUMBER_TRANSLATION = ['', '', '', '', '', '', ''];

  constructor() {
    super()
    this.state = {
      hourAngle: 0,
      minAngle: 0,
      secAngle: 0,
      transition: this.TRANSITION
    }
  }

  updateTime() {
    let date = new Date()

    let secAngle = (date.getSeconds() + date.getMilliseconds() / 1000) * 6;
    let minAngle = date.getMinutes() * 6 + secAngle / 60;
    let hourAngle = (date.getHours() % 12) * 30 + minAngle / 12;

    let transition = this.TRANSITION
    //  當秒針走到 0 的時候 角度其實是變小了 所以會倒着轉 需要暫時刪除 transition
    if (this.state.secAngle > secAngle) transition = null;

    this.setState({
      hourAngle: hourAngle,
      minAngle: minAngle,
      secAngle: secAngle,
      transition: transition
    })
  }

  componentWillMount() {
    this.updateTime();

    this.timer = setInterval(() => { this.updateTime() }, 100);
  }

  componentWillUnmount() {
    this.timer && clearTimeout(this.timer);
  }

  leadingZero(number) {
    return number < 10 ? '0' + number : number
  }


  render() {
    let hourArr = [...new Array(12).keys()]
    let grad = hourArr.map((item) => {
      return <div key={item} className="grad" style={{transform: `rotateZ(${item*30}deg)`}}></div>
    })
    let state = this.state
    let now = new Date()

    return (
      <div className="deTime">
        <div className="container">
          {grad}
          <div className="minute" style={{transform:  'rotateZ('+state.minAngle+'deg)'}}></div>
          <div className="hour" style={{transform:  'rotateZ('+state.hourAngle+'deg)'}}></div>
          <div className="second" style={{transition: state.transition, transform:  'rotateZ('+state.secAngle+'deg)'}}></div>
          <div className="center"></div>
        </div>
        <div className="time">
          {this.leadingZero(now.getHours())}:{this.leadingZero(now.getHours())}<span> {this.leadingZero(now.getSeconds())}</span>
        </div>
        <div className="date">
          <div>星期{this.NUMBER_TRANSLATION[now.getDay()]}</div>
          <div>{now.getFullYear()}年{now.getMonth()}月{now.getDate()}日</div>
        </div>
      </div>
    )
  }
}

  ReactDOM.render(
    <DeTime/>,
    document.getElementById('root')
  );

</script>
</body>
</html>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM