vue中实现3d词云效果(已封装组件)

这篇文章详细描述了一个使用Vue编写的动态词云组件,通过SVG实现3D效果,并处理鼠标移动、悬停和点击事件。组件可以根据用户交互实时调整形状和颜色。
摘要由CSDN通过智能技术生成

<!--
 * @Description: 词云组件 页面
 * @Date: 2024/3/10 23:39
-->
<template>
  <div
    :style="{
      display: 'flex',
      justifyContent: 'center',
      border: '1px solid red',
    }"
  >
    <svg
      :width="width"
      :height="height"
      @mousemove="listener($event)"
      @mouseout="listener1($event)"
      @mouseover="listener2($event)"
    >
      <a
        href="#"
        v-for="(tag, index) in tags"
        :key="index"
        @click="showOptionsAndResult(tag.text)"
      >
        <text
          :x="tag.x"
          :y="tag.y"
          :font-size="7 * (1000 / (800 - tag.z * 2))"
          :font-weight="550"
          :fill-opacity="(600 + tag.z) / 800"
          :style="style(tag)"
        >
          {{ tag.text }}
        </text>
      </a>
    </svg>
  </div>
</template>

<script>
export default {
  props: {
    width: {
      type: Number,
      default: 600,
    },
    height: {
      type: Number,
      default: 600,
    },
    radius: {
      type: Number,
      default: 200,
    },
  },
  data() {
    return {
      speedX: Math.PI / 1800,
      speedY: Math.PI / 1800,
      tags: [],
      colorList: [
        "#e27027",
        "#cc7b2e",
        "#ad4331",
        "#88343b",
        "#d4902f",
        "#c7a736",
        "#8d7a3d",
        "#8d7a3d",
        "#d9b134",
      ],
      CXNum: 2,
      CYNum: 2,
    };
  },
  computed: {
    CX() {
      return this.width / this.CXNum;
    },
    CY() {
      return this.height / this.CYNum;
    },
  },
  mounted() {
    let _this = this;
    window.addEventListener(
      "resize",
      () => {
        let normalWidth = document.body.scrollWidth;
        _this.screenWidth = normalWidth;
        if (normalWidth <= 1550) {
          _this.CXNum = 2.7;
          _this.CYNum = 1.9;
        } else {
          _this.CXNum = 2.5;
          _this.CYNum = 1.8;
        }
      },
      false
    );
    setInterval(() => {
      this.rotateX(this.speedX);
      this.rotateY(this.speedY);
    }, 17);
  },
  methods: {
    rotateX(angleX) {
      var cos = Math.cos(angleX);
      var sin = Math.sin(angleX);
      for (let tag of this.tags) {
        var y1 = (tag.y - this.CY) * cos - tag.z * sin + this.CY;
        var z1 = tag.z * cos + (tag.y - this.CY) * sin;
        tag.y = y1;
        tag.z = z1;
      }
    },
    rotateY(angleY) {
      var cos = Math.cos(angleY);
      var sin = Math.sin(angleY);
      for (let tag of this.tags) {
        var x1 = (tag.x - this.CX) * cos - tag.z * sin + this.CX;
        var z1 = tag.z * cos + (tag.x - this.CX) * sin;
        tag.x = x1;
        tag.z = z1;
      }
    },
    listener(event) {
      var x = event.clientX - this.CX;
      var y = event.clientY - this.CY;
      this.speedX =
        x * 0.0001 > 0
          ? Math.min(this.radius * 0.00002, x * 0.0001)
          : Math.max(-this.radius * 0.00002, x * 0.0001);
      this.speedY =
        y * 0.0001 > 0
          ? Math.min(this.radius * 0.00002, y * 0.0001)
          : Math.max(-this.radius * 0.00002, y * 0.0001);
    },
    listener1(e) {
      this.speedX = Math.PI / 1800;
      this.speedY = Math.PI / 1800;
    },
    listener2(e) {
      this.speedX = 0;
      this.speedY = 0;
    },
    showOptionsAndResult(text) {
      this.$emit("showOptionsAndResult", true, text, "", "");
    },
    style(tag) {
      return `fill:${tag.color};`;
    },
    calculation3DWord(radius = "") {
      let tags = [];
      for (let i = 0; i < this.tags.length; i++) {
        let tag = {};
        let k = -1 + (2 * (i + 1) - 1) / this.tags.length;
        let a = Math.acos(k);
        let b = a * Math.sqrt(this.tags.length * Math.PI);
        tag.text =
          typeof this.tags[i] === "string" ? this.tags[i] : this.tags[i].text;
        if (radius === "") {
          tag.x = this.CX + this.radius * Math.sin(a) * Math.cos(b);
          tag.y = this.CY + this.radius * Math.sin(a) * Math.sin(b);
          tag.z = this.radius * Math.cos(a);
        } else {
          tag.x = 150 * (radius / 120) + radius * Math.sin(a) * Math.cos(b);
          tag.y = 150 * (radius / 120) + radius * Math.sin(a) * Math.sin(b);
          tag.z = radius * Math.cos(a);
        }
        if (i <= this.colorList.length - 1) {
          tag.color = this.colorList[i];
        } else {
          tag.color =
            i % this.colorList.length === 0
              ? this.colorList[0]
              : this.colorList[i % this.colorList.length];
        }
        tags.push(tag);
      }
      this.tags.splice(0);
      this.tags = tags;
    },
    setTags(tags = []) {
      this.tags.splice(0);
      this.tags.push(...tags);
      this.calculation3DWord();
    },
  },
};
</script>

<style></style>
// 使用
  <wordCloud
            ref="wordCloud"
            :width="rBox3.width"
            :height="rBox3.height"
          />
                
                import wordCloud from "@/views/dataScreen/wordCloud.vue";
  components: { wordCloud },

                
      init() {
			this.$refs.wordCloud.setTags([1, 2, 3 ,4 ,5 ,6]);
      }

### 创建球形标签 为了在 Vue 中创建球形标签,可以采用 Three.js 来构建三维场景并渲染文字。Three.js 是一个流行的 JavaScript 库,用于基于 WebGL 渲染 3D 图形[^1]。 #### 安装依赖库 首先安装必要的 npm 包: ```bash npm install three @types/three vue-three-fiber ``` `three` 提供核心功能;`@types/three` 添加 TypeScript 类型定义支持;而 `vue-three-fiber` 则简化了 Vue 和 Three.js 的集成过程。 #### 组件结构设计 创建一个新的 Vue 单文件组件封装整个逻辑: ```html <template> <div id="word-cloud"></div> </template> <script lang="ts"> import { defineComponent, onMounted } from &#39;vue&#39;; // 导入所需的 Three.js 功能模块 import * as THREE from &#39;three&#39;; export default defineComponent({ name: &#39;WordCloud&#39;, setup() { let scene; let camera; let renderer; const initScene = () => { // 初始化场景对象 scene = new THREE.Scene(); // 设置相机视角参数 camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // 配置WebGLRenderer实例化 renderer = new THREE.WebGLRenderer({ antialias: true }); document.getElementById(&#39;word-cloud&#39;)?.appendChild(renderer.domElement); // ...其他初始化操作... }; onMounted(() => { initScene(); animate(); // 开始动画循环 function animate(): void { requestAnimationFrame(animate); render(); } function render(): void { renderer.render(scene, camera); } }); return {}; }, }); </script> <style scoped> #word-cloud { width: 100%; height: 100vh; /* 使用视口单位 */ } </style> ``` 此基础模板设置了一个基本的 Three.js 场景,并准备好了后续添加几何体和材质的空间。对于具体的单放置算法以及样式定制,则需进一步开发和完善。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值