threejs+vue3+js旋转词云


title: threejs
date: 2024-12-11 09:50:41
tags: threes

Threejs

双行可展示旋转词云显示。

一、简单案例——旋转球体

以下代码使用vue3+js+threejs技术站进行的搭建,其中包含了场景创建、相机创建、渲染器创建、物体材创建等相关流程,构建了一个简单的立体球效果

笔记

<template>
  <div class="app-container">
    <div ref="threeJsContainer" class="three-container"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
import { render } from "less";

//1.three创建场景
const scene = new THREE.Scene();

//2.创建相机(透视相机,有近大远小的效果)
const camera = new THREE.PerspectiveCamera(
  45, //视角,越大视野越大
  window.innerWidth / window.innerHeight, //宽高比
  0.1, //近平面(相机最近能看到的物体是什么)
  1000 //远平面(相机最远能看到的物体是什么)
);

//3.创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight); //设置渲染的宽高
document.body.appendChild(renderer.domElement); //将画布加载到body当中

//4.创建几何体
// const geometry = new THREE.BoxGeometry(1, 1, 1);
const geometry = new THREE.SphereGeometry(1, 32, 32);

//5.创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
//设置父元素材质为线框模式
material.wireframe = true;

//6.创建网格(创建物体)
const cube = new THREE.Mesh(geometry, material);
cube.position.set(2, 0, 0); //局部坐标,依赖与父亲元素,相对父元素的位置
cube.scale.set(2, 2, 2); //设置立方体的放大(xyz轴都放大两倍),相对于父元素来说
cube.rotation.x = Math.PI / 4; //180/4=45度(物体本身旋转的角度)但是要是父元素旋转了45度,那么字元素也会45+45旋转

//7.将网格添加到场景中
scene.add(cube);

//8.设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.lookAt(0, 0, 0); //相机看向原点,默认也是原点

//添加坐标辅助器(用于感知物体的位置)
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper); //将坐标系添加到场景当中

//添加轨道控制器(可以拉远缩近查看)
const controls = new OrbitControls(camera, renderer.domElement);
//设置带阻尼的惯性(让滑动物体之后不会马上停下来)
controls.enableDamping = true; //自动旋转
controls.dampingFactor = 0.05; //设置阻尼系数,越大惯性月小

//渲染函数
function animate() {
  controls.update(); //调用轨道控制器可以拉大或者减小相关的图形
  requestAnimationFrame(animate);
  //旋转
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  //渲染
  renderer.render(scene, camera);
}

//执行相关的动画函数
animate();

//监听窗口的变化,完成自适应操作
window.addEventListener("resize", () => {
  //重置渲染器宽高比
  renderer.setSize(window.innerWidth, window.innerHeight);
  //重置相机宽高比
  camera.aspect = window.innerHeight / window.innerHeight;
  //更新相机的投影矩阵
  camera.updateProjectionMatrix();
});
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
}

canvas {
  display: block;
  position: fixed;
  left: 0;
  top: 90px;
  width: 100%;
  height: 100%;
}
</style>

二、简单案例

单行旋转词云

笔者这个案例的文档使用的是官方提供的在线json地址,优点是简单方便,缺点是不支持中文显示。

在线json文库:“https://threejs.org/examples/fonts/helvetiker_regular.typeface.json”

只展示了name这一个单行属性。

<template>
  <div class="container">
    <div ref="threeContainer" class="three-container"></div>
  </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

const threeContainer = ref(null);

onMounted(() => {
  /** 创建场景、摄像机、渲染器 */
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    100, //视角越大视野越大
    window.innerWidth / window.innerHeight,
    0.1, //相机能看到的恶最近的距离
    1500 //相机能看到的最远的距离
  );
  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearAlpha(0); // 设置清除颜色为透明
  threeContainer.value.appendChild(renderer.domElement); //将画布加载到元素中

  // 创建轨道控制器
  const controls = new OrbitControls(camera, renderer.domElement);
  // controls.enableDamping = true; //停后自动旋转
  controls.dampingFactor = 0.05; //设置阻尼系数,越大惯性月小
  controls.enablePan = false; // 禁用平移
  controls.enableRotate = false; // 禁用旋转
  controls.enableZoom = true; // 启用缩放

  //添加坐标辅助器(用于感知物体的位置)
  const axesHelper = new THREE.AxesHelper(5);
  scene.add(axesHelper); //将坐标系添加到场景当中

  function createTextMaterial(color) {
    return new THREE.MeshBasicMaterial({
      color: color, // 指定颜色
      side: THREE.FrontSide, // 只渲染正面
    });
  }
  const fontLoader = new FontLoader();
  fontLoader.load(
    "https://threejs.org/examples/fonts/helvetiker_regular.typeface.json",
    (font) => {
      const sphereRadius = 290;

      const dataList = [
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "测试数据" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Three.js" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "测试数据" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
        { name: "Mapping" },
        { name: "Hello" },
        { name: "World" },
        { name: "Three.js" },
        { name: "Sphere" },
        { name: "Text" },
      ];

      // 创建一个函数来生成文本几何体
      function createTextMesh(text, color) {
        const textGeometry = new TextGeometry(text, {
          font: font,
          size: 6, // 字体大小
          height: 2, // 字体深度
        });
        const textMaterial = new THREE.MeshBasicMaterial({ color: color });
        const textMesh = new THREE.Mesh(textGeometry, textMaterial);
        return textMesh;
      }

      dataList.forEach((item, index) => {
        // const textGeometry = new TextGeometry(item.name, {
        //   font: font,
        //   size: 6, // 增加字体大小
        //   height: 2, // 增加字体深度
        // });

        // 创建一个组来放置name和id文本
        const group = new THREE.Group();

        // 创建name文本
        const nameMesh = createTextMesh(item.name, "#cccccc");
        nameMesh.position.set(0, 0, 0); // 设置name文本的位置
        group.add(nameMesh);

        // 创建id文本
        const idMesh = createTextMesh(item.id.toString(), "#cccccc");
        idMesh.position.set(0, -10, 0); // 设置id文本的位置,使其与name文本不在同一行
        group.add(idMesh);

        // 设置组的整体位置
        const phi = Math.acos(-1 + (2 * index) / dataList.length);
        const theta = Math.sqrt(dataList.length * Math.PI) * phi;
        group.position.setFromSphericalCoords(sphereRadius, phi, theta);

        //设置文字,创建文字
        // const phi = Math.acos(-1 + (2 * index) / dataList.length);
        // const theta = Math.sqrt(dataList.length * Math.PI) * phi;
        // const textColor = item.color || "#cccccc"; // 可以从数据项中获取颜色,或者使用默认颜色
        // const textMaterial = createTextMaterial(textColor);
        // const textMesh = new THREE.Mesh(textGeometry, textMaterial);

        // textMesh.position.setFromSphericalCoords(sphereRadius, phi, theta);
        // scene.add(textMesh);
        textMesh.lookAt(0, 0, 1000);
      });

      scene.rotation.x += Math.PI / 4;
      // 设置相机位置
      camera.position.set(-100, 0, 450);

      function animate() {
        requestAnimationFrame(animate);
        scene.rotation.y += 0.003;
        // scene.rotation.x += 0.002;
        // scene.rotation.z += 0.005;
        // 更新轨道控制器
        // 更新文字朝向摄像机
        dataList.forEach((item, index) => {
          const textMesh = scene.children[index]; // 假设场景中的每个子对象都是文本网格
          textMesh.lookAt(-200, 0, 800); // 使文字网格始终面向摄像机
        });
        controls.update();
        renderer.render(scene, camera);
      }

      animate();

      //监听窗口的变化,完成自适应操作
      // window.addEventListener("resize", () => {
      //   //重置渲染器宽高比
      //   renderer.setSize(window.innerWidth, window.innerHeight);
      //   //重置相机宽高比
      //   camera.aspect = window.innerHeight / window.innerHeight;
      //   //更新相机的投影矩阵
      //   camera.updateProjectionMatrix();
      // });
    }
  );
});
</script>

<style scoped>
.container {
  background: url(https://s3.bmp.ovh/imgs/2024/12/12/7143be5923fe6929.png);
  background-size: cover;
  text-align: center;
}
.three-container {
  margin-left: 100px;
  width: 100%;
  height: 90vh;
  overflow: hidden;
}
</style>

三、简单案例——双行旋转词云

这个双行球形词云每个元素显示两行,一行显示id,一行显示name,效果图如开始的图案所示。

由于官方提供的在线链接不支持中文,笔者将相关要使用的字体文件上传到了百度云盘,字体是仿宋字体。

通过网盘分享的文件:FangSong_Regular.zip
链接: https://pan.baidu.com/s/1-eL2SfFMA1xcrUYZtTVgwA?pwd=2025 提取码: 2025

<template>
  <div class="container">
    <div class="left-container">
      <el-button @click="changeSpeed">控制速度按钮</el-button>
    </div>
    <div ref="threeContainer" class="three-container"></div>
  </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
const status = ref(false)

const threeContainer = ref(null);

onMounted(() => {

  /** 创建场景、摄像机、渲染器 */
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    83, //视角越大视野越大
    1000 / 800,
    0.1, //相机能看到的恶最近的距离
    1500 //相机能看到的最远的距离
  );
  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(1000, 800);
  renderer.setClearAlpha(0); // 设置清除颜色为透明
  threeContainer.value.appendChild(renderer.domElement); //将画布加载到元素中

  // 创建轨道控制器
  const controls = new OrbitControls(camera, renderer.domElement);
  // controls.enableDamping = true; //停后自动旋转
  controls.dampingFactor = 0.05; //设置阻尼系数,越大惯性月小
  controls.enablePan = false; // 禁用平移
  controls.enableRotate = false; // 禁用旋转
  controls.enableZoom = true; // 启用缩放

  //添加坐标辅助器(用于感知物体的位置)
  // const axesHelper = new THREE.AxesHelper(5);
  // scene.add(axesHelper); //将坐标系添加到场景当中

  const fontLoader = new FontLoader();
  fontLoader.load(
    "https://threejs.org/examples/fonts/helvetiker_regular.typeface.json",
    (font) => {
      const sphereRadius = 300;

      const dataList = [
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
        { name: "World", id: "123swwww" },
        { name: "Three.js", id: "123swwww" },
        { name: "Sphere", id: "123swwww" },
        { name: "Text", id: "123swwww" },
        { name: "Mapping", id: "123swwww" },
        { name: "Hello", id: "123swwww" },
      ];

      // 创建一个函数来生成文本几何体
      function createTextMesh(text, color) {
        const textGeometry = new TextGeometry(text, {
          font: font,
          size: 6, // 字体大小
          height: 1, // 字体深度
        });
        const textMaterial = new THREE.MeshBasicMaterial({ color: color });
        const textMesh = new THREE.Mesh(textGeometry, textMaterial);
        return textMesh;
      }

      dataList.forEach((item, index) => {
        // 创建一个组来放置name和id文本
        const group = new THREE.Group();

        // 创建name文本
        const nameMesh = createTextMesh(item.name, "#cccccc");
        nameMesh.position.set(0, 0, 0); // 设置name文本的位置
        group.add(nameMesh);

        // 创建id文本
        const idMesh = createTextMesh(item.id.toString(), "#cccccc");
        idMesh.position.set(-10, -10, 0); // 设置id文本的位置,使其与name文本不在同一行
        group.add(idMesh);

        // 设置组的整体位置
        const phi = Math.acos(-1 + (2 * index) / dataList.length);
        const theta = Math.sqrt(dataList.length * Math.PI) * phi;

        group.position.setFromSphericalCoords(sphereRadius, phi, theta);
        scene.add(group);
        group.lookAt(0, 0, 500);
      });

      scene.rotation.z += Math.PI / 3;

      // 设置相机位置
      camera.position.set(0, 0, 500);

      function animate() {
        requestAnimationFrame(animate);
        scene.rotation.y += 0.007;
        // 更新轨道控制器
        // 更新文字朝向摄像机
        dataList.forEach((item, index) => {
          const textMesh = scene.children[index]; // 假设场景中的每个子对象都是文本网格
          textMesh.lookAt(0, 0, 500); // 使文字网格始终面向摄像机
        });
        controls.update();
        renderer.render(scene, camera);
      }

      animate();
    }
  );
});
</script>

<style scoped>
.container {
  background: url(https://s3.bmp.ovh/imgs/2024/12/12/7143be5923fe6929.png);
  background-size: cover;
  text-align: center;
  width: 100%;
  display: flex;

  .left-container {
    width: 350px;
  }

  .three-container {
    width: 1000px;
    height: 92vh;
    overflow: hidden;
    /* background-color: aquamarine; */
  }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939368.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RocketMQ源码分析(四) 延迟消息源码分析

0.前文 RocketMQ源码分析&#xff08;三&#xff09; 消费者 RocketMQ源码分析&#xff08;二&#xff09; 生产者 RocketMQ源码分析&#xff08;一&#xff09;broker启动&remoting抽象 1. 概述 RocketMQ的延迟消息是指消息发送到Broker后&#xff0c;不会立即被消费者…

嵌入式单片机中对应GPIO外设详解实现

一、GPIO外设详解 大家可以看到,函数库开发的时候外设的使用流程都是一样的,接下来就讲解一下细节。 l定义一个外设的结构体变量 变量命名规则 PPP_InitTypeDef PPP_InitStructure; 每个外设都有对应的结构体,结构体的定义一般都是存放在每个外设的头文件内,比如GPIO外…

C# OpenCvSharp DNN 实现百度网盘AI大赛-表格检测第2名方案第三部分-表格方向识别

目录 说明 效果 模型 项目 ​编辑 代码 参考 下载 其他 说明 百度网盘AI大赛-表格检测的第2名方案。 该算法包含表格边界框检测、表格分割和表格方向识别三个部分&#xff0c;首先&#xff0c;ppyoloe-plus-x 对边界框进行预测&#xff0c;并对置信度较高的表格边界…

智源研究院与腾讯达成战略合作,推动大模型技术前沿探索和应用落地

2024 年 12 月 18日&#xff0c; 智源研究院与腾讯签署战略合作协议&#xff0c;双方将在大模型研发、人工智能技术前沿探索及开源生态建设等领域展开深入合作。智源研究院院长王仲远、副院长兼总工程师林咏华&#xff0c;腾讯集团高级执行副总裁、云与智慧产业事业群总裁汤道生…

C++特殊类设计(单例模式等)

目录 引言 1.请设计一个类&#xff0c;不能被拷贝 2. 请设计一个类&#xff0c;只能在堆上创建对象 为什么设置实例的方法为静态成员呢 3. 请设计一个类&#xff0c;只能在栈上创建对象 4. 请设计一个类&#xff0c;不能被继承 5. 请设计一个类&#xff0c;只能创建一个对…

[SAP ABAP] ALV报表练习1

销售订单明细查询报表 业务目的&#xff1a;根据选择屏幕的筛选条件&#xff0c;使用 ALV 报表&#xff0c;显示销售订单详情 效果展示 用户的输入条件界面 用户的查询结果界面(部分截图) 完整代码如下所示 主程序(zsd001_437) *&----------------------------------…

Docker日志与监控

一、引言 随着容器技术在生产环境中被广泛应用&#xff0c;Docker容器的日志管理与监控变得尤为重要。在现代应用程序中&#xff0c;容器化的应用通常是由多个容器组成的服务&#xff0c;而容器中的日志与监控则是确保服务健康运行、诊断问题和优化性能的关键。通过日志和监控…

信号槽【QT】

文章目录 对象树字符集信号槽QT坐标系信号与槽connect自定义槽自定义信号disconnect 对象树 #ifndef MYLABEL_H #define MYLABEL_H#include<QLabel> class MyLabel : public QLabel { public:// 构造函数使用带 QWidget* 版本的.// 确保对象能够加到对象树上MyLabel(QWi…

3.zabbix中文设置

1、zabbix中文设置 2、中文乱码的原因 zabbix使用DejaVuSan.ttf字体&#xff0c;不支持中文&#xff0c;导致中文出现乱码。解决方法很简单&#xff0c;把我们电脑里面字体文件传到zabbix服务器上。 3、解决zabbix乱码方法 3.1、从Window服务器找到相应的字休复制到zabbix S…

电脑连接不上手机热点 找不到到服务器的ip地址

手机热点连接不上 找不到到服务器的ip地址 emmm希望不会有人不会吧 解决方法&#xff1a; 1.点击右上角图标进入设置 2.点击更改所有wifi网络的DNS设置 3.查看自己的IP分配和DNS分配是不是DHCP自动分配&#xff0c;不是的话就不对了&#xff0c;需要点击编辑手动改一下 4.改完…

计算机网络之王道考研读书笔记-2

第 2 章 物理层 2.1 通信基础 2.1.1 基本概念 1.数据、信号与码元 通信的目的是传输信息。数据是指传送信息的实体。信号则是数据的电气或电磁表现&#xff0c;是数据在传输过程中的存在形式。码元是数字通信中数字信号的计量单位&#xff0c;这个时长内的信号称为 k 进制码…

MySQL数据库04|内置函数、存储过程、视图、事务、索引

目录 十三、MySQL常用内置函数 1、字符串函数 1️⃣拼接字符串&#xff1a;concat(str1,str2,…) 2️⃣包含字符个数&#xff1a;length(str) 3️⃣截取字符串&#xff1a;left(str,len)、right(str,len)、substring(str,pos,len) 4️⃣去除空格&#xff1a;ltrim(str)、r…

【Unity3D】实现可视化链式结构数据(节点数据)

关键词&#xff1a;UnityEditor、可视化节点编辑、Unity编辑器自定义窗口工具 使用Newtonsoft.Json、UnityEditor相关接口实现 主要代码&#xff1a; Handles.DrawBezier(起点&#xff0c;终点&#xff0c;起点切线向量&#xff0c;终点切线向量&#xff0c;颜色&#xff0c;n…

Group FLUX - Beta Sprint Essay2

文章目录 I. SCRUMAchievements from yesterday’s stand-up meeting to the present Commit recordFrontend-CommitsBackend-Commits PM ReportBurnup mapRunning image of our current program I. SCRUM Achievements from yesterday’s stand-up meeting to the present Zh…

硬盘清洁器 -一个功能出色的的文件与使用纪录清理工具,不仅可以将磁盘中不必要的暂存盘一次扫除,供大家学习研究参考

【核心功能】 1.硬件性能检测。 2.清理日常垃圾信息。 3.永久性删除文件。不可恢复擦除可用空间。 4.系统恢复和还原。 5.磁盘管理。 6.重复文件删除。坏链清除&#xff0c;删除非必要文件。 7.恢复删除文件。含电子照片、PDF、视频等。 8.批量重命名。 下载&#xff1a;https:…

[Linux] 进程信号概念 | 信号产生

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 为什么我的课设这么难…

流程引擎Activiti性能优化方案

流程引擎Activiti性能优化方案 Activiti工作流引擎架构概述 Activiti工作流引擎架构大致分为6层。从上到下依次为工作流引擎层、部署层、业务接口层、命令拦截层、命令层和行为层。 基于关系型数据库层面优化 MySQL建表语句优化 Activiti在MySQL中创建默认字符集为utf8&…

51c视觉~合集36

我自己的原文哦~ https://blog.51cto.com/whaosoft/12275223 #无监督盲超分算法MLMC 即插即用的解决方案 本文介绍了一种新的无监督盲超分辨率算法MLMC&#xff0c;该算法结合了元学习和马尔可夫链蒙特卡罗核估计&#xff0c;无需监督预训练或参数先验&#xff0c;即可实现…

Firecrawl教程①:自动化抓取与数据转化,赋能AI应用

Firecrawl教程①:自动化抓取与数据转化,赋能AI应用 前言一、功能特点1. 支持 LLM 可处理的数据格式2. 全面抓取网站3. 强大的操作支持4. 灵活的定制选项5. 支持多种编程语言 SDK二、如何开始使用 Firecrawl第一步:获取 API 密钥第二步:官网在线工具使用第三步:安装 Firecr…

关于目标检测YOLO 各版本区别v1-v11/X/R/P

概述 YOLO&#xff08;You Only Look Once&#xff0c;你只看一次&#xff09;是一系列开创性的实时目标检测模型&#xff0c;它们彻底改变了计算机视觉领域。由Joseph Redmon开发&#xff0c;后续版本由不同研究人员迭代&#xff0c;YOLO模型以其在图像中检测对象的高速度和准…