我的情况是这样的:我有一个包含多个资产的资产清单,我希望每当用户将鼠标悬停在它们之上时就使用 OrbitController 开始渲染(我更喜欢轨迹球,但我知道这是不可能的,因为一个错误)。重点是类实例的“this”变量不能传入类的render方法中:
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js';
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/examples/jsm/controls/OrbitControls.js';
class Main {
constructor(canvasId)
{
this.canvas = document.querySelector(canvasId);
this.renderer = new THREE.WebGLRenderer({canvas: this.canvas});
this.fov = 75;
this.aspect = 2; // the canvas default
this.near = 0.1;
this.far = 5;
this.camera = new THREE.PerspectiveCamera(this.fov, this.aspect, this.near, this.far);
this.camera.position.z = 2;
this.controls = new OrbitControls(this.camera, this.canvas);
this.controls.target.set(0, 0, 0);
this.controls.update();
this.scene = new THREE.Scene();
this.color = 0xFFFFFF;
this.intensity = 1;
this.light = new THREE.DirectionalLight(this.color, this.intensity);
this.light.position.set(-1, 2, 4);
this.scene.add(this.light);
this.boxWidth = 1;
this.boxHeight = 1;
this.boxDepth = 1;
this.geometry = new THREE.BoxGeometry(this.boxWidth, this.boxHeight, this.boxDepth);
}
makeInstance(geometry, color, x){
let material = new THREE.MeshPhongMaterial(color);
let cube = new THREE.Mesh(geometry, material);
this.scene.add(cube);
cube.position.x = x;
return cube;
}
resizeRendererToDisplaySize(renderer) {
this.canvas = this.renderer.domElement;
let width = this.canvas.clientWidth;
let height = this.canvas.clientHeight;
let needResize = this.canvas.width !== width || this.canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
render() {
// this -> control object or window whereas it should be the class instance
if (this.resizeRendererToDisplaySize(this.renderer)) {
this.canvas = this.renderer.domElement;
this.camera.aspect = this.canvas.clientWidth / this.canvas.clientHeight;
this.camera.updateProjectionMatrix();
}
this.renderer.render(this.scene, this.camera);
}
starter() {
this.makeInstance(this.geometry, 0x44aa88, 0);
this.makeInstance(this.geometry, 0x8844aa, -2);
this.makeInstance(this.geometry, 0xaa8844, 2);
this.render();
this.controls.addEventListener('change', this.render);
window.addEventListener('resize', this.render);
// note: this is a workaround for an OrbitControls issue
// in an iframe. Will remove once the issue is fixed in
// three.js
window.addEventListener('mousedown', (e) => {
e.preventDefault();
window.focus();
});
window.addEventListener('keydown', (e) => {
e.preventDefault();
});
}
}
let main = new Main('#a');
main.starter();
我唯一能做的(但不正确)是用“main”实例替换渲染方法中的“this”
render() {
if (main.resizeRendererToDisplaySize(this.renderer)) {
main.canvas = main.renderer.domElement;
main.camera.aspect = main.canvas.clientWidth / main.canvas.clientHeight;
main.camera.updateProjectionMatrix();
}
main.renderer.render(main.scene, main.camera);
}
但是,我以这种方式杀死了该类,并且无法生成其他实例,例如主2 我已经生成了一个代码笔:https://codepen.io/jimver04/pen/XWjLPLo
答案 0 :(得分:2)
您正在向事件处理程序提供 this.render
。 this.render
在技术上是指向原型函数 (Main.prototype.render
) 的指针。
当处理程序被触发时,上下文将取决于源如何执行处理程序。在大多数情况下,提供给处理程序的上下文将是源的上下文(甚至可能是 null
)。例如,window.addEventListener( 'resize', handler );
将通过调用等效的 handler.call( window, event );
有几种方法可以解决这个问题。
this
箭头函数使用定义它们的上下文的 this
。因此,如果您的箭头函数是在您的类的成员函数中定义的,并且该成员函数正在该类的实例的上下文中执行,那么该箭头函数体内的 this
将引用该实例。
class Test{
constructor(){
this.name = "Test"
// Arrow function defined within the context of an instance:
window.addEventListener( 'resize', () => {
console.log( this.name ) // prints: "Test"
} )
// Normal function will execute with "window" as its "this"
window.addEventListener( 'resize', function() {
console.log( this.name ) // results may vary based on your browser, but "this" is not an instance of Test
} )
}
}
使用 bind
可以将函数附加到指定的上下文。 bind
函数返回一个永久绑定到提供的上下文的新函数。
class Test{
constructor(){
this.name = "Test"
this.boundRender = this.render.bind( this ) // returns "render" bound permanently to "this"
window.addEventListener( 'resize', this.boundRender )
}
render(){
console.log( this.name )
}
}
上面的例子仍然会在控制台打印“Test”。尽管 resize
事件想要使用 window
上下文执行处理程序,但 bind
已覆盖该处理程序并将处理程序锁定为在其绑定到的实例的上下文中执行。
答案 1 :(得分:0)
感谢 TheJim01!下面是使用 bind 方法的最终解决方案(带箭头的解决方案不起作用)。也可以在 https://codepen.io/jimver04/pen/XWjLPLo
中解析找到import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js';
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/examples/jsm/controls/OrbitControls.js';
class Main {
constructor(canvasId)
{
this.canvas = document.querySelector(canvasId);
this.renderer = new THREE.WebGLRenderer({canvas: this.canvas});
this.fov = 75;
this.aspect = 2; // the canvas default
this.near = 0.1;
this.far = 5;
this.camera = new THREE.PerspectiveCamera(this.fov, this.aspect, this.near, this.far);
this.camera.position.z = 2;
this.controls = new OrbitControls(this.camera, this.canvas);
this.controls.target.set(0, 0, 0);
this.controls.update();
this.scene = new THREE.Scene();
this.color = 0xFFFFFF;
this.intensity = 1;
this.light = new THREE.DirectionalLight(this.color, this.intensity);
this.light.position.set(-1, 2, 4);
this.scene.add(this.light);
this.boxWidth = 1;
this.boxHeight = 1;
this.boxDepth = 1;
this.geometry = new THREE.BoxGeometry(this.boxWidth, this.boxHeight, this.boxDepth);
this.boundRender = this.render.bind( this );
}
makeInstance(geometry, color, x){
let material = new THREE.MeshPhongMaterial(color);
let cube = new THREE.Mesh(geometry, material);
this.scene.add(cube);
cube.position.x = x;
return cube;
}
resizeRendererToDisplaySize(renderer) {
this.canvas = this.renderer.domElement;
let width = this.canvas.clientWidth;
let height = this.canvas.clientHeight;
let needResize = this.canvas.width !== width || this.canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
render() {
if (this.resizeRendererToDisplaySize(this.renderer)) {
this.canvas = this.renderer.domElement;
this.camera.aspect = this.canvas.clientWidth / this.canvas.clientHeight;
this.camera.updateProjectionMatrix();
}
this.renderer.render(this.scene, this.camera);
}
starter() {
this.makeInstance(this.geometry, 0x44aa88, 0);
this.makeInstance(this.geometry, 0x8844aa, -2);
this.makeInstance(this.geometry, 0xaa8844, 2);
this.render();
this.controls.addEventListener('change', this.boundRender );
window.addEventListener('resize', this.boundRender);
// note: this is a workaround for an OrbitControls issue
// in an iframe. Will remove once the issue is fixed in
// three.js
window.addEventListener('mousedown', (e) => {
e.preventDefault();
window.focus();
});
window.addEventListener('keydown', (e) => {
e.preventDefault();
});
}
}
let main = new Main('#a');
main.starter();
let main2 = new Main('#b');
main2.starter();