Проведите 1 час, играя в «компасные часы».

Vue.js
Проведите 1 час, играя в «компасные часы».

предисловие

Эффект исходил от программы-заставки: мировое время, что мне показалось довольно интересным, поэтому я решил сам поиграть в нее поздно ночью.

Предварительный просмотр эффекта

онлайн просмотр:компас часы

Скриншот предварительного просмотра:

run.gif

руки вверх

Первый — расположить несколько текстовых элементов по кругу, а затем повернуть круг на определенный угол для достижения эффекта.

Далее сначала сообразите, как расположить текст по кругу.После этого сделать последнее не составит труда.

Расположить текст по кругу

Далее, давайте сначала шаг за шагом реализуем самую крайнюю "вторую", которая имеет в общей сложности 60 элементов от 0 до 59. Во-первых, расположите их в квадратном div.

<template>
  <div class="home">
    <!-- 秒 -->
    <div class="box-wrapper">
      <div class="circle-box" :style="boxStyle('seconds')">
        <span
          v-for="(item, index) in secondTexts"
          :key="item"
        >{{ item }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Home',

  data() {
    return {
      secondTexts: [
        '零零', '一秒', '二秒', '三秒', '四秒', '五秒', '六秒', '七秒', '八秒', '九秒', '十秒',
        '十一秒', '十二秒', '十三秒', '十四秒', '十五秒', '十六秒', '十七秒', '十八秒', '十九秒', '二十秒',
        '二十一秒', '二十二秒', '二十三秒', '二十四秒', '二十五秒', '二十六秒', '二十七秒', '二十八秒', '二十九秒', '三十秒',
        '三十一秒', '三十二秒', '三十三秒', '三十四秒', '三十五秒', '三十六秒', '三十七秒', '三十八秒', '三十九秒', '四十秒',
        '四十一秒', '四十二秒', '四十三秒', '四十四秒', '四十五秒', '四十六秒', '四十七秒', '四十八秒', '四十九秒', '五十秒',
        '五十一秒', '五十二秒', '五十三秒', '五十四秒', '五十五秒', '五十六秒', '五十七秒', '五十八秒', '五十九秒'
      ],
      // 盒子大小
      boxSize: {
        seconds: 580
      }
    }
  },

  methods: {
    // 设置文字外围盒子宽高
    boxStyle(key) {
      return {
        width: this.boxSize[key] + 'px',
        height: this.boxSize[key] + 'px'
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.home {
  height: 100%;
  width: 100%;
  background-color: #000000;
  color: #71767D;
  position: relative;
  min-width: 800px;
  min-height: 660px;
  padding: 20px 0;
  overflow: hidden;
}

.box-wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.circle-box {
  position: relative;
  border: 1px solid red;
}

.circle-box span {
  white-space: nowrap;
  font-size: 14px;
  position: absolute;
}
</style>

Snipaste_2021-06-13_22-27-40.png

Теперь тексты складываются вместе после установки абсолютного позиционирования, а затем располагаются по кругу посредством вычисления позиции.

Snipaste_2021-06-13_22-16-07.png

Из рисунка мы знаем, что для размещения текста на окружности нам необходимо знать абсциссу и ординату элемента текста до точки О (центр окружности), то есть высоту а и длину b , соответствующий верхнему и левому значению.

Мы знаем радиус, то есть половину ширины коробки, r = 580/2 = 290, и угол c = (360/60)*i между каждым элементом и центром окружности.По математической формуле , мы можем найти разницу между значениями a и b. Добавленный код выглядит следующим образом:

<template>
  <div class="home">
    <!-- 秒 -->
    <div class="box-wrapper">
      <div class="circle-box" :style="boxStyle('seconds')">
        <span
          v-for="(item, index) in secondTexts"
          :key="item"
          :style="spanStyle(boxSize.seconds, secondTexts, index)"
        >{{ item }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Home',
  methods: {
    spanStyle(size, texts, i) {
      const r = size / 2 // 半径
      const deg = this.getPerDeg(texts) // 元素平均间隔度数
      const angle = i * deg // 夹角
      const { a, b } = this.getHypotenuse(r, angle)
      const rotateDeg = deg * i // 文字旋转角度
      return {
        top: a + r + 'px',
        left: b + r + 'px',
        transform: `rotate(${rotateDeg}deg)`,
        transformOrigin: '0 0'
      }
    },

    // 元素平均间隔度数
    getPerDeg(texts) {
      return 360 / texts.length
    },

    // 已知角度和斜边,获取直角边
    getHypotenuse(long, angle) {
      // 获得弧度
      let radian = 2 * Math.PI / 360 * angle
      return {
        a: Math.sin(radian) * long, // 邻边
        b: Math.cos(radian) * long // 对边
      }
    }
  }
}
</script>

Snipaste_2021-06-13_22-53-41.png

Эффект хороший.Далее я буду использовать CV Дафа, чтобы получить минутную широту. Добавленный код выглядит следующим образом:

<template>
  <div class="home">
    <!-- 分 -->
    <div class="box-wrapper">
      <div class="circle-box" :style="boxStyle('minutes', minutesDeg)">
        <span
          v-for="(item, index) in minuteTexts"
          :key="item"
          :style="spanStyle(boxSize.minutes, minuteTexts, index)"
        >{{ item }}</span>
      </div>
    </div>
    <!-- 秒 -->
    <div class="box-wrapper">
      <div class="circle-box" :style="boxStyle('seconds', secondsDeg)">
        <span
          v-for="(item, index) in secondTexts"
          :key="item"
          :style="spanStyle(boxSize.seconds, secondTexts, index)"
        >{{ item }}</span>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  name: 'Home',

  data() {
    return {
      secondTexts: [
        '零零', '一秒', '二秒', '三秒', '四秒', '五秒', '六秒', '七秒', '八秒', '九秒', '十秒',
        '十一秒', '十二秒', '十三秒', '十四秒', '十五秒', '十六秒', '十七秒', '十八秒', '十九秒', '二十秒',
        '二十一秒', '二十二秒', '二十三秒', '二十四秒', '二十五秒', '二十六秒', '二十七秒', '二十八秒', '二十九秒', '三十秒',
        '三十一秒', '三十二秒', '三十三秒', '三十四秒', '三十五秒', '三十六秒', '三十七秒', '三十八秒', '三十九秒', '四十秒',
        '四十一秒', '四十二秒', '四十三秒', '四十四秒', '四十五秒', '四十六秒', '四十七秒', '四十八秒', '四十九秒', '五十秒',
        '五十一秒', '五十二秒', '五十三秒', '五十四秒', '五十五秒', '五十六秒', '五十七秒', '五十八秒', '五十九秒'
      ],
      minuteTexts: [
        '零零', '一分', '二分', '三分', '四分', '五分', '六分', '七分', '八分', '九分', '十分',
        '十一分', '十二分', '十三分', '十四分', '十五分', '十六分', '十七分', '十八分', '十九分', '二十分',
        '二十一分', '二十二分', '二十三分', '二十四分', '二十五分', '二十六分', '二十七分', '二十八分', '二十九分', '三十分',
        '三十一分', '三十二分', '三十三分', '三十四分', '三十五分', '三十六分', '三十七分', '三十八分', '三十九分', '四十分',
        '四十一分', '四十二分', '四十三分', '四十四分', '四十五分', '四十六分', '四十七分', '四十八分', '四十九分', '五十分',
        '五十一分', '五十二分', '五十三分', '五十四分', '五十五分', '五十六分', '五十七分', '五十八分', '五十九分'
      ],
      // 盒子大小
      boxSize: {
        seconds: 580,
        minutes: 440
      }
    }
  }
}
</script>

Snipaste_2021-06-13_23-08-39.png

Поверните компас

Добавленный код выглядит следующим образом:

<template>
  <div class="home">
    <!-- 分 -->
    <div class="box-wrapper">
      <div class="circle-box" :style="boxStyle('minutes', minutesDeg)">
        <span
          v-for="(item, index) in minuteTexts"
          :key="item"
          :style="spanStyle(boxSize.minutes, minuteTexts, index)"
          :class="{'active': index === currentMinutes}"
        >{{ item }}</span>
      </div>
    </div>
    <!-- 秒 -->
    <div class="box-wrapper">
      <div class="circle-box" :style="boxStyle('seconds', secondsDeg)">
        <span
          v-for="(item, index) in secondTexts"
          :key="item"
          :style="spanStyle(boxSize.seconds, secondTexts, index)"
          :class="{'active': index === currentSeconds}"
        >{{ item }}</span>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  name: 'Home',

  data() {
    return {
      currentMinutes: 0, // 当前-分钟
      currentSeconds: 0, // 当前-秒
      minutesDeg: 0, // 当前-分钟-转动角度
      secondsDeg: 0, // 当前-面-转动角度
      timer: null // 定时器
    }
  },

  mounted() {
    this.init()
  },

  methods: {
    init() {
      const d = new Date()
      const minutes = d.getMinutes() // 分
      const seconds = d.getSeconds() // 秒
      // 当前时间
      this.currentMinutes = minutes
      this.currentSeconds = seconds
      // 角度
      this.minutesDeg = this.currentMinutes * this.getPerDeg(this.minuteTexts)
      this.secondsDeg = this.currentSeconds * this.getPerDeg(this.secondTexts)
      // 设置定时器
      this.timer = setInterval(() => {
        this.runClock()
      }, 1000)
      // 记得清除定时器
      this.$once('hook:beforeDestroy', () => {
        clearInterval(this.timer)
      })
    },

    boxStyle(key, deg) {
      return {
        // 设置文字外围盒子宽度、高度
        width: this.boxSize[key] + 'px',
        height: this.boxSize[key] + 'px',
        // 添加转动
        transform: `rotate(-${deg}deg)`
      }
    },

    // 元素平均间隔度数
    getPerDeg(texts) {
      return 360 / texts.length
    },

    runClock() {
      const d = new Date()
      const minutes = d.getMinutes() // 分
      const seconds = d.getSeconds() // 秒
      if (this.currentMinutes !== minutes) {
        this.currentMinutes = minutes
        this.minutesDeg += this.getPerDeg(this.minuteTexts)
      }
      this.currentSeconds = seconds
      this.secondsDeg += this.getPerDeg(this.secondTexts)
    }
  }
}
</script>

<style lang="scss" scoped>
.circle-box {
  position: relative;
  // 添加动画效果
  transition: transform 0.4s ease-in-out;
}

// 激活时文字颜色为白色
.circle-box span.active {
  color: #fff;
}
</style>

Kapture 2021-06-13 at 23.32.14.gif

На этом вся работа почти завершена. Остальные широты, такие как месяц, день и час, такие же, как рисование тыквы. Я не буду их здесь размещать. Заинтересованные студенты могут перейти на GitHub, чтобы просмотреть полный код.

Полный кодовый адрес:GitHub

конец

Эта статья подошла к концу, спасибо за прочтение, кодить не просто, приветствуем ваши лайки 👍! ! !

Если в этой статье есть ошибки, пожалуйста, исправьте и обменяйтесь в комментариях!