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>