需求:拖拽右侧面板,里面的three模型能够自适应
import { useEffect, useState, useRef } from 'react'
import './App.css'
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { debounce } from 'lodash';
const CanvasDemo = () => {let camera, scene, renderer, model, face;const [leftWidth, setLeftWidth] = useState(300);const leftWidthRef = useRef(leftWidth);const containerRef = useRef(null);const rightRef = useRef(null);const isDragging = useRef(false);// 更新 leftWidth 时同步更新 leftWidthRefuseEffect(() => {leftWidthRef.current = leftWidth;}, [leftWidth]);const handleMouseDown = () => {isDragging.current = true;};const handleMouseMove = (e) => {if (!isDragging.current) return;const containerRect = containerRef?.current?.getBoundingClientRect();const newLeftWidth = e.clientX - containerRect.left;setLeftWidth(newLeftWidth);};const handleMouseUp = () => {isDragging.current = false;};const init = () => {camera = new THREE.PerspectiveCamera(45, (window.innerWidth - leftWidthRef.current) / window.innerHeight, 0.25, 100);camera.position.set(- 5, 3, 10);camera.lookAt(0, 2, 0);scene = new THREE.Scene();scene.background = new THREE.Color(0xe0e0e0);scene.fog = new THREE.Fog(0xe0e0e0, 20, 100);const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3);hemiLight.position.set(0, 20, 0);scene.add(hemiLight);const dirLight = new THREE.DirectionalLight(0xffffff, 3);dirLight.position.set(0, 20, 10);scene.add(dirLight);const mesh = new THREE.Mesh(new THREE.PlaneGeometry(2000, 2000), new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }));mesh.rotation.x = - Math.PI / 2;scene.add(mesh);const grid = new THREE.GridHelper(200, 40, 0x000000, 0x000000);grid.material.opacity = 0.2;grid.material.transparent = true;grid.position.set(0, 0, 0); // 将网格放置在场景中心scene.add(grid);const loader = new GLTFLoader();loader.load('https://threejs.org/examples/models/gltf/RobotExpressive/RobotExpressive.glb', function (gltf) {model = gltf.scene;scene.add(model);// 打印模型信息,调试用console.log('Model loaded:', model);}, undefined, function (e) {console.error(e);});renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth - leftWidthRef.current, window.innerHeight);rightRef.current?.appendChild(renderer.domElement);}const debouncedResize = debounce(() => {onWindowResize();}, 10); // 100ms 防抖const onWindowResize = () => {if (!camera || !renderer) return;camera.aspect = (window.innerWidth - leftWidthRef.current) / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth - leftWidthRef.current, window.innerHeight);// 确保动画持续运行requestAnimationFrame(animate);}useEffect(() => {init();animate(); // 启动渲染循环if (rightRef.current) {const resizeObserver = new ResizeObserver(() => {if (rightRef.current) {debouncedResize();}});resizeObserver.observe(rightRef.current);return () => resizeObserver.disconnect();}}, [])const animate = () => {requestAnimationFrame(animate);renderer.render(scene, camera);};return (<divref={containerRef}className="container"onMouseMove={handleMouseMove}onMouseUp={handleMouseUp}onMouseLeave={handleMouseUp}><div className="left-pane" style={{ width: leftWidth }}>左侧内容</div><div className="divider" onMouseDown={handleMouseDown}></div><div ref={rightRef} className="right-pane"></div></div>);
}export default CanvasDemo
.container {display: flex;height: 100vh;user-select: none;
}
.left-pane {background-color: #f0f0f0;overflow: auto;
}
.divider {width: 5px;cursor: ew-resize;background-color: #ccc;
}
.right-pane {flex-grow: 1;background-color: #e0e0e0;overflow: auto;
}
问题1:页面宽度变化第一时间都是window.onresize的事件,然而,resize 事件只在 window 对象(即由 document.defaultView 返回)上触发。只有在 window 对象上注册的处理器才能接收 resize 事件。
所以替换方法为:
const resizeObserver = new ResizeObserver(() => {});
resizeObserver.observe(dom);
return () => resizeObserver.disconnect();
问题2:拖拽的时候,渲染模型会白屏闪烁,==>解决:加个防抖
const debouncedResize = debounce(() => {
onWindowResize();
}, 100); // 100ms 防抖
以上就是解决思路