Three.js 中处理 3D 模型拖拽、缩放、边界检测的正确方法。解决鼠标移动与模型移动不同步、缩放后只能看到模型一部分等问题。
56
62%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.agent/skills/3d-interaction/SKILL.md问题: 使用固定的 panSpeed = 0.01 进行平移计算
// ❌ 错误方式
const panSpeed = 0.01;
newPosition.add(right.multiplyScalar(deltaX * panSpeed));为什么发生: 相机距离变化时,同样的世界空间距离在屏幕上的像素表现不同。距离近时像素多,距离远时像素少。
解决方案: 根据相机距离和 FOV 动态计算像素→世界空间的映射
// ✅ 正确方式:动态计算
const cameraDistance = camera.position.distanceTo(modelCenter);
const fov = camera.fov * (Math.PI / 180);
const screenHeight = renderer.domElement.clientHeight;
const screenWidth = renderer.domElement.clientWidth;
// 在相机距离处,视口的世界空间高度
const worldHeight = 2 * Math.tan(fov / 2) * cameraDistance;
const worldWidth = worldHeight * (screenWidth / screenHeight);
// 每像素对应的世界空间距离
const pixelToWorldX = worldWidth / screenWidth;
const pixelToWorldY = worldHeight / screenHeight;
// 应用:鼠标移动的像素 × 每像素对应的世界空间距离
newPosition.add(right.multiplyScalar(deltaX * pixelToWorldX));
newPosition.add(up.multiplyScalar(-deltaY * pixelToWorldY));问题: 使用模型中心点的 NDC 坐标判断是否出界
// ❌ 错误方式:限制中心点位置
const ndc = position.clone().project(camera);
if (ndc.y > 0.2) clampedY = 0.2; // 限制顶部为什么发生: 模型放大后,中心点在屏幕中心,但身体大部分已超出屏幕。限制中心点 = 限制只能看到身体中间部分。
解决方案: 计算模型在屏幕上的可见区域(像素),只在可见区域过小时才校正
// ✅ 正确方式:基于可见像素
const MIN_VISIBLE_PIXELS = 50;
// 1. 计算模型包围盒并投影到屏幕
const box = new THREE.Box3().setFromObject(vrm.scene);
const corners = [/* 8个顶点 */];
let modelMinX = Infinity, modelMaxX = -Infinity;
let modelMinY = Infinity, modelMaxY = -Infinity;
corners.forEach(corner => {
const projected = corner.clone().project(camera);
const screenX = (projected.x * 0.5 + 0.5) * screenWidth;
const screenY = (-projected.y * 0.5 + 0.5) * screenHeight;
// 更新边界...
});
// 2. 计算可见区域
const visibleWidth = Math.max(0, Math.min(screenWidth, modelMaxX) - Math.max(0, modelMinX));
const visibleHeight = Math.max(0, Math.min(screenHeight, modelMaxY) - Math.max(0, modelMinY));
const visiblePixels = visibleWidth * visibleHeight;
// 3. 只在可见区域太小时校正
if (visiblePixels < MIN_VISIBLE_PIXELS) {
// 将模型拉回可见区域
}worldHeight = 2 × tan(fov/2) × cameraDistance
pixelToWorld = worldHeight / screenHeightconst ndc = worldPos.clone().project(camera);
const screenX = (ndc.x * 0.5 + 0.5) * screenWidth;
const screenY = (-ndc.y * 0.5 + 0.5) * screenHeight; // Y 轴反向c6f1797
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.