vue3 词云

<script setup lang="ts">
defineOptions({
  name: 'rightTop',
});
let timer: any = null;
const wrapperRef = ref()
const tagRef: any = ref([])
const rotateAngleX = ref()
const rotateAngleY = ref()
const option = ref({
  radius: 80, // 滚动半径,单位px
  maxFont: 24, // 最大字体大小
  color: 'white', // 字体颜色。为空时随机
  rotateAngleXbase: 600, // 默认旋转速度基数,数越小速度越快
  rotateAngleYbase: 600,
  backgrounds: ['0 0 24px 3px #b7a47b inset', '0 0 24px 3px rgba(46, 144, 255.2) inset', '0 0 24px 3px #218a88 inset', '0 0 24px 3px #0065a9 inset'],
  hover: true, // 是否开启悬浮联动
})

const tagList = ref([])
const data = ref([
  {
    name: '基层减负',
    num: 100,
  },
  {
    name: '收费政策',
    num: 20,
  },
  {
    name: '面子工程',
    num: 88,
  },
  {
    name: '形式主义',
    num: 30,
  },
  {
    name: '节庆活动',
    num: 100,
  },
  {
    name: '一刀切',
    num: 20,
  },
  {
    name: '展会',
    num: 20,
  },
  {
    name: '一票否决',
    num: 20,
  },
  {
    name: '文件多',
    num: 20,
  }
])

const getMineRef = (el: any, index: number) => {
  if (el) {
    tagRef.value[index] = el;
  }
};

function _initTags() {
  rotateAngleX.value = Math.PI / option.value.rotateAngleXbase;
  rotateAngleY.value = Math.PI / option.value.rotateAngleYbase;
  // 鼠标悬浮改变转速和方向
  if (option.value.hover) {
    nextTick(() => {
      wrapperRef.value.onmousemove = function (e: any) {
        rotateAngleY.value =
          (e.pageX - this.offsetLeft - this.offsetWidth / 2) / 10000;
        rotateAngleX.value =
          -(e.pageY - this.offsetTop - this.offsetHeight / 2) / 10000;
      };
    })

  } else {
    wrapperRef.value.onmousemove = null;
  }

  for (let i = 0, length = data.value.length; i < length; i++) {
    // 获取球面上均匀的点的经纬度 θ = arccos( ((2*num)-1)/all - 1); Φ = θ*sqrt(all * π);
    let angleX = Math.acos((2 * (i + 1) - 1) / length - 1);
    let angleY = angleX * Math.sqrt(length * Math.PI);
    // 根据经纬度获取点的坐标,球中心的点坐标是 (0,0,0) x=r*sinθ*cosΦ   y=r*sinθ*sinΦ   z=r*cosθ;
    const x = option.value.radius * Math.sin(angleX) * Math.cos(angleY);
    const y = option.value.radius * Math.sin(angleX) * Math.sin(angleY);
    const z = option.value.radius * Math.cos(angleX);
    nextTick(() => {
      if (option.value.color) {
        tagRef.value[i].style.color = option.value.color;
      } else {
        // 随机颜色
        tagRef.value[i].style.color =
          'rgb(' +
          Math.round(255 * Math.random()) +
          ',' +
          Math.round(255 * Math.random()) +
          ',' +
          Math.round(255 * Math.random()) +
          ')';
      }
      // 每个标签对象都有四对值
      let tag: any = {
        x: x,
        y: y,
        z: z,
        ele: tagRef.value[i],
      };
      tagList.value.push(tag);
    })

  }
  timer = setInterval(function () {
    for (var i = 0; i < tagList.value.length; i++) {
      rotateX(tagList.value[i]);
      rotateY(tagList.value[i]);
      setPosition(
        tagList.value[i],
        option.value.radius,
        option.value.maxFont
      );
      setBoxShadow(tagList.value[i], i)
    }
  }, 20);
}
_initTags()
function setBoxShadow(tag: any, i: any) {
  let length = option.value.backgrounds.length
  tag.ele.style.boxShadow = option.value.backgrounds[i % length]
}
function setPosition(tag: any, r: any, maxFont: any) {
  // 设置每个标签的坐标位置和字体大小以及透明度
  if (wrapperRef.value) {
    tag.ele.style.transform =
      'translate(' +
      (tag.x +
        wrapperRef.value.offsetWidth / 2 -
        tag.ele.offsetWidth / 2) +
      'px,' +
      (tag.y +
        wrapperRef.value.offsetHeight / 2 -
        tag.ele.offsetHeight / 2) +
      'px)';
    tag.ele.style.opacity = tag.z / r / 2 + 0.7;
    // tag.ele.style.fontSize = (tag.z / r / 2 + 0.5) * maxFont + 'px';
  }
}
function rotateX(tag: any) {
  var cos = Math.cos(rotateAngleX.value);
  var sin = Math.sin(rotateAngleX.value);
  var y1 = tag.y * cos - tag.z * sin;
  var z1 = tag.y * sin + tag.z * cos;
  tag.y = y1;
  tag.z = z1;
}
function rotateY(tag: any) {
  var cos = Math.cos(rotateAngleY.value);
  var sin = Math.sin(rotateAngleY.value);
  var x1 = tag.z * sin + tag.x * cos;
  var z1 = tag.z * cos - tag.x * sin;
  tag.x = x1;
  tag.z = z1;
}
function dbclickTag() {
  if (timer) {
    clearInterval(timer);
    timer = null;
  } else {
    timer = setInterval(function () {
      for (var i = 0; i < tagList.value.length; i++) {
        rotateX(tagList.value[i]);
        rotateY(tagList.value[i]);
        setPosition(
          tagList.value[i],
          option.value.radius,
          option.value.maxFont
        );
      }
    }, 20);
  }
}
function clickTag(item: any) {
  // this.$emit('clickTag', item);
}
onUnmounted(() => {
  if (timer) {
    clearInterval(timer);
  }
});
</script>
<template>
  <div class="cont-box w-full h-full">
    <div class="tag-cloud" ref="wrapperRef">
      <p v-for="(item, index) in data" :key="index" :ref="el => getMineRef(el, index)" @click="clickTag(item)"
        @dblclick="dbclickTag()">
        <!-- {{ item.num }}家
      <br />
      <br /> -->
        <span class="num">{{ item.num }}</span> 家
        <br />
        {{ item.name }}
      </p>
    </div>
  </div>
</template>
<style lang="less" scoped>
.cont-box {
  background: url("@/assets/images/home/hot-bg.png") no-repeat center center;
  background-size: contain;

  .tag-cloud {
    width: 100%;
    height: 100%;
    position: relative;
    color: #333;
    margin: 0 auto;
    text-align: center;
  }

  .tag-cloud p {
    position: absolute;
    top: 0px;
    left: 0px;
    color: white;
    font-family: Arial;
    text-decoration: none;
    margin: 0 10px 15px 0;
    text-align: center;
    font-size: 15px;
    padding: 4px 9px;
    display: inline-block;
    border-radius: 50%;
    width: 80px;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    height: 80px;
    line-height: 18px;
    background: rgba(50, 85, 113, 0.3);
    box-shadow: 0 0 24px 3px rgba(46, 144, 255.2) inset;
    padding-top: 19px;
  }

  .num {
    font-size: 20px;
    padding-top: 10px;
    font-weight: bold;
  }
}
</style>

 

posted @ 2024-11-12 14:44  abcByme  阅读(22)  评论(0编辑  收藏  举报