特定div未在指定位置渲染:Three.js鸟群动画布局异常求助
问题分析与解决方案
我一眼就揪出问题根源了——你的JavaScript代码里有个多余的DOM操作,直接把目标容器从原本的结构里“拽”出来塞到了body末尾,这就是#canvas-div跑到#home1后面的罪魁祸首!
核心问题代码:
container = document.getElementById("canvas-div"), document.body.appendChild(container)
你先通过getElementById获取了已经存在于.canvas-wrapper里的#canvas-div,紧接着又调用document.body.appendChild(container)——DOM元素不能同时存在于两个父节点下,这行代码会把#canvas-div从.canvas-wrapper里移除,再追加到body的最后面,自然就出现在#home1之后了。
完整修复步骤:
- 删除多余的
appendChild操作:既然#canvas-div已经在HTML结构里了,完全不需要再把它加到body里。 - 修正CSS语法错误:你的
#canvas-divCSS里background:width:100%;是无效语法,直接删掉;同时调整定位逻辑,确保容器固定在首屏顶部。 - 修复窗口resize的尺寸逻辑:原来的代码把渲染器尺寸设为窗口大小,会导致动画超出容器,改成适配容器尺寸。
修正后的完整代码
HTML:
<div class="canvas-wrapper"> <div id="canvas-div"></div> </div> <div id="home1"></div>
CSS:
.canvas-wrapper { /* 用vh单位更直观控制首屏占比,这里设为10%高度 */ height: 10vh; position: relative; /* 确保容器不会被其他元素挤走 */ width: 100%; } #canvas-div { width: 100%; height: 100%; margin: 0; padding: 0; position: relative; top: 0; left: 0; background-color: grey; overflow: hidden; } #home1 { height: 300px; width: 100%; }
JavaScript:
function init() { // 只获取容器,不要执行appendChild操作 container = document.getElementById("canvas-div"); camera = new THREE.PerspectiveCamera(75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000); camera.position.z = 450; scene = new THREE.Scene(); birds = []; boids = []; for (var i = 0; i < 30; i++) { boid = boids[i] = new Boid(); boid.position.x = 400 * Math.random() - 200; boid.position.y = 400 * Math.random() - 200; boid.position.z = 400 * Math.random() - 200; boid.velocity.x = 2 * Math.random() - 1; boid.velocity.y = 2 * Math.random() - 1; boid.velocity.z = 2 * Math.random() - 1; boid.setAvoidWalls(true); boid.setWorldSize(500, 500, 400); bird = birds[i] = new THREE.Mesh( new Bird(), new THREE.MeshBasicMaterial({ color: 0xffffff * Math.random(), side: THREE.DoubleSide }) ); bird.phase = Math.floor(62.83 * Math.random()); scene.add(bird); } renderer = new THREE.CanvasRenderer({ alpha: true }); renderer.setClearColor(0xffffff, 0); // 用容器的实际尺寸设置渲染器大小 renderer.setSize(container.offsetWidth, container.offsetHeight); container.appendChild(renderer.domElement); window.addEventListener("resize", onWindowResize, false); } function onWindowResize() { // 窗口变化时,根据容器尺寸更新相机和渲染器 const containerWidth = container.offsetWidth; const containerHeight = container.offsetHeight; camera.aspect = containerWidth / containerHeight; camera.updateProjectionMatrix(); renderer.setSize(containerWidth, containerHeight); } function onDocumentMouseMove(i) { var t = new THREE.Vector3(i.clientX - SCREEN_WIDTH_HALF, -i.clientY + SCREEN_HEIGHT_HALF, 0); for (var o = 0, e = boids.length; o < e; o++) { boid = boids[o]; t.z = boid.position.z; boid.repulse(t); } } function animate() { requestAnimationFrame(animate); render(); } function render() { for (var i = 0, t = birds.length; i < t; i++) { boid = boids[i]; boid.run(boids); bird = birds[i]; bird.position.copy(boids[i].position); color = bird.material.color; color.r = color.g = color.b = 0xcccccc; bird.rotation.y = Math.atan2(-boid.velocity.z, boid.velocity.x); bird.rotation.z = Math.asin(boid.velocity.y / boid.velocity.length()); bird.phase = (bird.phase + (Math.max(0, bird.rotation.z) + 0.1)) % 62.83; bird.geometry.vertices[5].y = bird.geometry.vertices[4].y = 5 * Math.sin(bird.phase); } renderer.render(scene, camera); } var Boid = function() { var i, t, o = new THREE.Vector3(), e = 500, n = 500, s = 200, r = 100, a = 4, d = 0.1, c = false; this.position = new THREE.Vector3(); this.velocity = new THREE.Vector3(); i = new THREE.Vector3(); this.setGoal = function(i) { t = i; }; this.setAvoidWalls = function(i) { c = i; }; this.setWorldSize = function(i, t, o) { e = i; n = t; s = o; }; this.run = function(t) { if (c) { o.set(-e, this.position.y, this.position.z); o = this.avoid(o); o.multiplyScalar(5); i.add(o); o.set(e, this.position.y, this.position.z); o = this.avoid(o); o.multiplyScalar(5); i.add(o); o.set(this.position.x, -n, this.position.z); o = this.avoid(o); o.multiplyScalar(5); i.add(o); o.set(this.position.x, n, this.position.z); o = this.avoid(o); o.multiplyScalar(5); i.add(o); o.set(this.position.x, this.position.y, -s); o = this.avoid(o); o.multiplyScalar(5); i.add(o); o.set(this.position.x, this.position.y, s); o = this.avoid(o); o.multiplyScalar(5); i.add(o); } if (Math.random() > 0.5) this.flock(t); this.move(); }; this.flock = function(o) { if (t) i.add(this.reach(t, 0.005)); i.add(this.alignment(o)); i.add(this.cohesion(o)); i.add(this.separation(o)); }; this.move = function() { this.velocity.add(i); var t = this.velocity.length(); if (t > a) this.velocity.divideScalar(t / a); this.position.add(this.velocity); i.set(0, 0, 0); }; this.checkBounds = function() { if (this.position.x > e) this.position.x = -e; if (this.position.x < -e) this.position.x = e; if (this.position.y > n) this.position.y = -n; if (this.position.y < -n) this.position.y = n; if (this.position.z > s) this.position.z = -s; if (this.position.z < -s) this.position.z = s; }; this.avoid = function(i) { var t = new THREE.Vector3(); t.copy(this.position); t.sub(i); t.multiplyScalar(1 / this.position.distanceToSquared(i)); return t; }; this.repulse = function(t) { var o = this.position.distanceTo(t); if (o < 150) { var e = new THREE.Vector3(); e.subVectors(this.position, t); e.multiplyScalar(0.5 / o); i.add(e); } }; this.reach = function(i, t) { var o = new THREE.Vector3(); o.subVectors(i, this.position); o.multiplyScalar(t); return o; }; this.alignment = function(i) { var t, o = new THREE.Vector3(), e = 0, n = 0, s = i.length; for (; n < s; n++) { if (Math.random() > 0.6) continue; t = i[n]; distance = t.position.distanceTo(this.position); if (distance > 0 && distance <= r) { o.add(t.velocity); e++; } } if (e > 0) { o.divideScalar(e); var a = o.length(); if (a > d) o.divideScalar(a / d); } return o; }; this.cohesion = function(i) { var t, o, e = new THREE.Vector3(), n = new THREE.Vector3(), s = 0, a = 0, c = i.length; for (; a < c; a++) { if (Math.random() > 0.6) continue; t = i[a]; o = t.position.distanceTo(this.position); if (o > 0 && o <= r) { e.add(t.position); s++; } } if (s > 0) { e.divideScalar(s); n.subVectors(e, this.position); var h = n.length(); if (h > d) n.divideScalar(h / d); } return n; }; this.separation = function(i) { var t, o, e = new THREE.Vector3(), n = new THREE.Vector3(), s = 0, a = i.length; for (; s < a; s++) { if (Math.random() > 0.6) continue; t = i[s]; o = t.position.distanceTo(this.position); if (o > 0 && o <= r) { n.subVectors(this.position, t.position); n.normalize(); n.divideScalar(o); e.add(n); } } return e; }; }; var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight, SCREEN_WIDTH_HALF = SCREEN_WIDTH / 2, SCREEN_HEIGHT_HALF = SCREEN_HEIGHT / 2, camera, scene, renderer, birds, bird, boid, boids, stats; init(); animate();
额外优化说明:
- 我把JS里紧凑的逗号表达式拆分成了换行写法,同时把
!0/!1改成了更易读的true/false,30 > i调整为常规的i < 30。 - 修正了CSS里的无效
background语法,改为标准的background-color属性。 - 窗口resize时现在会根据容器的实际尺寸更新相机比例和渲染器大小,确保动画始终适配容器范围。
这样修改后,#canvas-div就会老老实实待在.canvas-wrapper里,固定在首屏顶部,不会再被移到#home1后面了!
内容的提问来源于stack exchange,提问作者Phillip Dews
相关产品推荐
相关产品推荐

