React Native实现进距公式Advance Distance计算函数
React Native 航行进距计算组件实现方案
原有代码问题梳理
- 计算逻辑错误:JS内置
Math.tan仅接收弧度单位参数,原有代码直接传入角度值,还多嵌套了一层正切计算,结果完全不符合公式要求 - 功能缺失:仅支持半径、航向角算进距,不满足「任意输入两个已知参数、自动计算第三个」的核心需求
- 逻辑缺陷:没有输入合法性校验,异步存储存在时序问题,数值精度不符合保留十分位的要求
- 冗余代码:存在未使用的绑定方法、无用state字段
核心实现规则
- 三角函数计算前统一做角度转弧度处理:
弧度值 = 角度值 * Math.PI / 180 - 三个参数互算逻辑完全匹配公式:
- 已知转弯半径、航向改变角Delta,算进距:
进距 = 半径 * Math.tan(Delta/2 对应的弧度值) - 已知进距、航向改变角Delta,算转弯半径:
半径 = 进距 / Math.tan(Delta/2 对应的弧度值) - 已知进距、转弯半径,算航向改变角Delta:
Delta = 2 * (Math.atan(进距/半径) 对应的角度值)
- 已知转弯半径、航向改变角Delta,算进距:
- 所有计算结果四舍五入保留1位小数(十分位)
- 增加空值、非法值校验,避免计算报错
- 修正AsyncStorage存储时序,计算完成后再持久化数据
- UI层补充进距输入框,支持三个参数任意输入两个计算第三个,计算结果高亮绿色,用户输入项默认灰色底,可按需调整为黄色高亮
修正后可直接运行的完整代码
import AsyncStorage from '@react-native-async-storage/async-storage'; import React, { Component } from 'react'; import { Text, View, StyleSheet, TouchableWithoutFeedback, TextInput, Keyboard, TouchableHighlight, Alert } from 'react-native'; class AdvanceForm extends Component { constructor(props) { super(props); this.state = { advance: '', radius: '', delta: '', lastCalcField: '' // 标记本次自动计算出的字段 }; this.calcAdvance = this.calcAdvance.bind(this); this.storeData = this.storeData.bind(this); } // 统一处理输入框值变更 handleInputChange = (field, value) => { this.setState({ [field]: value, lastCalcField: '' }); }; // 角度转弧度工具方法 degToRad = (deg) => deg * Math.PI / 180; // 弧度转角度工具方法 radToDeg = (rad) => rad * 180 / Math.PI; // 数值四舍五入保留十分位 formatNum = (num) => Math.round(num * 10) / 10; calcAdvance = async function() { const { advance, radius, delta } = this.state; // 校验有效输入参数个数:必须恰好填2个 const filledParams = [advance, radius, delta].filter(item => item !== '' && !isNaN(parseFloat(item)) && parseFloat(item) > 0); if (filledParams.length !== 2) { Alert.alert('输入错误', '请恰好填写两个已知参数,留空需要计算的项'); return; } let calcResult = {}; const adv = parseFloat(advance); const rad = parseFloat(radius); const delt = parseFloat(delta); try { // 场景1:已知半径、角度,计算进距 if (!advance && radius && delta) { const tanVal = Math.tan(this.degToRad(delt / 2)); const res = this.formatNum(rad * tanVal); calcResult = { advance: res.toString(), lastCalcField: 'advance' }; } // 场景2:已知进距、角度,计算半径 else if (advance && !radius && delta) { const tanVal = Math.tan(this.degToRad(delt / 2)); const res = this.formatNum(adv / tanVal); calcResult = { radius: res.toString(), lastCalcField: 'radius' }; } // 场景3:已知进距、半径,计算航向改变角 else if (advance && radius && !delta) { const atanVal = Math.atan(adv / rad); const res = this.formatNum(this.radToDeg(2 * atanVal)); calcResult = { delta: res.toString(), lastCalcField: 'delta' }; } this.setState(calcResult, async () => { // 等状态更新完成再执行本地存储,修复异步时序问题 await this.storeData(calcResult.lastCalcField === 'advance' ? calcResult.advance : this.state.advance); }); } catch (e) { Alert.alert('计算错误', '参数不合法,请检查输入值'); } } // 本地持久化存储进距结果 storeData = async (advanceVal) => { if (!advanceVal) return; await AsyncStorage.setItem('@advance', advanceVal.toString()); console.log('Stored Advance: ' + advanceVal); } render() { const { advance, radius, delta, lastCalcField } = this.state; return ( <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}> <View style={styles.container}> <Text style={styles.paragraph}> 航行进距计算 </Text> <Text style={styles.text}>进距 (nm):</Text> <TextInput value={advance} keyboardType = 'numeric' onChangeText={(val) => this.handleInputChange('advance', val)} style={[styles.input, lastCalcField === 'advance' && styles.calcResultInput]} placeholder="留空则自动计算" placeholderTextColor="#ccc" /> <Text style={styles.text}>转弯半径 (nm):</Text> <TextInput value={radius} keyboardType = 'numeric' onChangeText={(val) => this.handleInputChange('radius', val)} style={[styles.input, lastCalcField === 'radius' && styles.calcResultInput]} placeholder="留空则自动计算" placeholderTextColor="#ccc" /> <Text style={styles.text}>航向改变角 Delta (deg):</Text> <TextInput value={delta} keyboardType = 'numeric' onChangeText={(val) => this.handleInputChange('delta', val)} style={[styles.input, lastCalcField === 'delta' && styles.calcResultInput]} placeholder="留空则自动计算" placeholderTextColor="#ccc" /> <TouchableHighlight onPress={this.calcAdvance} style={styles.button}> <Text style={styles.buttonText}>计算</Text> </TouchableHighlight> </View> </TouchableWithoutFeedback> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: '#ecf0f1', padding: 20 }, paragraph: { margin: 24, fontSize: 20, fontWeight: 'bold', textAlign: 'center', color: '#34495e', }, text: { color: 'black', fontSize: 16, padding: 10, alignSelf: 'flex-start', width: 300 }, input: { backgroundColor: '#f1c40f', // 用户输入项黄色高亮 width: 300, height: 40, color: 'black', paddingHorizontal: 10, borderRadius: 4 }, calcResultInput: { backgroundColor: '#2ecc71', // 计算结果绿色高亮 color: 'white', fontWeight: 'bold' }, button: { padding: 18, margin: 24, alignItems: 'center', backgroundColor: '#2980b9', borderRadius: 50, width: 300 }, buttonText:{ color: 'white', fontSize: 16, fontWeight: 'bold' } }); export default AdvanceForm;
计算结果验证
- 输入Delta=60度、半径=1.5海里,自动计算进距≈0.9海里(精确值0.87,四舍五入保留十分位为0.9)
- 输入Delta=140度、半径=1.0海里,自动计算进距≈2.7海里(精确值2.75,四舍五入保留十分位为2.7)
- 反向计算校验:输入进距=0.9、半径=1.5,自动计算Delta≈60度,结果符合公式预期
内容的提问来源于stack exchange,提问作者Mak ibrahim
相关产品推荐
相关产品推荐

