在rnmapbox上实现根据手机方向旋转的定位功能

1.问题描述

最近通过React Native开发安卓app的时候有个需求,是需要实现一个可以跟着手机旋转的类似地图APP上导航的那种箭头定位图标。

2.插件选择

实现该功能主要用到两个库,一个React Native Sensors传感器库和一个工具库rxjs,至于rn和rnmapbox就不赘述了,该文章重点不是这俩。

2.1React Native Sensors

react-native-sensors是一个用于在React Native应用程序中访问传感器数据的库。它提供了对设备内置传感器的访问,包括加速度计、陀螺仪和磁力计。

2.2RxJS

RxJS 是使用 Observables 的响应式编程的库,它使编写异步或基于回调的代码更容易。

3.代码实现

3.1安装依赖

安装依赖就不多讲了,通过npm或者yarn添加依赖:

npm install react-native-sensors
npm install rxjs

3.2权限申明

在使用react-native-sensors之前需要在AndroidManifest.xml中申请权限来确保可以读取相关的硬件数据:

    <!--    传感器权限-->
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
    <!--    传感器权限-->

3.3插件引入

主要使用了react-native-sensors插件的加速度计和磁力计功能,以及rxjs的一些方法:

import { accelerometer, magnetometer } from "react-native-sensors";
import { throttleTime, combineLatestWith, map } from "rxjs/operators";
import { from } from 'rxjs';

3.4主体代码

主要就是通过react-native-sensors插件的加速度计和磁力计返回的真机数据来计算出偏转角度,然后通过React Native的Animated改变角度以及RxJS的一些方法来提高性能。

export function MyLocation() {
  const location = useLocation() as Position
  const [heading, setHeading] = useState(0);
  const [rotation, setRotation] = useState(new Animated.Value(0));

  useEffect(() => {
    // 使用Animated提高性能
    Animated.timing(rotation, {
      toValue: heading,
      duration: 0, // 瞬间变化
      useNativeDriver: true,
    }).start();
  }, [heading])

  useEffect(() => {
    // 加速度计
    const accObservable = from(accelerometer);
    // 磁力计
    const magObservable = from(magnetometer);

    const subscription = accObservable.pipe(
      combineLatestWith(magObservable),
      map(([accelerometerData, magnetometerData]) => {
        const { x: ax, y: ay, z: az } = accelerometerData;
        const { x: mx, y: my, z: mz } = magnetometerData;

        // Normalize the accelerometer vector
        const normA = Math.sqrt(ax * ax + ay * ay + az * az);
        const aX = ax / normA;
        const aY = ay / normA;
        const aZ = az / normA;

        // Calculate the horizontal component of the magnetic field
        const hX = mx - aX * (mx * aX + my * aY + mz * aZ);
        const hY = my - aY * (mx * aX + my * aY + mz * aZ);
        const normH = Math.sqrt(hX * hX + hY * hY);
        const hXNorm = hX / normH;
        const hYNorm = hY / normH;

        // Calculate the heading in radians
        let headingRad = Math.atan2(hYNorm, hXNorm);

        // Convert heading to degrees
        let headingDeg = headingRad * (180 / Math.PI);

        // Adjust for negative angles
        if (headingDeg < 0) {
          headingDeg += 360;
        }

        return headingDeg;
      }),
      // 加节流,不用那么频繁
      throttleTime(250),
    ).subscribe(heading => {
      setHeading(Number(heading.toFixed(1)))
    })
    return () => {
      subscription.unsubscribe();
    }
  }, [])

  return (
    location ? (
      <MarkerView anchor={{ y: 0.5, x: 0 }} coordinate={location}>
        <Animated.View
          style={{
            transform: [{ rotate: rotation.interpolate({ inputRange: [0, 360], outputRange: ['0deg', '360deg'] }) }]
          }}
        >
          <Image
            source={导航图标图片的地址}
          />
        </Animated.View>
      </MarkerView>
    ) : null
  )
}

4.参考文献

API·React Native Sensors
如何使用React Native Sensors库:全面指南
RxJS中文文档

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容