在这里插入图片描述

今天我们用 React Native 实现一个摩斯密码转换工具,支持文本和摩斯密码的双向转换,还带有完整的摩斯密码对照表。

摩斯密码映射表

import React, { useState, useRef } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, Animated } from 'react-native';

const morseMap: { [key: string]: string } = {
  'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....',
  'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.',
  'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
  'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-',
  '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', ' ': '/'
};

const reverseMorse = Object.fromEntries(Object.entries(morseMap).map(([k, v]) => [v, k]));

摩斯密码的核心是字符和编码的映射关系。

映射表设计:用对象存储 26 个字母、10 个数字、1 个空格的摩斯编码。键是字符,值是摩斯编码。比如 'A': '.-' 表示字母 A 的摩斯编码是 .-

点和划:摩斯密码用点(.)和划(-)表示。点是短信号,划是长信号,长度是点的 3 倍。字母之间用空格分隔,单词之间用 / 分隔。

反向映射reverseMorse 是反向映射表,键是摩斯编码,值是字符。用 Object.fromEntriesObject.entries 配合 map 实现键值对调。这样就能从摩斯编码反向查找字符。

为什么用对象而不是数组?因为对象的查找是 O(1) 复杂度,数组的查找是 O(n)。虽然只有 37 个字符,性能差异不大,但对象的语义更清晰。

状态设计

export const MorseCode: React.FC = () => {
  const [text, setText] = useState('');
  const [morse, setMorse] = useState('');
  
  const buttonAnim1 = useRef(new Animated.Value(1)).current;
  const buttonAnim2 = useRef(new Animated.Value(1)).current;
  const outputAnim = useRef(new Animated.Value(1)).current;

状态设计非常简洁:

文本状态

  • text:普通文本,用户输入或从摩斯密码转换得到
  • morse:摩斯密码,从文本转换得到或用户输入

动画值

  • buttonAnim1:第一个按钮(文本转摩斯)的缩放动画
  • buttonAnim2:第二个按钮(摩斯转文本)的缩放动画
  • outputAnim:摩斯密码输入框的缩放动画

为什么两个按钮用独立的动画值?因为用户可能快速点击不同的按钮,独立的动画值让每个按钮的动画互不干扰。

动画函数

  const animateButton = (anim: Animated.Value) => {
    Animated.sequence([
      Animated.timing(anim, { toValue: 0.9, duration: 100, useNativeDriver: true }),
      Animated.spring(anim, { toValue: 1, friction: 3, useNativeDriver: true }),
    ]).start();
    
    Animated.sequence([
      Animated.timing(outputAnim, { toValue: 0.95, duration: 100, useNativeDriver: true }),
      Animated.spring(outputAnim, { toValue: 1, friction: 4, useNativeDriver: true }),
    ]).start();
  };

这个函数同时触发两个动画:按钮动画和输出框动画。

按钮动画:传入的 anim 参数指定要动画化的按钮。先缩小到 90%(100ms),再弹回到 100%。friction: 3 让弹簧动画有明显的回弹效果。

输出框动画:摩斯密码输入框缩小到 95%,再弹回到 100%。friction: 4 比按钮的摩擦力大,弹性稍弱,动画更稳定。

为什么输出框缩小到 95% 而按钮缩小到 90%?因为输出框比按钮大,相同的缩放比例在大元素上更明显。95% 的缩放刚刚好,既能让用户注意到变化,又不会太夸张。

两个动画同时启动,给用户"点击按钮 → 按钮缩放 → 输出更新"的完整反馈。

文本转摩斯密码

  const textToMorse = () => {
    animateButton(buttonAnim1);
    const result = text.toUpperCase().split('').map(c => morseMap[c] || c).join(' ');
    setMorse(result);
  };

文本转摩斯密码的算法很简单:

转大写toUpperCase() 把文本转成大写,因为摩斯密码不区分大小写,映射表只有大写字母。

拆分字符split('') 把字符串拆成字符数组,每个字符独立处理。

映射转换map(c => morseMap[c] || c) 遍历每个字符,在映射表中查找对应的摩斯编码。如果找到(比如 'A' 找到 '.-'),返回摩斯编码;如果找不到(比如标点符号),返回原字符。

拼接结果join(' ') 用空格把摩斯编码拼接起来。每个字符的摩斯编码之间用空格分隔,这是摩斯密码的标准格式。

示例"HELLO"['H', 'E', 'L', 'L', 'O']['....', '.', '.-..', '.-..', '---']".... . .-.. .-.. ---"

摩斯密码转文本

  const morseToText = () => {
    animateButton(buttonAnim2);
    const result = morse.split(' ').map(m => reverseMorse[m] || m).join('');
    setText(result);
  };

摩斯密码转文本是反向过程:

拆分编码split(' ') 按空格分割,得到每个字符的摩斯编码数组。

反向映射map(m => reverseMorse[m] || m) 遍历每个摩斯编码,在反向映射表中查找对应的字符。如果找到(比如 '.-' 找到 'A'),返回字符;如果找不到,返回原编码。

拼接结果join('') 直接拼接,不加分隔符,因为文本字符之间不需要分隔。

示例".... . .-.. .-.. ---"['....', '.', '.-..', '.-..', '---']['H', 'E', 'L', 'L', 'O']"HELLO"

空格处理:如果摩斯密码中有 /(表示单词分隔),会被转换成空格,因为映射表中 ' ': '/',反向映射就是 '/': ' '

鸿蒙 ArkTS 对比:转换逻辑

morseMap: Record<string, string> = {
  'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....',
  // ... 其他映射
}

reverseMorse: Record<string, string> = {}

aboutToAppear() {
  // 构建反向映射
  Object.entries(this.morseMap).forEach(([k, v]) => {
    this.reverseMorse[v] = k
  })
}

textToMorse() {
  this.animateButton(0)
  const result = this.text.toUpperCase().split('').map(c => this.morseMap[c] || c).join(' ')
  this.morse = result
}

morseToText() {
  this.animateButton(1)
  const result = this.morse.split(' ').map(m => this.reverseMorse[m] || m).join('')
  this.text = result
}

ArkTS 中的转换逻辑完全一样,因为字符串方法、数组方法、对象操作都是 JavaScript 标准 API,跨平台通用。反向映射在组件初始化时构建,避免每次转换都重新计算。

界面渲染:头部和文本输入

  return (
    <ScrollView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.headerIcon}>📡</Text>
        <Text style={styles.headerTitle}>摩斯密码</Text>
        <Text style={styles.headerSubtitle}>文本与摩斯密码互转</Text>
      </View>

      <View style={styles.inputCard}>
        <Text style={styles.label}>📝 文本</Text>
        <TextInput
          style={styles.input}
          value={text}
          onChangeText={setText}
          placeholder="输入文本..."
          placeholderTextColor="#666"
          multiline
        />
      </View>

头部区域包含图标、标题、副标题。📡 天线图标表示"无线电通信"的概念,和摩斯密码的历史用途相符。

文本输入框

  • 标签"📝 文本"明确告诉用户这里输入普通文本
  • multiline 支持多行输入,用户可以输入长文本
  • placeholder 提示用户输入内容
  • 最小高度 80 像素,确保有足够的空间

转换按钮和摩斯输入

      <Animated.View style={{ transform: [{ scale: buttonAnim1 }] }}>
        <TouchableOpacity style={styles.btn} onPress={textToMorse} activeOpacity={0.8}>
          <Text style={styles.btnText}>转换为摩斯密码 ⬇️</Text>
        </TouchableOpacity>
      </Animated.View>

      <Animated.View style={[styles.inputCard, { transform: [{ scale: outputAnim }] }]}>
        <Text style={styles.label}>📡 摩斯密码</Text>
        <TextInput
          style={[styles.input, styles.morseInput]}
          value={morse}
          onChangeText={setMorse}
          placeholder=".- -... -.-."
          placeholderTextColor="#666"
          multiline
        />
      </Animated.View>

      <Animated.View style={{ transform: [{ scale: buttonAnim2 }] }}>
        <TouchableOpacity style={styles.btn} onPress={morseToText} activeOpacity={0.8}>
          <Text style={styles.btnText}>转换为文本 ⬆️</Text>
        </TouchableOpacity>
      </Animated.View>

第一个按钮:用 Animated.View 包裹,应用 buttonAnim1 动画。点击时触发 textToMorse 函数,把文本转换成摩斯密码。⬇️ 箭头表示"向下转换"。

摩斯密码输入框

  • Animated.View 包裹,应用 outputAnim 动画
  • 标签"📡 摩斯密码"明确告诉用户这里显示摩斯编码
  • morseInput 样式应用等宽字体和字符间距,让摩斯编码更易读
  • 占位符 ".- -... -.-."(ABC 的摩斯编码)提示用户格式

第二个按钮:应用 buttonAnim2 动画。点击时触发 morseToText 函数,把摩斯密码转换成文本。⬆️ 箭头表示"向上转换"。

为什么用两个按钮而不是一个切换按钮?因为两个输入框都可以编辑,用户可能在任意一个输入框输入内容,然后选择转换方向。两个按钮让转换方向更明确。

摩斯密码对照表

      <View style={styles.table}>
        <Text style={styles.tableTitle}>📋 摩斯密码表</Text>
        <View style={styles.tableGrid}>
          {Object.entries(morseMap).filter(([k]) => k !== ' ').map(([char, code]) => (
            <View key={char} style={styles.tableItem}>
              <Text style={styles.tableChar}>{char}</Text>
              <Text style={styles.tableCode}>{code}</Text>
            </View>
          ))}
        </View>
      </View>
    </ScrollView>
  );
};

对照表显示所有字符和对应的摩斯编码,方便用户查阅。

过滤空格filter(([k]) => k !== ' ') 过滤掉空格,因为空格的摩斯编码是 /,显示在表格里不太合适。

网格布局flexWrap: 'wrap' 让表格项自动换行。每个表格项宽度 16.66%(100% / 6),一行显示 6 个字符。

表格项内容

  • 上面是字符,蓝色粗体,醒目
  • 下面是摩斯编码,灰色小字,起辅助说明作用

为什么显示对照表?因为摩斯密码不是常用知识,大多数人不记得编码。对照表让用户可以手动查阅,也可以学习摩斯密码。

样式定义:容器和输入框

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#0f0f23', padding: 20 },
  header: { alignItems: 'center', marginBottom: 24 },
  headerIcon: { fontSize: 50, marginBottom: 8 },
  headerTitle: { fontSize: 28, fontWeight: '700', color: '#fff' },
  headerSubtitle: { fontSize: 14, color: '#888', marginTop: 4 },
  inputCard: {
    backgroundColor: '#1a1a3e',
    borderRadius: 16,
    padding: 16,
    marginBottom: 16,
    borderWidth: 1,
    borderColor: '#3a3a6a',
  },
  label: { color: '#888', fontSize: 14, marginBottom: 10 },
  input: {
    backgroundColor: '#252550',
    padding: 14,
    borderRadius: 10,
    minHeight: 80,
    color: '#fff',
    fontSize: 16,
  },
  morseInput: { fontFamily: 'monospace', letterSpacing: 2 },

输入卡片用深蓝色背景,圆角 16,边框。输入框用更深的背景色,和卡片形成对比。

摩斯输入框特殊样式

  • fontFamily: 'monospace':等宽字体让点和划对齐,更易读
  • letterSpacing: 2:增加字符间距,让点和划分开,避免粘在一起

为什么摩斯编码需要等宽字体?因为摩斯编码由点和划组成,等宽字体让每个字符占据相同宽度,编码看起来更整齐。

样式定义:按钮和对照表

  btn: {
    backgroundColor: '#4A90D9',
    padding: 16,
    borderRadius: 12,
    alignItems: 'center',
    marginBottom: 16,
    shadowColor: '#4A90D9',
    shadowOffset: { width: 0, height: 4 },
    shadowOpacity: 0.3,
    shadowRadius: 8,
  },
  btnText: { color: '#fff', fontWeight: '700', fontSize: 16 },
  table: {
    backgroundColor: '#1a1a3e',
    padding: 16,
    borderRadius: 16,
    borderWidth: 1,
    borderColor: '#3a3a6a',
  },
  tableTitle: { fontSize: 16, fontWeight: '600', marginBottom: 16, textAlign: 'center', color: '#fff' },
  tableGrid: { flexDirection: 'row', flexWrap: 'wrap' },
  tableItem: { width: '16.66%', padding: 8, alignItems: 'center' },
  tableChar: { fontSize: 16, fontWeight: '600', color: '#4A90D9' },
  tableCode: { fontSize: 9, color: '#888', marginTop: 2 },
});

按钮用蓝色背景和蓝色阴影,是页面的视觉焦点。阴影向下偏移 4 像素,模拟"悬浮"效果。

对照表用网格布局,每个表格项宽度 16.66%(1/6),一行显示 6 个字符。字符用蓝色粗体,编码用灰色小字,形成清晰的视觉层次。

编码字号 9 很小,因为对照表要显示 36 个字符(26 个字母 + 10 个数字),如果字号太大,表格会很长,需要滚动很久。小字号让表格更紧凑。

小结

这个摩斯密码工具展示了字符映射和双向转换的实现。用对象存储映射关系,查找效率是 O(1)。反向映射通过键值对调实现,避免重复定义。转换算法用 splitmapjoin 三步完成,代码简洁清晰。对照表让用户可以查阅和学习摩斯密码。在 OpenHarmony 平台上,字符串和数组方法是 JavaScript 标准 API,跨平台通用。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐