leonsimon23's picture
Update main.js
7e8d11f verified
// main.js (最终版,包含UI缩放按钮和更新后的数据)
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
// --- 1. 数据中心 ---
// 在这里管理所有的中药饮片信息。
const herbsData = [
{
id: 'biejia',
name: '鳖甲',
path: './assets/biejia/', // 模型所在文件夹路径
objFile: 'biejia.obj',
mtlFile: 'biejia.mtl',
},
{
id: 'wanglingzhi', // 已按要求修改
name: '醋五灵脂',
path: './assets/wanglingzhi/',
objFile: 'wanglingzhi.obj',
mtlFile: 'wanglingzhi.mtl',
},
{
id: 'jiangcan',
name: '僵蚕',
path: './assets/jiangcan/',
objFile: 'jiangchan-xiao.obj',
mtlFile: 'jiangchan-xiao.mtl',
}
];
// --- 2. 全局变量 ---
let scene, camera, renderer, controls;
let currentModel = null;
const loadingOverlay = document.getElementById('loading-overlay');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
// --- 3. 初始化函数 ---
function init() {
// 基础设置
const canvas = document.querySelector('#c');
renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
scene = new THREE.Scene();
scene.background = new THREE.Color(0x333344);
// 相机和控制器
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 30);
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.target.set(0, 5, 0);
// 光照
scene.add(new THREE.AmbientLight(0xffffff, 0.8));
const dirLight = new THREE.DirectionalLight(0xffffff, 1.2);
dirLight.position.set(10, 15, 10);
scene.add(dirLight);
// 渲染循环
function render() {
// 响应式调整
const pixelRatio = window.devicePixelRatio;
const width = canvas.clientWidth * pixelRatio | 0;
const height = canvas.clientHeight * pixelRatio | 0;
if (renderer.domElement.width !== width || renderer.domElement.height !== height) {
renderer.setSize(width, height, false);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
// 创建UI界面
createUI();
// 添加UI按钮的事件监听
setupZoomControls();
// 默认加载第一个模型
if (herbsData.length > 0) {
loadModel(herbsData[0]);
document.querySelector(`#herb-list li[data-id='${herbsData[0].id}']`).classList.add('active');
}
}
// --- 4. UI创建与交互 ---
function createUI() {
const herbList = document.getElementById('herb-list');
herbsData.forEach(herb => {
const li = document.createElement('li');
li.textContent = herb.name;
li.dataset.id = herb.id;
li.addEventListener('click', () => {
herbList.querySelectorAll('li').forEach(item => item.classList.remove('active'));
li.classList.add('active');
loadModel(herb);
});
herbList.appendChild(li);
});
}
// --- 新增:设置缩放按钮的功能 ---
// --- 修改后的函数:设置缩放按钮的功能 ---
function setupZoomControls() {
const zoomInBtn = document.getElementById('zoom-in-btn');
const zoomOutBtn = document.getElementById('zoom-out-btn');
const zoomScale = 1.2; // 定义一个缩放因子,大于1
zoomInBtn.addEventListener('click', () => {
// --- 直接修改相机位置 ---
// 获取从相机指向目标点的向量
const offset = new THREE.Vector3().subVectors(camera.position, controls.target);
// 将这个向量缩短 (除以一个大于1的数)
offset.multiplyScalar(1 / zoomScale);
// 将新的向量加回到目标点,得到相机的新位置
camera.position.copy(controls.target).add(offset);
// 我们不需要手动调用 controls.update(),因为 render 循环会做这件事
});
zoomOutBtn.addEventListener('click', () => {
// --- 直接修改相机位置 ---
// 获取从相机指向目标点的向量
const offset = new THREE.Vector3().subVectors(camera.position, controls.target);
// 将这个向量拉长 (乘以一个大于1的数)
offset.multiplyScalar(zoomScale);
// 将新的向量加回到目标点,得到相机的新位置
camera.position.copy(controls.target).add(offset);
});
}
// --- 5. 模型加载函数 (带进度条) ---
function loadModel(herb) {
if (currentModel) {
scene.remove(currentModel);
}
loadingOverlay.classList.add('visible');
progressBar.style.width = '0%';
progressText.textContent = '0%';
const onProgress = (xhr) => {
if (xhr.lengthComputable) {
const percentComplete = (xhr.loaded / xhr.total) * 100;
const percent = Math.round(percentComplete);
progressBar.style.width = percent + '%';
progressText.textContent = percent + '%';
}
};
const onError = (error) => {
console.error(`加载模型 ${herb.name} 时发生错误:`, error);
loadingOverlay.classList.remove('visible');
};
const mtlLoader = new MTLLoader();
mtlLoader.setResourcePath(herb.path);
mtlLoader.load(
herb.path + herb.mtlFile,
(materials) => {
materials.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(
herb.path + herb.objFile,
(object) => {
const box = new THREE.Box3().setFromObject(object);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
object.position.sub(center);
const maxDim = Math.max(size.x, size.y, size.z);
if (maxDim > 0) {
const scale = 15 / maxDim;
object.scale.set(scale, scale, scale);
}
scene.add(object);
currentModel = object;
console.log(`成功加载: ${herb.name}`);
loadingOverlay.classList.remove('visible');
},
onProgress,
onError
);
},
onProgress,
onError
);
}
// --- 启动系统 ---
init();