在不改变方向的情况下回应本地世博会方向听众



我正在尝试在React Native应用程序上检测当前方向,而无需更改方向。我做了一个相机应用程序,我只想在方向改变时翻转图标。现在我不能在不改变实际方向的情况下使用ScreenOrientation.addOrientationChangeListener(listener)。有办法解决这个问题吗?

如果我理解正确,即使实际方向由于被orientation锁定而没有更改,也需要获得设备方向。

您可以使用DeviceMotion模块获取设备旋转(欧拉角(

精确的计算来自https://github.com/expo/expo/issues/2430#issuecomment-538074235

function orientationCalculation(gamma: number, beta: number) {
let ABSOLUTE_GAMMA = Math.abs(gamma);
let ABSOLUTE_BETA = Math.abs(beta);
let isGammaNegative = Math.sign(gamma) == -1;
let orientation = 0;
if (ABSOLUTE_GAMMA <= 0.04 && ABSOLUTE_BETA <= 0.24) {
//Portrait mode, on a flat surface.
orientation = 0;
} else if (
(ABSOLUTE_GAMMA <= 1.0 || ABSOLUTE_GAMMA >= 2.3) &&
ABSOLUTE_BETA >= 0.5
) {
//General Portrait mode, accounting for forward and back tilt on the top of the phone.
orientation = 0;
} else {
if (isGammaNegative) {
//Landscape mode with the top of the phone to the left.
orientation = -90;
} else {
//Landscape mode with the top of the phone to the right.
orientation = 90;
}
}
return orientation;
}
DeviceMotion.addListener(({ orientation, rotation }) => {
setOrientation(
orientationCalculation(rotation.gamma, rotation.beta) === 0
? 'protrait'
: 'landscape'
);
});

世博会演示:https://expo.dev/@moshfeu/通过设备运动检测方向?serviceType=classic&分销=expo-go

源代码:https://github.com/moshfeu/detect-orientation-by-device-motion/


旧答案

DeviceMotion.addListener(({rotation}) => {
const alpha = Math.abs(rotation.alpha);
this.setState({
orientation: alpha > 3 || (alpha > 0 && alpha < 0.5) ? 'landscape' : 'protrait'
});
});

现场演示:https://expo.io/@moshfeu/snack-dc115508-fab4-4be4-818a-5a03f89725bd


最旧答案

如果我理解正确,你想在用户不改变方向的情况下加载应用程序的当前方向。

如果是这样,您可以从ScreenOrientation.getOrientationAsync()获得它,它将返回一个Promise,其中可能有一个值

ScreenOrientation.getOrientationAsync().then(data => this.setState({data}));

现场示例:https://snack.expo.io/@moshfeu/ff4c76

为了让它工作起来,我需要:

  • 向componentDidMount添加代码:

async componentDidMount() {
if (await DeviceMotion.isAvailableAsync()) {
DeviceMotion.addListener(this.calculateRotation)
}
// ...
}

  • 将代码添加到组件WillUnmount:

componentWillUnmount() {
DeviceMotion.removeAllListeners()
}

  • 根据github上的一个有用答案,添加一种基于贝塔和伽马计算旋转的方法:

calculateRotation = ({rotation: {beta, gamma}}) => {
let absGamma = Math.abs(gamma)
let absBeta = Math.abs(beta)
let rotate = 0
if (absGamma <= 0.04 && absBeta <= 0.24) {
// Portrait mode, on a flat surface.
rotate = 0
} else if ((absGamma <= 1.0 || absGamma >= 2.3) && absBeta >= 0.5) {
// General Portrait mode, accounting for forward and back tilt on the top of the phone.
rotate = 0
} else {
if (gamma < 0) {
// Landscape mode with the top of the phone to the left.
rotate = -90
} else {
// Landscape mode with the top of the phone to the right.
rotate = 90
}
}
this.setState({rotate})
}

  • 当我需要获得当前轮换时,调用this.state.rotate。这告诉一张照片需要旋转多少才能获得右侧图像

matchPhotoRotation = (photo) => {
const {rotate} = this.state
if (rotate === 0) return photo
return ImageManipulator.manipulateAsync(
photo.uri,
[{rotate}],
)
}

我在GitHub上找到了另一个解决方案,类似于Mosh Feu使用DeviceMotion的答案,但使用gamma和beta(x和y轴(而不是alpha(z轴(来计算设备的旋转。就我而言,我用这种技术取得了更令人满意的结果。

isPortrait(gamma: number, beta: number) {
const ABSOLUTE_GAMMA = Math.abs(gamma);
const ABSOLUTE_BETA = Math.abs(beta);
const isGammaNegative = Math.sign(gamma) === -1;
if (ABSOLUTE_GAMMA <= 0.04 && ABSOLUTE_BETA <= 0.24) {
//Portrait mode, on a flat surface.
return true;
} else 
if (
(ABSOLUTE_GAMMA <= 1.0 || ABSOLUTE_GAMMA >= 2.3) &&
ABSOLUTE_BETA >= 0.5
) {
//General Portrait mode, accounting for forward and back tilt on the top of the phone.
return true;
} else {
if (isGammaNegative) {
//Landscape mode with the top of the phone to the left.
return false;
} else {
//Landscape mode with the top of the phone to the right.
return false;
}
}
}

我在监听器中从componentDidMount方法调用它(最好先用DeviceMotion检查传感器的可用性。isAvailableAsync(((:

const sensorAvailable = await DeviceMotion.isAvailableAsync();
if (sensorAvailable) {
DeviceMotion.addListener(({ rotation }) => {
const { gamma, beta } = rotation;
this.setState({
orientation: this.isPortrait(gamma, beta) ? 'portrait' : 'landscape'
});
});
}

我的解决方案基于@Mosh Feu的回答,希望它能帮助

DeviceMotion.addListener(({ rotation }) => {
let ABSOLUTE_GAMMA = Math.abs(rotation.gamma);
if (ABSOLUTE_GAMMA > Math.PI / 2) {
const _gamma = ABSOLUTE_GAMMA - Math.PI;
ABSOLUTE_GAMMA = Math.abs(_gamma);
}
const ABSOLUTE_BETA = Math.abs(rotation.beta);
const gap = ABSOLUTE_BETA - ABSOLUTE_GAMMA;
if (gap > 0.5) {
console.log("portrait");
} else if (gap < -0.5) {
console.log("landscape");
} else {
console.log("unknow");
}
});

最新更新