2

前言

最近闲着没事, 就翻翻文章看, 突然想到以前写过的一些demo, 其中有一个是iPhone时钟. 不过当时是用jquery写的, 现在也在玩vue了, 所以就想着用vue重写一下.

预览

先看效果图吧 (没有动态图片 T_T):
图片描述

代码

然后是就是代码部分了

模版文件(.vue)

<script>
/**
 * @desc 时钟组件
 */

export default {
  name: 'v-clock',
  props: {
    size: {
      type: Number,
      default: () => 300
    }
  },
  data: () => ({ time: new Date(), isRunning: false }),
  computed: {
    fontSize() {
      const size = this.size / 300 * 16
      return size > 12 ? `${size}px` : '12px'
    },
    scales() {
      const radius = this.size / 2 * 0.8
      const arr = []
      for (let i = 1; i <= 12; i++) {
        const radian = 30 * Math.PI / 180 * i - 0.5 * Math.PI
        const left = radius * Math.cos(radian) + this.size * 0.45
        const top = radius * Math.sin(radian) + this.size * 0.45
        arr.push({
          num: i,
          left: `${left.toFixed(2)}px`,
          top: `${top.toFixed(2)}px`
        })
      }
      return arr
    },
    hourHand() {
      const hourDeg = 30 * (this.time.getHours() % 12)
      const minuteDeg = 0.5 * this.time.getMinutes()
      return `translate(-50%, 0) rotate(${hourDeg + minuteDeg - 180}deg)`
    },
    minuteHand() {
      const minuteDeg = 6 * this.time.getMinutes()
      const secondDeg = 0.1 * this.time.getSeconds()
      return `translate(-50%, 0) rotate(${minuteDeg + secondDeg - 180}deg)`
    },
    secondHand() {
      return `translate(-50%, -10%) rotate(${6 * this.time.getSeconds() - 180}deg)`
    }
  },

  mounted() {
    this.isRunning = true
    const tick = () => {
      if (!this.isRunning) {
        return
      }
      this.time = new Date()
      const timer = setTimeout(() => {
        clearTimeout(timer)
        tick()
      }, 1000)
    }
    tick()
  },
  destroyed() {
    this.isRunning = false
  }
}
</script>

<template>
  <div
    :style="{width:`${size}px`,height:`${size}px`}"
  >
    <div :class="$style['black-dail']">
      <div :class="$style['white-dail']">
        <span
          v-for="v in scales"
          :key="`ds-${v.num}`"
          :class="$style['degree-scale']"
          :style="{left:v.left,top:v.top,fontSize}"
        >{{v.num}}</span>
        <b :class="$style['hour-hand']" :style="{transform:hourHand}"></b>
        <b :class="$style['minute-hand']" :style="{transform:minuteHand}"></b>
        <b :class="$style['second-hand']" :style="{transform:secondHand}"></b>
      </div>
    </div>
  </div>
</template>

<style lang="scss" module>
@import './styles.scss';
</style>

样式文件(.scss)

.black-dail {
  width: 100%;
  height: 100%;
  padding: 5%;
  border-radius: 20%;
  background-color: #000;
  box-sizing: border-box;
}

.white-dail {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-color: #fff;
}

.degree-scale {
  position: absolute;
  display: block;
  // font-size: 16px;
  width: 2em;
  height: 2em;
  text-align: center;
  line-height: 2;
  transform: translate(-50%, -50%);
}

.hour-hand,
.minute-hand,
.second-hand {
  position: absolute;
  top: 50%;
  left: 50%;
}

.hour-hand {
  width: 2.5%;
  height: 30%;
  background-color: #000;
  transform-origin: 50% 0;
  transform: translate(-50%, 0) rotate(0);
  &::after {
    position: absolute;
    top: 0;
    left: 50%;
    display: block;
    content: '';
    width: 200%;
    padding-top: 200%;
    border-radius: 50%;
    background-color: #000;
    transform: translate(-50%, -50%);
  }
}

.minute-hand {
  width: 2%;
  height: 40%;
  background-color: #000;
  transform-origin: 50% 0;
  transform: translate(-50%, 0) rotate(0);
}

$second-color: rgb(255, 74, 74);
.second-hand {
  width: 1%;
  height: 50%;
  background-color: $second-color;
  transform-origin: 50% 10%;
  transform: translate(-50%, -10%) rotate(0);

  &::after {
    position: absolute;
    top: 10%;
    left: 50%;
    display: block;
    content: '';
    width: 300%;
    padding-top: 300%;
    border-radius: 50%;
    background-color: $second-color;
    transform: translate(-50%, -50%);
  }
}

dom部分

这个也没啥好说的了, '黑色的底盘 + 白色圆盘 + 刻度*12 + 时针 + 分针 + 秒针'.
除了刻度的位置是计算出来的之外, 其他的基本用样式就ok了.

逻辑部分

然后来说说逻辑部分的代码.

刻度的位置计算

    scales() {
      const radius = this.size / 2 * 0.8
      const arr = []
      for (let i = 1; i <= 12; i++) {
        const radian = 30 * Math.PI / 180 * i - 0.5 * Math.PI
        const left = radius * Math.cos(radian) + this.size * 0.45
        const top = radius * Math.sin(radian) + this.size * 0.45
        arr.push({
          num: i,
          left: `${left.toFixed(2)}px`,
          top: `${top.toFixed(2)}px`
        })
      }
      return arr
    },

this.size就是这个时钟的尺寸了, 先定义半径radius. arr就是12个刻度的数组.
这里计算各个刻度坐标使用的方法是圆的参数方程,lefttop就相当于坐标轴上的xy.
radian部分需要减去90度, 因为0度刚好是3点钟, 需要往回走到12点钟.
然后是top, left最后要加this.size * 0.45, 因为白色的圆盘的尺寸在样式里面定义的是总大小的90%.

秒针/分针/时针 的旋转角度的计算

this.time既是当前的时间

时针
计算当前的小时后, %12转成12小时制, 每一小时的角度是30度; 当前的分钟, 每一分钟的角度是6度.然后把总的旋转角度加起来. 秒针因为度数太小, 就没有算进去.

分钟和秒针同理. 还有记得减去180度, 这里是因为一开始定义的样式的问题(0度指向6点钟).

计时器

计时器也是简单的每秒钟执行一个回调而已, 要记得组件卸载时关闭计时功能.


以上


darcrand
637 声望20 粉丝