Three.js PlaneGeometry适配窗口:保持宽高比并置于左上角
解决方案
1. 确定宽高比并计算平面尺寸
先定义旗帜的原始宽高比(根据你的实际需求调整,示例为2:1),以窗口高度为基准计算平面宽度:
// 自定义旗帜宽高比(宽度/高度) const aspectRatio = 2; // 获取当前窗口高度 const windowHeight = window.innerHeight; // 计算平面尺寸:高度适配窗口高度,宽度按比例推导 const planeHeight = windowHeight; const planeWidth = planeHeight * aspectRatio; // 创建适配后的PlaneGeometry const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight);
2. 将平面定位到左上角
Three.js默认场景原点在中心,使用正交相机更适合固定UI元素的位置计算,视口直接覆盖整个窗口:
// 创建正交相机,视口范围与窗口一致 const camera = new THREE.OrthographicCamera( -window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.1, 1000 ); // 调整平面位置:左上角对应相机视口的(-窗口宽/2 + 平面宽/2, 窗口高/2 - 平面高/2) plane.position.set( -window.innerWidth / 2 + planeWidth / 2, window.innerHeight / 2 - planeHeight / 2, 0 );
如果使用透视相机,需要结合相机FOV和距离计算位置,但正交相机的逻辑更直观,适合这类固定UI需求。
3. 监听窗口resize事件,动态更新适配效果
窗口尺寸变化时,重新计算平面尺寸、相机参数和位置:
window.addEventListener('resize', () => { const newWindowHeight = window.innerHeight; const newPlaneHeight = newWindowHeight; const newPlaneWidth = newPlaneHeight * aspectRatio; // 更新平面几何体缩放比例 geometry.scale(newPlaneWidth / planeWidth, newPlaneHeight / planeHeight, 1); planeWidth = newPlaneWidth; planeHeight = newPlaneHeight; // 更新正交相机视口参数 camera.left = -window.innerWidth / 2; camera.right = window.innerWidth / 2; camera.top = window.innerHeight / 2; camera.bottom = -window.innerHeight / 2; camera.updateProjectionMatrix(); // 更新平面位置 plane.position.set( -window.innerWidth / 2 + newPlaneWidth / 2, window.innerHeight / 2 - newPlaneHeight / 2, 0 ); // 更新渲染器尺寸 renderer.setSize(window.innerWidth, window.innerHeight); });
4. 整合到Angular组件(完整示例)
在Angular生命周期钩子中初始化Three.js逻辑,同时注意组件销毁时清理监听和资源:
import { Component, AfterViewInit, OnDestroy } from '@angular/core'; import * as THREE from 'three'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit, OnDestroy { private scene!: THREE.Scene; private camera!: THREE.OrthographicCamera; private renderer!: THREE.WebGLRenderer; private plane!: THREE.Mesh; private aspectRatio = 2; // 自定义旗帜宽高比 private planeWidth!: number; private planeHeight!: number; private resizeListener!: () => void; ngAfterViewInit(): void { this.initThreeJS(); this.animate(); } ngOnDestroy(): void { window.removeEventListener('resize', this.resizeListener); this.renderer.dispose(); } private initThreeJS(): void { // 初始化场景 this.scene = new THREE.Scene(); // 初始化相机 const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; this.camera = new THREE.OrthographicCamera( -windowWidth / 2, windowWidth / 2, windowHeight / 2, -windowHeight / 2, 0.1, 1000 ); this.camera.position.z = 5; // 计算平面尺寸 this.planeHeight = windowHeight; this.planeWidth = this.planeHeight * this.aspectRatio; // 创建平面(替换为你的飘动材质) const geometry = new THREE.PlaneGeometry(this.planeWidth, this.planeHeight); const material = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide }); this.plane = new THREE.Mesh(geometry, material); // 定位平面到左上角 this.plane.position.set( -windowWidth / 2 + this.planeWidth / 2, windowHeight / 2 - this.planeHeight / 2, 0 ); this.scene.add(this.plane); // 初始化渲染器 this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(windowWidth, windowHeight); document.body.appendChild(this.renderer.domElement); // 绑定resize监听 this.resizeListener = () => this.onWindowResize(); window.addEventListener('resize', this.resizeListener); } private onWindowResize(): void { const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; // 更新相机参数 this.camera.left = -windowWidth / 2; this.camera.right = windowWidth / 2; this.camera.top = windowHeight / 2; this.camera.bottom = -windowHeight / 2; this.camera.updateProjectionMatrix(); // 更新平面尺寸与位置 const newPlaneHeight = windowHeight; const newPlaneWidth = newPlaneHeight * this.aspectRatio; this.plane.scale.set(newPlaneWidth / this.planeWidth, newPlaneHeight / this.planeHeight, 1); this.planeWidth = newPlaneWidth; this.planeHeight = newPlaneHeight; this.plane.position.set( -windowWidth / 2 + this.planeWidth / 2, windowHeight / 2 - this.planeHeight / 2, 0 ); // 更新渲染器 this.renderer.setSize(windowWidth, windowHeight); } private animate(): void { requestAnimationFrame(() => this.animate()); // 在此添加旗帜飘动的动画逻辑(如修改顶点位置) this.renderer.render(this.scene, this.camera); } }
内容的提问来源于stack exchange,提问作者nasarhon
相关产品推荐
相关产品推荐

