Skip to content

如何实现一个tooltip组件

  1. VUE代码如下
vue
<template>
  <div class="box" ref="boxRef">
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
  </div>
</template>

<script setup lang="ts">
  import {onMounted, ref} from 'vue';

  const boxRef = ref<HTMLElement | null>(null);

  // 获取transform 的rotateY的值
  const getRotateYFromTransform = (transform: string) => {
    return +transform.replace('rotateY(', '').replace('deg)', '');
  }
  // 计算div的transform rotateY的值 保证rotateY的值在0-360之间
  const getRangeRotateY = (rotateY: number) => {
    if (rotateY > 360) {
      return rotateY - 360;
    } else if (rotateY < 0) {
      return rotateY + 360;
    } else {
      return rotateY;
    }
  }

  onMounted(() => {
    // 计算鼠标在box div的拖动距离
    let x = 0;
    let isDown = false;
    boxRef.value!.onmousedown = (e) => {
      isDown = true;
      x = e.clientX;
      document.onmousemove = (e) => {
        if (isDown) {
          const moveX = e.clientX - x;
          const box = boxRef.value!;
          const rotateY = getRangeRotateY(getRotateYFromTransform(box.style.transform!) + moveX)
          boxRef.value!.style.transform = `rotateY(${rotateY}deg)`;
        }
      }
      document.onmouseup = (e) => {
        isDown = false;
        document.onmousemove = document.onmouseup = null;
      }
    };
  })
</script>

<style lang="scss" scoped>
  .box {
    margin: auto;
    width: 100px;
    height: 100px;
    background-color: yellow;
    transform: rotateY(30deg);
    position: relative;
    transform-style: preserve-3d;
    // animation: ani 5s linear infinite;
  }

  // 360 /6 = 60
  $items: 60;

  @for $i from 1 through 6 {

    ::v-deep(.box-item):nth-child(#{$i}) {
      position: absolute;
      width: 100px;
      height: 100px;
      color: #fff;
      background-color: rgba(random(255), random(255), random(255), 1);
      transform: rotateY($items*$i+deg) translateZ(400px);
      transform-style: preserve-3d;
    }
  }

  @keyframes ani {
    from {
      transform: rotateX(-10deg) rotateY(0deg);
    }

    to {
      transform: rotateX(-10deg) rotateY(360deg);
    }
  }
</style>
<template>
  <div class="box" ref="boxRef">
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
    <div class="box-item"></div>
  </div>
</template>

<script setup lang="ts">
  import {onMounted, ref} from 'vue';

  const boxRef = ref<HTMLElement | null>(null);

  // 获取transform 的rotateY的值
  const getRotateYFromTransform = (transform: string) => {
    return +transform.replace('rotateY(', '').replace('deg)', '');
  }
  // 计算div的transform rotateY的值 保证rotateY的值在0-360之间
  const getRangeRotateY = (rotateY: number) => {
    if (rotateY > 360) {
      return rotateY - 360;
    } else if (rotateY < 0) {
      return rotateY + 360;
    } else {
      return rotateY;
    }
  }

  onMounted(() => {
    // 计算鼠标在box div的拖动距离
    let x = 0;
    let isDown = false;
    boxRef.value!.onmousedown = (e) => {
      isDown = true;
      x = e.clientX;
      document.onmousemove = (e) => {
        if (isDown) {
          const moveX = e.clientX - x;
          const box = boxRef.value!;
          const rotateY = getRangeRotateY(getRotateYFromTransform(box.style.transform!) + moveX)
          boxRef.value!.style.transform = `rotateY(${rotateY}deg)`;
        }
      }
      document.onmouseup = (e) => {
        isDown = false;
        document.onmousemove = document.onmouseup = null;
      }
    };
  })
</script>

<style lang="scss" scoped>
  .box {
    margin: auto;
    width: 100px;
    height: 100px;
    background-color: yellow;
    transform: rotateY(30deg);
    position: relative;
    transform-style: preserve-3d;
    // animation: ani 5s linear infinite;
  }

  // 360 /6 = 60
  $items: 60;

  @for $i from 1 through 6 {

    ::v-deep(.box-item):nth-child(#{$i}) {
      position: absolute;
      width: 100px;
      height: 100px;
      color: #fff;
      background-color: rgba(random(255), random(255), random(255), 1);
      transform: rotateY($items*$i+deg) translateZ(400px);
      transform-style: preserve-3d;
    }
  }

  @keyframes ani {
    from {
      transform: rotateX(-10deg) rotateY(0deg);
    }

    to {
      transform: rotateX(-10deg) rotateY(360deg);
    }
  }
</style>

最终的效果如下