我有一个 Three.js 应用程序,但遇到了问题。我有几个文件:
main.js:
它看起来与this视频中的代码完全一样。我所做的只是将所有内容包装到一个函数中,这样我就可以动态加载 5 个 3D 模型,而不是创建 5 个执行相同操作的 js 文件。
React 组件:
import { displayObject } from "../main"
export default function Atraction() {
setTimeout(() => { displayObject(myArgs) }, 3000)
return (
<canvas className="webgl-1"></canvas>
)
}
我得到错误:
无法读取 null 的属性(读取“宽度”)
它指的是我要访问的
canvas
元素。我该怎么办?
所以,很难从小片段中准确地弄清楚你的代码是什么样子的。但是,您提供的内容可能会得到改进。如果页面加载时间超过 3 秒(例如网速较慢),会发生什么情况? setTimeout 将中断,因为 html 尚未呈现。另外,如果他们的计算机在 500 毫秒内加载对象,你真的希望人们必须等待 3 秒才能加载对象吗?
useEffect 在 DOM 渲染完成或重新加载时立即运行。可以肯定地说我们只想建立场景一次,所以我们可以在 useEffect 钩子的开始使用一个守卫来确保它只运行一次(当组件第一次渲染时)。
此外,对于 React 函数式组件,我们可以使用 useRef 来获取 DOM 的钩子。虽然从技术上讲,我们可以使用组件的 id 来呈现我们的 ThreeJS 代码,但使用 useRef() 钩子会告诉每个程序员我们稍后将编辑 DOM——它传达了有助于其他程序员阅读的意图。
let rendered = false;
export function Component() {
const ref = useRef() // this allows us to refer to the dom element once it has been created
// useeffect happens as soon as Component() has rendered to html
useEffect(() => {
if (rendered) { // this makes sure the scene isn't created more than once even if Component() changes/re-renders
return;
}
rendered = true;
const scene = new THREE.Scene();
const renderer = new THREE.Renderer(... etc.);
// ... setup scene/camera/renderer ect.
// best to separate loading/adding-to-scene out of one function to avoid brittle code
const car = loadObject('etc/car.gltf');
scene.add(car)
const house = loadObject('etc/house.gltf');
scene.add(house)
// since the useEffect only runs once dom has rendered, ref will now refer to the dom
ref.current.appendChild(renderer.domElement)
});
return <div ref={ref} ></div>
}
function loadObject(objectLocation) {
// load object at objectLocation
return loadedObject
}
另外,如果我没记错的话,
ref.current.appendChild(renderer.domElement)
自己创建一个新的画布组件并将其放入目标组件中。因此,根组件(如我所展示的)可能最好只是一个
<div />
。 (需要引用)
因为我还没有看到你的其余代码:如果这对你没有帮助,请发布更多代码,我会更新我的答案以更好地帮助你。