我正在尝试创建custom look controls for A-Frame as per their documentation,并在TypeScript中编写此自定义组件,因为我正在使用tslint规则在Angular项目中工作,该规则禁止在类主体之外使用 this 。
控件注册正常,我可以在window.AFRAME.components [“ touch-controls”]中看到它们,但是 init 从未触发。这是我的自定义外观控件的代码
const AFRAME = (window as any).AFRAME;
const THREE = AFRAME.THREE;
const utils = AFRAME.utils;
const bind = utils.bind;
const PolyfillControls = utils.device.PolyfillControls;
// To avoid recalculation at every mouse movement tick
const PI_2 = Math.PI / 2;
const checkHasPositionalTracking = utils.device.checkHasPositionalTracking;
export class TouchControls {
attrName?: string;
data: any;
dependencies?: string[];
el: any;
id: string;
multiple?: boolean;
name: string;
schema: any;
system: any | undefined;
previousHMDPosition: any;
hmdQuaternion: any;
hmdEuler: any;
position: any;
savedRotation: any;
savedPosition: any;
polyfillObject: any;
polyfillControls: any;
rotation: any;
deltaRotation: any;
savedPose: any;
pointerLocked: boolean;
mouseDown: boolean;
pitchObject: any;
yawObject: any;
previousMouseEvent: any;
touchStart: any;
touchStarted: boolean;
hasSavedPose: boolean;
hasPositionalTracking: boolean;
constructor() {
console.log("Constructing touch controls");
this.dependencies = ["position", "rotation"];
this.schema = {
enabled: {
default: true
},
hmdEnabled: {
default: true
},
pointerLockEnabled: {
default: false
},
reverseMouseDrag: {
default: false
},
reverseTouchDrag: {
default: false
},
touchEnabled: {
default: true
}
};
}
init(data?: any): void {
console.log("Init da thing", data);
this.previousHMDPosition = new THREE.Vector3();
this.hmdQuaternion = new THREE.Quaternion();
this.hmdEuler = new THREE.Euler();
this.position = new THREE.Vector3();
// To save / restore camera pose
this.savedRotation = new THREE.Vector3();
this.savedPosition = new THREE.Vector3();
this.polyfillObject = new THREE.Object3D();
this.polyfillControls = new PolyfillControls(this.polyfillObject);
this.rotation = {};
this.deltaRotation = {};
this.savedPose = undefined;
this.pointerLocked = false;
this.setupMouseControls();
this.bindMethods();
this.savedPose = {
position: new THREE.Vector3(),
rotation: new THREE.Euler()
};
// Call enter VR handler if the scene has entered VR before the event listeners attached.
if (this.el.sceneEl.is("vr-mode")) {
this.onEnterVR();
}
}
update(oldData): void {
var data = this.data;
// Disable grab cursor classes if no longer enabled.
if (data.enabled !== oldData.enabled) {
this.updateGrabCursor(data.enabled);
}
// Reset pitch and yaw if disabling HMD.
if (oldData && !data.hmdEnabled && !oldData.hmdEnabled) {
this.pitchObject.rotation.set(0, 0, 0);
this.yawObject.rotation.set(0, 0, 0);
}
if (oldData && !data.pointerLockEnabled !== oldData.pointerLockEnabled) {
this.removeEventListeners();
this.addEventListeners();
if (this.pointerLocked) {
(document as any).exitPointerLock();
}
}
}
tick(time: number, timeDelta: number): void {
var data = this.data;
if (!data.enabled) {
return;
}
this.updateOrientation();
}
play(): void {
this.addEventListeners();
}
pause(): void {
this.removeEventListeners();
}
remove(): void {
this.removeEventListeners();
}
extendSchema(update: any): void {
console.log("extendSchema");
}
flushToDOM(): void {
console.log("flushToDOM");
}
bindMethods() {
this.onMouseDown = bind(this.onMouseDown, this);
this.onMouseMove = bind(this.onMouseMove, this);
this.onMouseUp = bind(this.onMouseUp, this);
this.onTouchStart = bind(this.onTouchStart, this);
this.onTouchMove = bind(this.onTouchMove, this);
this.onTouchEnd = bind(this.onTouchEnd, this);
this.onEnterVR = bind(this.onEnterVR, this);
this.onExitVR = bind(this.onExitVR, this);
this.onPointerLockChange = bind(this.onPointerLockChange, this);
this.onPointerLockError = bind(this.onPointerLockError, this);
}
/**
* Set up states and Object3Ds needed to store rotation data.
*/
setupMouseControls() {
this.mouseDown = false;
this.pitchObject = new THREE.Object3D();
this.yawObject = new THREE.Object3D();
this.yawObject.position.y = 10;
this.yawObject.add(this.pitchObject);
}
/**
* Add mouse and touch event listeners to canvas.
*/
addEventListeners() {
var sceneEl = this.el.sceneEl;
var canvasEl = sceneEl.canvas;
// Wait for canvas to load.
if (!canvasEl) {
sceneEl.addEventListener("render-target-loaded", bind(this.addEventListeners, this));
return;
}
// Mouse events.
canvasEl.addEventListener("mousedown", this.onMouseDown, false);
window.addEventListener("mousemove", this.onMouseMove, false);
window.addEventListener("mouseup", this.onMouseUp, false);
// Touch events.
canvasEl.addEventListener("touchstart", this.onTouchStart);
window.addEventListener("touchmove", this.onTouchMove);
window.addEventListener("touchend", this.onTouchEnd);
// sceneEl events.
sceneEl.addEventListener("enter-vr", this.onEnterVR);
sceneEl.addEventListener("exit-vr", this.onExitVR);
// Pointer Lock events.
if (this.data.pointerLockEnabled) {
document.addEventListener("pointerlockchange", this.onPointerLockChange, false);
document.addEventListener("mozpointerlockchange", this.onPointerLockChange, false);
document.addEventListener("pointerlockerror", this.onPointerLockError, false);
}
}
/**
* Remove mouse and touch event listeners from canvas.
*/
removeEventListeners() {
var sceneEl = this.el.sceneEl;
var canvasEl = sceneEl && sceneEl.canvas;
if (!canvasEl) {
return;
}
// Mouse events.
canvasEl.removeEventListener("mousedown", this.onMouseDown);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
// Touch events.
canvasEl.removeEventListener("touchstart", this.onTouchStart);
window.removeEventListener("touchmove", this.onTouchMove);
window.removeEventListener("touchend", this.onTouchEnd);
// sceneEl events.
sceneEl.removeEventListener("enter-vr", this.onEnterVR);
sceneEl.removeEventListener("exit-vr", this.onExitVR);
// Pointer Lock events.
document.removeEventListener("pointerlockchange", this.onPointerLockChange, false);
document.removeEventListener("mozpointerlockchange", this.onPointerLockChange, false);
document.removeEventListener("pointerlockerror", this.onPointerLockError, false);
}
/**
* Update orientation for mobile, mouse drag, and headset.
* Mouse-drag only enabled if HMD is not active.
*/
updateOrientation() {
var el = this.el;
var hmdEuler = this.hmdEuler;
var pitchObject = this.pitchObject;
var yawObject = this.yawObject;
var sceneEl = this.el.sceneEl;
// In VR mode, THREE is in charge of updating the camera rotation.
if (sceneEl.is("vr-mode") && sceneEl.checkHeadsetConnected()) {
return;
}
// Calculate polyfilled HMD quaternion.
this.polyfillControls.update();
hmdEuler.setFromQuaternion(this.polyfillObject.quaternion, "YXZ");
// On mobile, do camera rotation with touch events and sensors.
el.object3D.rotation.x = hmdEuler.x + pitchObject.rotation.x;
el.object3D.rotation.y = hmdEuler.y + yawObject.rotation.y;
}
/**
* Translate mouse drag into rotation.
*
* Dragging up and down rotates the camera around the X-axis (yaw).
* Dragging left and right rotates the camera around the Y-axis (pitch).
*/
onMouseMove(event) {
var direction;
var movementX;
var movementY;
var pitchObject = this.pitchObject;
var previousMouseEvent = this.previousMouseEvent;
var yawObject = this.yawObject;
// Not dragging or not enabled.
if (!this.data.enabled || (!this.mouseDown && !this.pointerLocked)) {
return;
}
// Calculate delta.
if (this.pointerLocked) {
movementX = event.movementX || event.mozMovementX || 0;
movementY = event.movementY || event.mozMovementY || 0;
} else {
movementX = event.screenX - previousMouseEvent.screenX;
movementY = event.screenY - previousMouseEvent.screenY;
}
this.previousMouseEvent = event;
// Calculate rotation.
direction = this.data.reverseMouseDrag ? 1 : -1;
yawObject.rotation.y += movementX * 0.002 * direction;
pitchObject.rotation.x += movementY * 0.002 * direction;
pitchObject.rotation.x = Math.max(-PI_2, Math.min(PI_2, pitchObject.rotation.x));
}
/**
* Register mouse down to detect mouse drag.
*/
onMouseDown(evt) {
if (!this.data.enabled) {
return;
}
// Handle only primary button.
if (evt.button !== 0) {
return;
}
var sceneEl = this.el.sceneEl;
var canvasEl = sceneEl && sceneEl.canvas;
this.mouseDown = true;
this.previousMouseEvent = evt;
this.showGrabbingCursor();
if (this.data.pointerLockEnabled && !this.pointerLocked) {
if (canvasEl.requestPointerLock) {
canvasEl.requestPointerLock();
} else if (canvasEl.mozRequestPointerLock) {
canvasEl.mozRequestPointerLock();
}
}
}
/**
* Shows grabbing cursor on scene
*/
showGrabbingCursor() {
this.el.sceneEl.canvas.style.cursor = "grabbing";
}
/**
* Hides grabbing cursor on scene
*/
hideGrabbingCursor() {
this.el.sceneEl.canvas.style.cursor = "";
}
/**
* Register mouse up to detect release of mouse drag.
*/
onMouseUp() {
this.mouseDown = false;
this.hideGrabbingCursor();
}
/**
* Register touch down to detect touch drag.
*/
onTouchStart(evt) {
if (evt.touches.length !== 1 || !this.data.touchEnabled) {
return;
}
this.touchStart = {
x: evt.touches[0].pageX,
y: evt.touches[0].pageY
};
this.touchStarted = true;
}
/**
* Translate touch move to Y-axis rotation.
*/
onTouchMove(evt) {
var direction;
var canvas = this.el.sceneEl.canvas;
var deltaY;
var yawObject = this.yawObject;
if (!this.touchStarted || !this.data.touchEnabled) {
return;
}
deltaY = 2 * Math.PI * (evt.touches[0].pageX - this.touchStart.x) / canvas.clientWidth;
direction = this.data.reverseTouchDrag ? 1 : -1;
// Limit touch orientaion to to yaw (y axis).
yawObject.rotation.y -= deltaY * 0.5 * direction;
this.touchStart = {
x: evt.touches[0].pageX,
y: evt.touches[0].pageY
};
}
/**
* Register touch end to detect release of touch drag.
*/
onTouchEnd() {
this.touchStarted = false;
}
/**
* Save pose.
*/
onEnterVR() {
this.saveCameraPose();
}
/**
* Restore the pose.
*/
onExitVR() {
this.restoreCameraPose();
this.previousHMDPosition.set(0, 0, 0);
}
/**
* Update Pointer Lock state.
*/
onPointerLockChange() {
this.pointerLocked = !!((document as any).pointerLockElement || (document as any).mozPointerLockElement);
}
/**
* Recover from Pointer Lock error.
*/
onPointerLockError() {
this.pointerLocked = false;
}
/**
* Toggle the feature of showing/hiding the grab cursor.
*/
updateGrabCursor(enabled) {
var sceneEl = this.el.sceneEl;
function enableGrabCursor() {
sceneEl.canvas.classList.add("a-grab-cursor");
}
function disableGrabCursor() {
sceneEl.canvas.classList.remove("a-grab-cursor");
}
if (!sceneEl.canvas) {
if (enabled) {
sceneEl.addEventListener("render-target-loaded", enableGrabCursor);
} else {
sceneEl.addEventListener("render-target-loaded", disableGrabCursor);
}
return;
}
if (enabled) {
enableGrabCursor();
return;
}
disableGrabCursor();
}
/**
* Save camera pose before entering VR to restore later if exiting.
*/
saveCameraPose() {
var el = this.el;
var hasPositionalTracking = this.hasPositionalTracking !== undefined ? this.hasPositionalTracking : checkHasPositionalTracking();
if (this.hasSavedPose || !hasPositionalTracking) {
return;
}
this.savedPose.position.copy(el.object3D.position);
this.savedPose.rotation.copy(el.object3D.rotation);
this.hasSavedPose = true;
}
/**
* Reset camera pose to before entering VR.
*/
restoreCameraPose() {
var el = this.el;
var savedPose = this.savedPose;
var hasPositionalTracking = this.hasPositionalTracking !== undefined ? this.hasPositionalTracking : checkHasPositionalTracking();
if (!this.hasSavedPose || !hasPositionalTracking) {
return;
}
// Reset camera orientation.
el.object3D.position.copy(savedPose.position);
el.object3D.rotation.copy(savedPose.rotation);
this.hasSavedPose = false;
}
}
这就是我注册组件的方式
import { TouchControls } from "../../aframe/touch-controls";
const AFRAME = (window as any).AFRAME;
AFRAME.registerComponent("touch-controls", new TouchControls());
最后是模板
<a-scene>
<a-entity touch-controls>
<a-animation
attribute="rotation"
dur="3000"
fill="forwards"
from="0 00 0"
to="0 10 0"
repeat="0">
</a-animation>
</a-entity>
<a-assets #assetLoader>
<img id="render-asset" crossorigin="anonymous" [src]="renderPath">
</a-assets>
<a-sky src="#render-asset"></a-sky>
</a-scene>
答案 0 :(得分:1)
从docs到source code,我认为AFRAME.registerComponent
严格要求使用JSON对象作为组件定义。
var definition = {
init: function() {},
update: function() {}
}
AFRAME.registerComponent("name", definition)
如果您的类具有返回JSON内容的方法,则该方法可能有效,但否则src/core/component.js
必须知道如何解释另一种类型的对象。