如何写一个转盘

发布于:2024-12-23 ⋅ 阅读:(11) ⋅ 点赞:(0)

需求描述

  • 实现一个圆形转盘组件,转盘上的元素均匀分布,同时保持转盘元素的水平轴始终指向圆心。保证转盘最左边的元素是水平放置,正常展示。
  • 点击地下一排按钮,则转盘上对应的元素转到最左边。

实现

思路

核心实现机制:

  • 外部的容器,也就是圆周,采用相对定位
  • 每个数字块的定位采用绝对定位 + transform,旋转好角度后有序放在圆周上

交互逻辑:

  • 点击底部按钮时,计算目标数字所需旋转角度
  • 转盘通过 CSS transform: rotate() 实现整体旋转
  • 通过 CSS transition 实现平滑旋转动画

页面结构

什么是静态的?

  • 转盘的形状
  • 转盘元素的形状
  • 转盘元素相对于圆盘水平线的角度

什么是动态的?

  • 圆盘的旋转角度

什么是要算的?

  • 圆盘的旋转角度
  • 转盘元素相对于圆盘水平线的角度

动态的和要算的要写成变量或者方法

实现机制

不要去想转了多少度,不要想成计算;而是应该每个位置对应一个唯一的旋转角度,要想成映射

计算转盘元素相对于圆盘水平线的角度,包含三个关键步骤:

  • rotate(角度) - 按序分布在圆周上
  • translate(-150px) - 将数字移到圆周上,这里应为要保证最左边的元素是我们要展示的元素,所以是负数
  • translate(-150px) translate(-50%)确保圆盘元素的中心在圆周的上
// 计算每个数字块的位置和角
getItemStyle(index) {
  const anglePerSector = 360 / this.totalSectors;
  const rotateAngle = index * anglePerSector;
  return {
    transform: `rotate(${rotateAngle}deg) translate(-150px) translate(-50%) translateY(-50%)`,
  };
},

旋转转盘的方法,也就是要算出目标位置对应的角度是多少

// 计算按钮点击后转盘的旋转角度
rotateToNumber(index) {
    const anglePerSector = 360 / this.totalSectors;
    const targetAngle = index * anglePerSector;
    // 为了让目标数字转到最左边,我们需要相应调整角度
    this.rotation = -targetAngle;
},

组件代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
  <script src="./js/vue.min.js"></script>
    <title>Title</title>
</head>
<body>
<div id="vue_root">
  <div class="wheel-container">
    <!-- 转盘 -->
    <div class="wheel" :style="{ transform: `rotate(${rotation}deg)` }">
      <div v-for="(item, index) in numbers" :key="index" class="wheel-item" :style="getItemStyle(index)">
        {{ item }}
      </div>
    </div>

    <!-- 数字按钮 -->
    <div class="buttons">
      <button v-for="(item, index) in numbers" :key="index" @click="rotateToNumber(index)">
        {{ item }}
      </button>
    </div>
  </div>
</div>
</body>
<script>
  let vm = new Vue({
    el: "#vue_root",
    data() {
      return {
        numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9], // 转盘上的数字
        rotation: 0, // 转盘的旋转角度
        totalSectors: 9, // 转盘分为9个部分
      };
    },
    methods: {
      // 计算按钮点击后转盘的旋转角度
      rotateToNumber(index) {
        const anglePerSector = 360 / this.totalSectors;
        const targetAngle = index * anglePerSector;
        // 为了让目标数字转到最左边,我们需要相应调整角度
        this.rotation = -targetAngle;
      },
      // 计算每个数字块的位置和角
      getItemStyle(index) {
        const anglePerSector = 360 / this.totalSectors;
        const rotateAngle = index * anglePerSector;
        return {
          transform: `rotate(${rotateAngle}deg) translate(-150px) translate(-50%) translateY(-50%)`,
        };
      },
    },
  })
</script>
<style>
  .wheel-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    margin-top: 50px;
  }

  .wheel {
    width: 300px;
    height: 300px;
    border-radius: 50%;
    border: 2px solid #000;
    position: relative;
    /*left: 50%;*/
    margin-bottom: 20px;
    transition: transform 1s ease-in-out; /* 转盘旋转时的动画效果 */
  }

  .wheel-item {
    position: absolute;
    top: 50%;
    left: 50%;
    transform-origin: 0 0; /* 以左上角为原点进行旋转 */
    width: 30px;
    height: 30px;
    background-color: #f1c40f;
    text-align: center;
    line-height: 30px;
    border-radius: 50%;
  }

  .buttons {
    display: flex;
    justify-content: center;
  }

  button {
    margin: 0 5px;
    padding: 10px 20px;
    background-color: #3498db;
    color: white;
    border: none;
    cursor: pointer;
  }

  button:hover {
    background-color: #2980b9;
  }
</style>
</html>

效果展示

在这里插入图片描述