Three.js鼠标悬停物体时无法实现相机缩放效果求助
问题描述
我是Three.js新手,这是我的第一个项目。我想要实现鼠标悬停在frame1物体上时,相机能平滑缩放聚焦该物体的效果(类似将目标元素放大至屏幕中心的交互)。目前我能将frame1移到屏幕中央,但无法实现相机缩放,尝试过Tween但无效,怀疑代码基础逻辑存在问题。
项目代码
<!DOCTYPE html> <html> <head> <title>Three.js JPG Background with Custom Position</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script> <script src="./js/tween.js/dist/tween.umd.js"></script> <!-- Css --> <style> body { overflow: hidden; } canvas { width: 100%; height: 100%; position: fixed; top: 0; left: 0; } </style> </head> <body> <script> // Initialize Three.js scene var scene = new THREE.Scene(); var camera = new THREE.OrthographicCamera(window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 0.1,); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Load the Main BG texture var mainBg = new THREE.TextureLoader().load("http://127.0.0.1:5500/img/main-bg.jpg"); // Create a full-screen plane to display the texture var geometry1 = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight); var material1 = new THREE.MeshBasicMaterial({ map: mainBg }); var plane1 = new THREE.Mesh(geometry1, material1); plane1.position.set(0, 0, 0) scene.add(plane1); // Create a full-screen plane to display the texture var frame1 = new THREE.TextureLoader().load("http://127.0.0.1:5500/img/frame1.png"); var geometry2 = new THREE.PlaneGeometry(90, 110); var material2 = new THREE.MeshBasicMaterial({ map: frame1 }); var plane2 = new THREE.Mesh(geometry2, material2); plane2.position.set(-190, 130, .1); scene.add(plane2); // Check if mouse is over the child asset var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); var zoomed = false; // Mouse move function function onMouseMove(event) { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects([plane2]); if (intersects.length > 0 && !zoomed) { zoomed = true; camera.position.set(plane2.position.x, plane2.position.y, plane2.position.z); camera.lookAt(plane2.position.x, plane2.position.y, plane2.position.z); } else if (intersects.length === 0 && zoomed) { zoomed = false; camera.position.set(0, 0, 5); camera.lookAt(new THREE.Vector3(0, 0, 0)); } } // Mouse move function calling on moving mouse document.addEventListener('mousemove', onMouseMove, false); window.addEventListener('mousedown', function () { gsap.to(camera.position, { z: 15, duration: 15 }); }) // Render loop function render() { camera.position.z = 5; requestAnimationFrame(render); renderer.render(scene, camera); } render(); </script> </body> </html>
项目资源
- frame1(透明PNG格式图片)
- main-bg(背景图)
解决方案
你的代码存在三个核心问题导致缩放失效,逐个修正即可实现需求:
1. 正交相机缩放逻辑错误
你使用的是OrthographicCamera(正交相机),这类相机无透视效果,修改position.z不会产生缩放效果。正交相机的缩放需调整zoom属性,或者修改left/right/top/bottom参数,同时每次修改后必须调用camera.updateProjectionMatrix()更新投影矩阵。
2. 渲染循环强制覆盖相机状态
render函数中的camera.position.z = 5;会在每一帧强制重置相机z轴位置,直接覆盖所有动画修改,这是缩放失效的直接原因,必须删除该行代码。
3. 动画逻辑混乱
你同时引入了Tween.js和GSAP但未正确使用,且悬停逻辑直接硬改相机位置,无平滑过渡效果。
以下是修正后的完整代码,实现鼠标悬停frame1时平滑缩放聚焦、移出时恢复原状的效果:
<!DOCTYPE html> <html> <head> <title>Three.js JPG Background with Custom Position</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> <!-- Css --> <style> body { overflow: hidden; margin: 0; } canvas { width: 100%; height: 100%; position: fixed; top: 0; left: 0; } </style> </head> <body> <script> // Initialize Three.js scene var scene = new THREE.Scene(); // 补全正交相机远裁剪面参数,避免渲染异常 var camera = new THREE.OrthographicCamera( window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / -2, 0.1, 1000 ); camera.position.set(0, 0, 5); camera.lookAt(scene.position); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Load the Main BG texture(替换为你的本地背景图路径) var mainBg = new THREE.TextureLoader().load("./img/main-bg.jpg"); mainBg.minFilter = THREE.LinearFilter; // Create a full-screen plane to display the texture var geometry1 = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight); var material1 = new THREE.MeshBasicMaterial({ map: mainBg }); var plane1 = new THREE.Mesh(geometry1, material1); plane1.position.set(0, 0, 0) scene.add(plane1); // Create frame1 plane(替换为你的本地frame1图片路径) var frame1 = new THREE.TextureLoader().load("./img/frame1.png"); frame1.minFilter = THREE.LinearFilter; var geometry2 = new THREE.PlaneGeometry(90, 110); // 开启透明,适配PNG图片的透明区域 var material2 = new THREE.MeshBasicMaterial({ map: frame1, transparent: true }); var plane2 = new THREE.Mesh(geometry2, material2); plane2.position.set(-190, 130, 0.1); scene.add(plane2); // Check if mouse is over the child asset var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); var zoomed = false; // 保存相机初始状态,用于移出时恢复 const initialCamera = { pos: new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z), zoom: camera.zoom }; // 聚焦frame1时的相机状态 const targetCamera = { pos: new THREE.Vector3(plane2.position.x, plane2.position.y, camera.position.z), zoom: 3 // 缩放倍数,数值越大放大效果越明显 }; // Mouse move function function onMouseMove(event) { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects([plane2]); if (intersects.length > 0 && !zoomed) { zoomed = true; // 平滑过渡到聚焦状态 gsap.to(camera.position, { x: targetCamera.pos.x, y: targetCamera.pos.y, duration: 0.5, onUpdate: () => camera.updateProjectionMatrix() }); gsap.to(camera, { zoom: targetCamera.zoom, duration: 0.5, onUpdate: () => camera.updateProjectionMatrix() }); } else if (intersects.length === 0 && zoomed) { zoomed = false; // 平滑恢复初始状态 gsap.to(camera.position, { x: initialCamera.pos.x, y: initialCamera.pos.y, duration: 0.5, onUpdate: () => camera.updateProjectionMatrix() }); gsap.to(camera, { zoom: initialCamera.zoom, duration: 0.5, onUpdate: () => camera.updateProjectionMatrix() }); } } document.addEventListener('mousemove', onMouseMove, false); // 窗口大小变化时更新相机参数,保证适配屏幕 window.addEventListener('resize', () => { camera.left = window.innerWidth / -2; camera.right = window.innerWidth / 2; camera.top = window.innerHeight / 2; camera.bottom = window.innerHeight / -2; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Render loop function render() { requestAnimationFrame(render); renderer.render(scene, camera); } render(); </script> </body> </html>
关键修改说明
- 删除了渲染循环中强制重置相机z轴的代码,避免覆盖动画效果
- 补全正交相机的远裁剪面参数,修复潜在渲染问题
- 使用正交相机的
zoom属性实现缩放,配合updateProjectionMatrix()更新投影 - 采用GSAP实现相机位置和缩放的平滑过渡,提升交互体验
- 为frame1材质添加
transparent: true,确保PNG透明区域正常显示 - 添加窗口大小监听,保证屏幕尺寸变化时相机参数自动适配
内容的提问来源于stack exchange,提问作者Zohaib Mateen
相关产品推荐
相关产品推荐

