如何将Three.js画布放入指定HTML元素?解决画布移至body末尾问题
问题原因与解决方案
你遇到的问题根源有两个:
- 多余的
document.body.appendChild(container);语句,导致已存在于DOM中的#canvas_3d元素被移动到body末尾 - Renderer直接设置了全屏尺寸,没有匹配目标容器的实际大小
详细解释
当你通过document.getElementById('canvas_3d')获取到页面上已存在的div元素后,又执行document.body.appendChild(container),这会触发DOM的移动逻辑——因为一个元素不能同时存在于DOM树的两个位置,浏览器会自动把这个div从它原本在section里的位置,转移到body的最后。
另外,你的Renderer用了window.innerWidth和window.innerHeight作为尺寸,这会让画布铺满整个窗口,即使它嵌套在#canvas_3d容器里,也会超出容器范围,视觉上也会出现错位问题。
修改步骤
- 删除多余的append语句:去掉
document.body.appendChild(container);这一行,目标容器已经在HTML结构里了,不需要再手动添加到body - 匹配容器尺寸:把Renderer的尺寸设置为容器的宽高,而不是全屏尺寸
- 修复resize逻辑:当窗口大小变化时,同步更新容器尺寸和相机、Renderer的参数
修改后的完整代码
HTML部分(保持不变)
<section class="lc_hero hero_fulHeight" style="background-image: url('img/hero_top.jpg');"> <div id="canvas_3d"></div> <div class="container"> <div class="row"> <%# <div class="col-md-1"></div> %> <div class="col-md-7"> <div class="lc_hero__heading"> <h1 class="head_white"><span>L</span>aboratório de <span>In</span>ovação e <span>C</span>ompetições de <span>E</span>ngenharia</h1> <p>Projetos que vão além da <span>faculdade!</span></p> </div> </div> </div> <div class="lc_hero__scroll_down"> <%= svg "scroll_down" %> </div> </div> </section>
JavaScript部分
var mouseX = 0, mouseY = 0, windowHalfX = window.innerWidth / 2, windowHalfY = window.innerHeight / 2, SEPARATION = 200, AMOUNTX = 10, AMOUNTY = 10, camera, scene, renderer; init(); animate(); function init() { var container, separation = 100, amountX = 50, amountY = 50, particles, particle; var container = document.getElementById('canvas_3d'); // 移除多余的body.appendChild语句 // 相机比例改为容器的宽高比 camera = new THREE.PerspectiveCamera(40, container.clientWidth / container.clientHeight, 1, 10000); camera.position.z = 100; scene = new THREE.Scene(); renderer = new THREE.CanvasRenderer(); renderer.setPixelRatio(window.devicePixelRatio); // 使用容器的尺寸设置Renderer大小 renderer.setSize(container.clientWidth, container.clientHeight); container.appendChild(renderer.domElement); // particles var PI2 = Math.PI * 2; var material = new THREE.SpriteCanvasMaterial({ color: 0xffffff, program: function (context) { context.beginPath(); context.arc(0, 0, 0.5, 0, PI2, true); context.fill(); } }); var points = []; for (var i = 0; i < 100; i++) { particle = new THREE.Sprite(material); particle.position.x = Math.random() * 2 - 1; particle.position.y = Math.random() * 2 - 1; particle.position.z = Math.random() * 2 - 1; particle.position.normalize(); particle.position.multiplyScalar(Math.random() * 10 + 450); particle.scale.x = particle.scale.y = 10; scene.add(particle); points.push(particle.position); } // lines var geometry = new THREE.BufferGeometry().setFromPoints(points); var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 0.5 })); scene.add(line); document.addEventListener('mousemove', onDocumentMouseMove, false); document.addEventListener('touchstart', onDocumentTouchStart, false); document.addEventListener('touchmove', onDocumentTouchMove, false); // 恢复resize事件监听,确保窗口变化时更新尺寸 window.addEventListener('resize', onWindowResize, false); } function onWindowResize() { var container = document.getElementById('canvas_3d'); // 更新相机比例和Renderer尺寸 camera.aspect = container.clientWidth / container.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(container.clientWidth, container.clientHeight); } function onDocumentMouseMove(event) { mouseX = event.clientX - windowHalfX; mouseY = event.clientY - windowHalfY; } function onDocumentTouchStart(event) { if (event.touches.length > 1) { event.preventDefault(); mouseX = event.touches[0].pageX - windowHalfX; mouseY = event.touches[0].pageY - windowHalfY; } } function onDocumentTouchMove(event) { if (event.touches.length == 1) { event.preventDefault(); mouseX = event.touches[0].pageX - windowHalfX; mouseY = event.touches[0].pageY - windowHalfY; } } function animate() { requestAnimationFrame(animate); render(); } function render() { camera.position.x += (mouseX - camera.position.x) * .05; camera.position.y += (-mouseY + 200 - camera.position.y) * .05; camera.lookAt(scene.position); renderer.render(scene, camera); }
额外提示
如果你的#canvas_3d容器需要全屏或者固定尺寸,记得给它添加CSS样式,比如:
#canvas_3d { width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none; /* 可选,避免画布遮挡下方元素的交互 */ }
这样能确保容器本身的尺寸正确,Renderer也能匹配上容器的大小。
内容的提问来源于stack exchange,提问作者Felipe Marcon
相关产品推荐
相关产品推荐

