如何在 iOS 上将 DeviceOrientationEvent.webkitCompassHeading 与关联的 beta 和 gamma 值结合使用?

Nig*_*l G 6 javascript ios three.js

我已将 DeviceOrientationControls 修改为朝北,而不是设备启动时的方向,虽然它可以在 Android 上运行,但我在 iOS 版本上运行时遇到了困难。这是我的修改版本

/**
 * @author richt / http://richt.me
 * @author WestLangley / http://github.com/WestLangley
 *
 * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html)
 */

THREE.DeviceOrientationControls = function ( object ) {

    var scope = this;

    this.object = object;
    this.object.rotation.reorder( 'YXZ' );

    this.enabled = true;

    this.deviceOrientation = {};
    this.screenOrientation = 0;

    this.alphaOffset = 0; // radians
    this.absolute = false;
    this.alphaOffsetDevice = undefined;
    this.alphaOffsetScreen = undefined;

    var onDeviceOrientationChangeEvent = function ( event ) {
        if(scope.absolute) return;

        scope.deviceOrientation = event;

    };

    var onDeviceOrientationAbsoluteChangeEvent = function ( event ) {

        scope.deviceOrientation = event;
        scope.absolute = true;

    };

    var onScreenOrientationChangeEvent = function () {

        scope.screenOrientation = window.orientation || 0;

    };

    // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''

    var setObjectQuaternion = function () {

        var zee = new THREE.Vector3( 0, 0, 1 );

        var euler = new THREE.Euler();

        var q0 = new THREE.Quaternion();

        var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis

        return function ( quaternion, alpha, beta, gamma, orient ) {

            euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us

            quaternion.setFromEuler( euler ); // orient the device

            quaternion.multiply( q1 ); // camera looks out the back of the device, not the top

            quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation

        };

    }();

    this.connect = function () {

        onScreenOrientationChangeEvent(); // run once on load

        window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
        window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
        window.addEventListener( 'deviceorientationabsolute', onDeviceOrientationAbsoluteChangeEvent, false );

        scope.enabled = true;

    };

    this.disconnect = function () {

        window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
        window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
        window.removeEventListener( 'deviceorientationabsolute', onDeviceOrientationAbsoluteChangeEvent, false );

        scope.enabled = false;

    };

    this.update = function () {
        if ( scope.enabled === false ) return;

        var device;
        device = scope.deviceOrientation;

        if ( device ) {

            var alpha = this.getDirection() ? THREE.Math.degToRad( this.getDirection() ) + scope.alphaOffset : 0; // Z

            var beta = device.beta ? THREE.Math.degToRad( device.beta ) : 0; // X'

            var gamma = device.gamma ? THREE.Math.degToRad( device.gamma ) : 0; // Y''

            var orient = scope.screenOrientation ? THREE.Math.degToRad( scope.screenOrientation ) : 0; // O

            setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );

        }

    };

    this.dispose = function () {

        scope.disconnect();

    };

    this.iOSOrientationPermission = function ()
    {
    if (typeof DeviceOrientationEvent.requestPermission === 'function') {
            DeviceOrientationEvent.requestPermission().then(permissionState => {
                if (permissionState === 'granted') {

                }

                console.log(permissionState);
            }).catch(console.error);
        }
    }

    this.getDirection = function ()
    {
        return (typeof scope.deviceOrientation.webkitCompassHeading != "undefined") ? scope.deviceOrientation.webkitCompassHeading : scope.deviceOrientation.alpha;
    }

    this.getDirectionMap = function ()
    {
        return (typeof scope.deviceOrientation.webkitCompassHeading != "undefined") ? (360 - scope.deviceOrientation.webkitCompassHeading) : scope.deviceOrientation.alpha;
    }

    this.connect();

};

Run Code Online (Sandbox Code Playgroud)

我已将 DeviceOrientationControls 中提及的 DeviceOrientationEvent.alpha 替换为函数 getDirection(),该函数在 Android 上返回 DeviceOrientationEvent.alpha,在 iOS 上返回 DeviceOrientationEvent.webkitCompassHeading,但这对 iOS 不起作用,反而会导致场景相机摇晃,如下所示手机沿 Y 轴旋转,或者手机倾斜超过地平线时。我在网上发现的关于如何使用 webkitCompassHeading 值的信息很少,而且我发现的很少建议用它来替换 alpha 值,如上所述,这在我的情况下不起作用。看起来 alpha/beta/gamma 值是在内部从四元数转换而来的,而 webkitCompassHeading 只是一个孤立的 0-360 值,因此与其他值一起使用并不那么直接。

我尝试过使用校准函数在启动时获取 webkitCompassHeading 并使用它来抵消 alpha,但这只能在大约 50% 的时间内提供正确的结果。由于在 Android 上,alpha 值会在内部随着标题值不断更新,因此我认为在 iOS 上执行类似操作的方法将是最好的方法,但此时我被难住了。在 Android 上我使用 Google Chrome,在 iOS 上使用 Safari。

这里有几个关于我正在使用的 API 的链接

https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent

https://developer.mozilla.org/en-US/docs/Web/API/Window/ondeviceorientation

https://developer.mozilla.org/en-US/docs/Web/API/Window/ondeviceorientationabsolute