This commit is contained in:
nicomacbookpro
2025-12-12 16:21:46 +08:00
commit c0e405b34c
40 changed files with 25844 additions and 0 deletions

288
index.html Normal file
View File

@@ -0,0 +1,288 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D模型拆解动画 - ExplosionManager</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
overflow: hidden;
}
#box {
width: 100vw;
height: 100vh;
position: relative;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 24px;
z-index: 100;
text-align: center;
}
.loading-spinner {
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid white;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.info {
position: absolute;
top: 20px;
left: 20px;
color: white;
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 8px;
font-size: 14px;
z-index: 10;
max-width: 300px;
}
.info h2 {
margin-bottom: 10px;
font-size: 18px;
}
.info p {
margin: 5px 0;
line-height: 1.6;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div id="box"></div>
<div id="loading" class="loading">
<div class="loading-spinner"></div>
<div>正在加载模型...</div>
</div>
<div class="info">
<h2>3D模型拆解动画</h2>
<p>使用鼠标拖拽旋转视角</p>
<p>使用滚轮缩放</p>
<p>右侧控制面板可控制动画</p>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/",
"gsap": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/index.js",
"dat.gui": "https://cdn.jsdelivr.net/npm/dat.gui@0.7.9/build/dat.gui.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as dat from 'dat.gui'
import { ExplosionManager } from './src/ExplosionManager.js'
const box = document.getElementById('box')
const loading = document.getElementById('loading')
// 创建场景
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x1a1a2e)
// 创建相机
const camera = new THREE.PerspectiveCamera(75, box.clientWidth / box.clientHeight, 0.1, 1000)
camera.position.set(5, 5, 5)
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true })
renderer.setSize(box.clientWidth, box.clientHeight)
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
box.appendChild(renderer.domElement)
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.05
controls.minDistance = 2
controls.maxDistance = 50
// 窗口大小调整
window.addEventListener('resize', () => {
renderer.setSize(box.clientWidth, box.clientHeight)
camera.aspect = box.clientWidth / box.clientHeight
camera.updateProjectionMatrix()
})
// 添加光照
scene.add(new THREE.AmbientLight(0xffffff, 0.6))
const pointLight = new THREE.PointLight(0xffffff, 1.5, 0, 2)
pointLight.position.set(5, 5, 5)
pointLight.castShadow = true
scene.add(pointLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 2)
directionalLight.position.set(-5, 5, -5)
directionalLight.castShadow = true
scene.add(directionalLight)
// 添加坐标轴辅助线
scene.add(new THREE.AxesHelper(1000))
// 创建爆炸管理器
const explosionManager = new ExplosionManager(scene)
// 加载模型
async function loadModel() {
try {
// 加载OBJ模型public目录下的模型
await explosionManager.loadOBJ(
'./public/model/刘家大堰室内示例.obj',
'./public/model/刘家大堰室内示例.mtl',
(progress) => {
if (progress.lengthComputable) {
const percent = (progress.loaded / progress.total) * 100
console.log(`加载进度: ${percent.toFixed(2)}%`)
}
}
)
console.log('模型加载成功')
loading.classList.add('hidden')
// 自动调整相机位置以适应模型
const model = explosionManager.getModel()
if (model) {
const box = new THREE.Box3().setFromObject(model)
const center = box.getCenter(new THREE.Vector3())
const size = box.getSize(new THREE.Vector3())
const maxDim = Math.max(size.x, size.y, size.z)
const distance = maxDim * 2
camera.position.set(center.x + distance, center.y + distance, center.z + distance)
controls.target.copy(center)
controls.update()
}
} catch (error) {
console.error('模型加载失败:', error)
loading.innerHTML = '<div style="color: #ff6b6b;">模型加载失败,请检查文件路径</div>'
}
}
// 创建GUI控制面板
const gui = new dat.GUI({ name: '控制面板' })
gui.domElement.style.position = 'absolute'
gui.domElement.style.top = '20px'
gui.domElement.style.right = '20px'
gui.domElement.style.zIndex = '100'
// 动画控制
const controlsObj = {
'拆解动画': () => {
explosionManager.explode({
duration: params.duration,
distance: params.distance,
ease: params.ease,
onComplete: () => {
console.log('拆解动画完成')
}
})
},
'还原动画': () => {
explosionManager.restore({
duration: params.duration,
ease: params.ease,
onComplete: () => {
console.log('还原动画完成')
}
})
},
'切换状态': () => {
explosionManager.toggle({
duration: params.duration,
distance: params.distance,
ease: params.ease
})
}
}
gui.add(controlsObj, '拆解动画').name('💥 拆解')
gui.add(controlsObj, '还原动画').name('🔄 还原')
gui.add(controlsObj, '切换状态').name('🔄 切换')
// 动画参数
const params = {
duration: 1,
distance: 1.5,
ease: 'power2.inOut'
}
const paramsFolder = gui.addFolder('动画参数')
paramsFolder.add(params, 'duration', 0.1, 3).name('时长(秒)').onChange((value) => {
explosionManager.setAnimationParams({ duration: value })
})
paramsFolder.add(params, 'distance', 0.5, 15).name('距离').onChange((value) => {
explosionManager.setAnimationParams({ distance: value })
})
paramsFolder.add(params, 'ease', [
'power1.inOut',
'power2.inOut',
'power3.inOut',
'power4.inOut',
'back.inOut',
'elastic.inOut',
'bounce.inOut'
]).name('缓动函数').onChange((value) => {
explosionManager.setAnimationParams({ ease: value })
})
paramsFolder.open()
// 渲染循环
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
// 开始加载模型和动画
loadModel()
animate()
</script>
</body>
</html>