在不改变方向的情况下反应 Native Expo 方向监听器

React Native Expo orientation listener without changing orientation

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

如果我没理解错的话,要求是获取设备方向,即使实际方向没有改变,因为它被 orientation 锁定。

您可以使用 DeviceMotion module to get the device rotation (Euler angle)

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() which will return a Promise with one of the possible values

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

实例:https://snack.expo.io/@moshfeu/ff4c76

我发现 another solution on GitHub, similar to 使用 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 方法的侦听器中调用它 (it's good practice to check sensor availability first with 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'
   });
  });
 }

为了让它正常工作,我需要:

  • 将代码添加到 componentDidMount:

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

  // ...
}

  • 将代码添加到 componentWillUnmount:

componentWillUnmount() {
  DeviceMotion.removeAllListeners()
}

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。这表明 right-side-up 图像需要旋转多少照片。

matchPhotoRotation = (photo) => {
  const {rotate} = this.state

  if (rotate === 0) return photo

  return ImageManipulator.manipulateAsync(
    photo.uri,
    [{rotate}],
  )
}