🥜
Expo Motion Sensorsを使ってみた
Expo Motion Sensors Demo
このプロジェクトは、Expoを使用してモバイルデバイスの加速度センサーを視覚的に表示するデモアプリケーションです。デバイスの傾きを矢印で直感的に表示します。
機能
- デバイスの傾きをリアルタイムで検出
- 傾きの方向と強さを視覚的に表示(SVGを使用)
- 傾きの角度と強度を数値で表示
必要な環境
- Node.js (v20以上)
- Expo CLI
- iOS/Androidデバイス、またはシミュレータ
使用しているパッケージ
- expo-sensors: デバイスの加速度センサーにアクセス
- react-native-svg: 傾きを視覚的に表示するためのSVGコンポーネント
セットアップ手順
- プロジェクトをクローン
git clone https://github.com/sakurakotubaki/expo-motion-sensors
cd expo-motion-sensors
- 依存パッケージをインストール
bun install
- アプリを起動
bun run start
- ExpoGoアプリでQRコードをスキャン
- iOSの場合:カメラアプリでQRコードをスキャン
- Androidの場合:ExpoGoアプリでQRコードをスキャン
コードの解説
センサーの初期化と購読
const _subscribe = () => {
setSubscription(
Accelerometer.addListener(accelerometerData => {
setData(accelerometerData);
})
);
Accelerometer.setUpdateInterval(100);
};
加速度センサーのデータを100ミリ秒ごとに更新し、状態を更新します。
傾きの計算
const calculateAngle = () => {
const angle = Math.atan2(y, x);
return angle * (180 / Math.PI);
};
const calculateTiltStrength = () => {
return Math.min(Math.sqrt(x * x + y * y), 1);
};
-
calculateAngle
: x軸とy軸の値から傾きの角度を計算 -
calculateTiltStrength
: 傾きの強さを0-1の範囲で計算
視覚的な表示
SVGを使用して、円と矢印で傾きを表示します:
- 外側の円:基準となる円
- 緑の矢印:デバイスの傾きの方向と強さを表示
- 中心点:基準点
注意事項
- 実機でのテストを推奨(シミュレータでは加速度センサーの値が正確に取得できない場合があります)
- デバイスを平らな場所に置いた状態が初期状態となります
デモアプリの作成
bunx create-expo-app expo-motion-sensors -t expo-template-blank-typescript
add package:
bun add expo-sensors
bun add react-native-svg
権限の許可をする。
app.json
{
"expo": {
"name": "expo-motion-sensors",
"slug": "expo-motion-sensors",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/favicon.png"
},
"plugins": [
[
"expo-sensors",
{
"motionPermission": " モーションセンサーへのアクセスを許可します。"
}
]
]
}
}
example
App.tsx
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import { Accelerometer } from 'expo-sensors';
import { useEffect, useState } from 'react';
import { Subscription } from 'expo-sensors/build/Pedometer';
import Svg, { Circle, Line, Path } from 'react-native-svg';
const CIRCLE_RADIUS = 120;
const INDICATOR_LENGTH = 100;
export default function App() {
const [{ x, y, z }, setData] = useState({
x: 0,
y: 0,
z: 0,
});
const [subscription, setSubscription] = useState<Subscription | null>(null);
useEffect(() => {
_subscribe();
return () => _unsubscribe();
}, []);
const _subscribe = () => {
setSubscription(
Accelerometer.addListener(accelerometerData => {
setData(accelerometerData);
})
);
Accelerometer.setUpdateInterval(100);
};
const _unsubscribe = () => {
subscription?.remove();
setSubscription(null);
};
// 加速度からx,y軸の角度を計算
const calculateAngle = () => {
const angle = Math.atan2(y, x);
return angle * (180 / Math.PI);
};
// 傾きの強さを計算(0-1の範囲)
const calculateTiltStrength = () => {
return Math.min(Math.sqrt(x * x + y * y), 1);
};
const renderCompass = () => {
const centerX = CIRCLE_RADIUS;
const centerY = CIRCLE_RADIUS;
const angle = calculateAngle();
const strength = calculateTiltStrength();
// 傾きに基づいて線の終点を計算
const endX = centerX + Math.cos(angle * Math.PI / 180) * INDICATOR_LENGTH * strength;
const endY = centerY + Math.sin(angle * Math.PI / 180) * INDICATOR_LENGTH * strength;
// 矢印の頭のサイズ
const arrowSize = 15;
// 傾き方向の矢印の頭を計算
const arrowAngle = Math.atan2(endY - centerY, endX - centerX);
const arrowPoint1X = endX - arrowSize * Math.cos(arrowAngle - Math.PI / 6);
const arrowPoint1Y = endY - arrowSize * Math.sin(arrowAngle - Math.PI / 6);
const arrowPoint2X = endX - arrowSize * Math.cos(arrowAngle + Math.PI / 6);
const arrowPoint2Y = endY - arrowSize * Math.sin(arrowAngle + Math.PI / 6);
return (
<Svg height={CIRCLE_RADIUS * 2} width={CIRCLE_RADIUS * 2}>
{/* 外側の円 */}
<Circle
cx={centerX}
cy={centerY}
r={CIRCLE_RADIUS - 10}
stroke="#333"
strokeWidth="2"
fill="none"
/>
{/* 中心から傾きを示す線と矢印 */}
<Line
x1={centerX}
y1={centerY}
x2={endX}
y2={endY}
stroke="#4CAF50"
strokeWidth="3"
/>
<Path
d={`M ${endX} ${endY} L ${arrowPoint1X} ${arrowPoint1Y} L ${arrowPoint2X} ${arrowPoint2Y} Z`}
fill="#4CAF50"
/>
{/* 中心点 */}
<Circle
cx={centerX}
cy={centerY}
r={5}
fill="#4CAF50"
/>
</Svg>
);
};
return (
<View style={styles.container}>
<View style={styles.compassContainer}>
{renderCompass()}
</View>
<View style={styles.dataContainer}>
<Text>傾き強度: {calculateTiltStrength().toFixed(2)}</Text>
<Text>角度: {calculateAngle().toFixed(0)}°</Text>
</View>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
compassContainer: {
marginBottom: 20,
},
dataContainer: {
alignItems: 'center',
},
});
感想
加速度センサーなるものを試したかったがこれはではなかったかも😅
カメラで物体を撮影して速度測るアプリ作りたかったのですが、機械学習の技術が必要らしいのを後で知ったので断念しました。何かパッケージありそうだが。
Discussion