一、安装必要的软件
3D项目创建:
npm create vite@latest task-hub -- --template vue
npm create vite@latest:拉取最新版 Vite 脚手架创建项目
task-hub:项目文件夹名称(生成在当前目录)
--:分隔 npm 参数与 Vite 专属参数,不能省略
--template vue:指定模板为纯 Vue3(不含 TS、JSX 等)
npm install vue-router 安装路由
npm install three --save # three 生产依赖
npm install @types/three -D # 类型声明 开发依赖
npm install ant-design-vue 业务 UI 组件库
npm install typescript --save-dev 等价 npm install typescript -D
npm install stats.js //帧率监控,性能监控
npm install @types/stats.js -D //开发依赖
// 运行依赖,页面要实际使用控制面板
Three.js/ WebGL 开发专用可视化调试控制面板,快速拖拽修改参数,不用反复改代码刷新页面。
专门调 Three.js、3D 场景、动画、光照、模型参数,普通用户不会看到这个面板。
npm install dat.gui
npm install @types/dat.gui -D
npm install gsap 补间动画
二、文件内容
文件结构如下:
tian@hang:~/ThreejsEngineer$ tree -L 3
.
├── index.html
.......
├── package.json
├── package-lock.json
├── public
│ ├── favicon.svg
│ └── icons.svg
├── README.md
├── src
│ ├── App.vue
│ ├── assets
│ │ ├── css
│ │ └── img
│ ├── components
│ │ └── ThreeView.vue
│ ├── lesson
│ │ └── Home.vue
│ ├── main
│ │ └── main.js
│ └── router
│ └── index.ts
└── vite.config.js
tian@hang:~/ThreejsEngineer$ ls
index.html node_modules package.json package-lock.json public README.md src vite.config.js
内容如下:
package.json内容
{
"name": "task-hub",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview --host"
},
"dependencies": {
"ant-design-vue": "^4.2.6",
"dat.gui": "^0.7.9",
"gsap": "^3.15.0",
"stats.js": "^0.17.0",
"three": "^0.185.0",
"vue": "^3.5.39",
"vue-router": "^5.1.0"
},
"devDependencies": {
"@types/stats.js": "^0.17.4",
"@types/three": "^0.185.0",
"@vitejs/plugin-vue": "^6.0.7",
"@vue/tsconfig": "^0.9.1",
"typescript": "^6.0.3",
"vite": "^8.1.1",
"vue-tsc": "^3.3.6"
}
}
index.html内容:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./src/assets/img/TSK.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>工业系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main/main.js"></script>
</body>
</html>
main.js 内容
import { createApp } from 'vue'
import '../assets/css/style.css'
import App from '../App.vue'
createApp(App).mount('#app')
Home.vue 内容
<template>
<div>
<h2>Three.js 演示</h2>
<ThreeView />
</div>
</template>
<script setup lang="ts">
import ThreeView from '../components/ThreeView.vue'
</script>
App.vue内容
<script setup>
import Home from './lesson/Home.vue'
</script>
<template>
<Home />
</template>
ThreeView.vue内容
<template>
<!-- 专属容器,不再直接挂body -->
<div ref="canvasContainer" style="width:100%;height:600px;border:1px solid #eee;"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { gsap } from 'gsap'
// DOM容器ref
const canvasContainer = ref<HTMLDivElement | null>(null)
// 全局场景变量,统一管理,卸载时销毁
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let controls: OrbitControls
let cube: THREE.Mesh
let animateId: number
let gsapTween: gsap.core.Tween
// 窗口resize处理函数
const handleResize = () => {
if (!canvasContainer.value) return
const width = canvasContainer.value.clientWidth
const height = canvasContainer.value.clientHeight
camera.aspect = width / height
camera.updateProjectionMatrix()
renderer.setSize(width, height)
renderer.setPixelRatio(window.devicePixelRatio)
}
// 双击全屏
const handleDblClick = () => {
if (!document.fullscreenElement) {
renderer.domElement.requestFullscreen()
} else {
document.exitFullscreen()
}
}
// 渲染循环
const renderLoop = () => {
animateId = requestAnimationFrame(renderLoop)
controls.update()
renderer.render(scene, camera)
}
// 挂载时初始化3D场景
onMounted(() => {
if (!canvasContainer.value) return
// 1. 场景
scene = new THREE.Scene()
// 2. 相机
camera = new THREE.PerspectiveCamera(
75,
canvasContainer.value.clientWidth / canvasContainer.value.clientHeight,
0.1,
1000
)
camera.position.set(0, 0, 10)
scene.add(camera)
// 3. 立方体
const boxGeo = new THREE.BoxGeometry(1, 1, 1)
const mat = new THREE.MeshBasicMaterial({ color: 0xffff00 })
cube = new THREE.Mesh(boxGeo, mat)
scene.add(cube)
// 4. 渲染器,挂载到vue容器,不是body
renderer = new THREE.WebGLRenderer()
renderer.setSize(canvasContainer.value.clientWidth, canvasContainer.value.clientHeight)
canvasContainer.value.appendChild(renderer.domElement)
// 5. 轨道控制器
controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
// 6. gsap动画
gsapTween = gsap.to(cube.position, {
x: 5,
duration: 5,
repeat: -1,
yoyo: true,
ease: "bounce.out",
onComplete: () => console.log("动画完成"),
onStart: () => console.log("动画开始")
})
// 7. 坐标轴辅助
const axesHelper = new THREE.AxesHelper(8)
scene.add(axesHelper)
// 8. 监听事件
window.addEventListener('resize', handleResize)
renderer.domElement.addEventListener('dblclick', handleDblClick)
// 启动渲染
renderLoop()
})
// 组件销毁,释放资源(关键!防止内存泄漏)
onUnmounted(() => {
// 停止渲染循环
cancelAnimationFrame(animateId)
// 停止gsap动画
gsapTween.kill()
// 移除监听
window.removeEventListener('resize', handleResize)
renderer.domElement.removeEventListener('dblclick', handleDblClick)
// 销毁渲染器、几何体、材质释放显存
renderer.dispose()
cube.geometry.dispose()
if (Array.isArray(cube.material)) {
cube.material.forEach(m => m.dispose())
} else {
cube.material.dispose()
}
})
</script>
三、服务器部署
(1)文件打包:npm run build
tian@hang:~/ThreejsEngineer$ npm run build
> task-hub@0.0.0 build
> vite build
vite v8.1.2 building client environment for production...
✓ 21 modules transformed.
computing gzip size...
dist/index.html 0.48 kB │ gzip: 0.34 kB
dist/assets/TSK-BnnPvGX4.png 10.62 kB
dist/assets/index-Bzyp_i7E.css 0.04 kB │ gzip: 0.06 kB
dist/assets/index-UVdWu6yE.js 671.57 kB │ gzip: 185.80 kB
[plugin builtin:vite-reporter]
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rolldownOptions.output.codeSplitting to improve chunking: https://rolldown.rs/reference/OutputOptions.codeSplitting
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 672ms
(2)文件传输
/home/tian/ThreejsEngineer/dist/ :文件在主机的位置
kickpi@192.168.31.224:/home/kickpi/myvue3 :服务器用户名;ip地址、文件放置服务器的位置
scp -r /home/tian/ThreejsEngineer/dist/ kickpi@192.168.31.224:/home/kickpi/myvue3
(3)权限配置
服务器第一次没起起来,检查时权限问题;做了如下操作
sudo mkdir -p /www/myvue3
sudo cp -r /home/kickpi/myvue3/dist/* /www/myvue3/
sudo chown -R www-data:www-data /www/myvue3
sudo chmod -R 755 /www/myvue3
(4)安装服务器
用到的命令如下:
sudo apt update
sudo apt install nginx -y //安装服务器
nginx -v //查看版本
sudo systemctl start nginx # 启动服务
sudo systemctl enable nginx # 设置开机自启
sudo ufw allow 80/tcp # 放行80端口 **kickpi不用**
sudo systemctl status nginx # 查看运行状态
sudo systemctl reload nginx.service #重新加载 ;更改文件后需要
sudo nginx -t # 校验配置语法
(5)文件增加
sudo vim /etc/nginx/conf.d/taskhub.conf
sudo vim /etc/nginx/conf.d/taskhub.conf
文件内容:
server {
listen 5210;
# 填你的域名或服务器公网IP
server_name myvue3.com 192.168.31.224;
# 上传dist的目录
root /www/myvue3;
index index.html;
# 关键:Vue Router history模式刷新404解决方案
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存,提升加载速度
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 7d;
add_header Cache-Control "public";
}
}
四、内网访问展示
