使用用TypeScript编写的自定义组件自定义A框架外观控件?

时间:2018-11-09 09:59:26

标签: angular typescript aframe

我正在尝试创建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>

1 个答案:

答案 0 :(得分:1)

docssource code,我认为AFRAME.registerComponent严格要求使用JSON对象作为组件定义。

var definition = {
  init: function() {},
  update: function() {}
}
AFRAME.registerComponent("name", definition)

如果您的类具有返回JSON内容的方法,则该方法可能有效,但否则src/core/component.js必须知道如何解释另一种类型的对象。