在这里插入图片描述

今天我们用 React Native 实现一个 JSON 格式化工具,支持格式化、压缩、验证三种功能,还带有错误提示和动画效果。

状态设计

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

export const JsonFormatter: React.FC = () => {
  const [input, setInput] = useState('');
  const [output, setOutput] = useState('');
  const [error, setError] = useState('');
  
  const buttonAnims = useRef([new Animated.Value(1), new Animated.Value(1), new Animated.Value(1)]).current;
  const outputAnim = useRef(new Animated.Value(0)).current;
  const shakeAnim = useRef(new Animated.Value(0)).current;

状态设计包含三个状态变量和三组动画值:

输入内容 input:存储用户输入的 JSON 字符串,初始为空。这是所有操作的数据源。

输出内容 output:存储处理后的结果,可能是格式化后的 JSON、压缩后的 JSON、或者验证结果。初始为空,只有在用户点击按钮后才会有内容。

错误信息 error:存储 JSON 解析失败时的错误信息,初始为空。如果 JSON 格式正确,这个状态保持为空;如果格式错误,显示具体的错误原因。

为什么要分开 outputerror?因为它们是互斥的:要么显示输出结果,要么显示错误信息,不会同时显示。分开存储让逻辑更清晰,也方便条件渲染。

按钮动画数组 buttonAnims:三个按钮(格式化、压缩、验证)各有一个独立的动画值,初始都是 1(正常大小)。用数组字面量 [new Animated.Value(1), ...] 创建,比 Array(3).fill().map() 更简洁。

输出动画 outputAnim:输出区域的弹出动画,初始值 0(隐藏状态)。当有输出结果时,从 0 弹到 1,形成"弹出"效果。

抖动动画 shakeAnim:错误提示的抖动动画,初始值 0(静止状态)。当 JSON 格式错误时,错误框左右抖动,吸引用户注意。

这些动画值让工具更生动,不同的操作结果有不同的视觉反馈:成功时弹出输出,失败时抖动错误提示。

按钮动画函数

  const animateButton = (index: number) => {
    Animated.sequence([
      Animated.timing(buttonAnims[index], { toValue: 0.9, duration: 100, useNativeDriver: true }),
      Animated.spring(buttonAnims[index], { toValue: 1, friction: 3, useNativeDriver: true }),
    ]).start();
  };

这个函数在用户点击按钮时触发,播放按钮的缩放动画。

动画序列的工作原理

  • 第一阶段:用 Animated.timing 在 100 毫秒内把按钮缩小到 90%。这个阶段模拟"按下"的动作,按钮被压缩了。
  • 第二阶段:用 Animated.spring 弹回到原大小(100%)。friction: 3 设置摩擦力,值越小弹性越大,按钮会有明显的"回弹"效果。

参数 index:指定要动画化的按钮索引(0=格式化,1=压缩,2=验证)。每个按钮有独立的动画值,点击哪个按钮就动画化哪个,互不干扰。

原生驱动useNativeDriver: true 让动画在原生层执行,不占用 JavaScript 线程。即使主线程在解析 JSON(可能比较耗时),动画也能保持流畅。

这个动画给用户即时反馈:“我点击了这个按钮,操作正在执行”。即使 JSON 解析需要几毫秒,用户也不会感觉到延迟,因为动画立即开始了。

输出动画函数

  const showOutput = () => {
    outputAnim.setValue(0);
    Animated.spring(outputAnim, { toValue: 1, friction: 5, useNativeDriver: true }).start();
  };

这个函数在操作成功时触发,播放输出区域的弹出动画。

重置动画值outputAnim.setValue(0) 把动画值重置为 0(隐藏状态)。为什么要重置?因为用户可能多次点击按钮,每次都应该有弹出效果。如果不重置,第二次点击时动画值已经是 1,就不会有动画了。

弹簧动画:从 0 弹到 1,friction: 5 设置摩擦力。摩擦力比按钮动画大(5 vs 3),弹性稍弱,动画更稳定。输出区域比按钮大,如果弹性太强,会显得很夸张。

视觉效果:输出区域从无到有,伴随缩放和透明度变化(后面会看到),给用户"结果已生成"的明确反馈。

错误动画函数

  const showError = () => {
    Animated.sequence([
      Animated.timing(shakeAnim, { toValue: 1, duration: 50, useNativeDriver: true }),
      Animated.timing(shakeAnim, { toValue: -1, duration: 50, useNativeDriver: true }),
      Animated.timing(shakeAnim, { toValue: 1, duration: 50, useNativeDriver: true }),
      Animated.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true }),
    ]).start();
  };

这个函数在 JSON 格式错误时触发,播放错误框的抖动动画。

抖动序列:四个阶段,每个阶段 50 毫秒,总共 200 毫秒:

  1. 向右旋转(0 → 1)
  2. 向左旋转(1 → -1)
  3. 再向右旋转(-1 → 1)
  4. 回到中心(1 → 0)

为什么用旋转而不是平移?因为旋转更引人注意。平移只是左右移动,旋转会让整个错误框"摇晃",像是在说"不对不对"。这种拟人化的动画更容易被用户注意到。

时长选择:50 毫秒很短,抖动很快,给人"紧急"的感觉。如果用 200 毫秒,抖动会很慢,像是在"摇摆",不够紧迫。

视觉效果:错误框快速左右摇晃,吸引用户注意。配合红色背景和边框,让用户立即知道"出错了"。

格式化函数

  const format = () => {
    animateButton(0);
    try {
      const obj = JSON.parse(input);
      setOutput(JSON.stringify(obj, null, 2));
      setError('');
      showOutput();
    } catch (e: any) {
      setError('JSON格式错误: ' + e.message);
      setOutput('');
      showError();
    }
  };

格式化是最常用的功能,把压缩的 JSON 转换成易读的格式。

执行流程

  1. animateButton(0):触发第一个按钮(格式化)的动画
  2. JSON.parse(input):解析 JSON 字符串,转换成 JavaScript 对象
  3. JSON.stringify(obj, null, 2):把对象转回 JSON 字符串,带缩进
  4. setOutput(...):更新输出状态
  5. setError(''):清空错误信息(如果之前有错误)
  6. showOutput():触发输出区域的弹出动画

JSON.stringify 的参数

  • 第一个参数:要转换的对象
  • 第二个参数:null 表示不过滤属性(保留所有属性)
  • 第三个参数:2 表示缩进 2 个空格

比如输入 {"name":"John","age":30},输出:

{
  "name": "John",
  "age": 30
}

错误处理:如果 JSON.parse 失败(JSON 格式错误),会抛出异常。catch 捕获异常,提取错误信息(e.message),显示给用户。同时清空输出(setOutput('')),触发错误动画。

为什么先动画再解析?因为动画是同步的(立即开始),解析是可能耗时的(大 JSON 可能需要几毫秒)。先动画让用户立即看到反馈,不会感觉到延迟。

压缩函数

  const minify = () => {
    animateButton(1);
    try {
      const obj = JSON.parse(input);
      setOutput(JSON.stringify(obj));
      setError('');
      showOutput();
    } catch (e: any) {
      setError('JSON格式错误: ' + e.message);
      setOutput('');
      showError();
    }
  };

压缩功能把格式化的 JSON 转换成紧凑格式,去除所有空白字符。

和格式化的区别:只有一个地方不同——JSON.stringify(obj) 只传一个参数,不传缩进参数。这样生成的 JSON 没有换行和缩进,所有内容在一行。

比如输入:

{
  "name": "John",
  "age": 30
}

输出:{"name":"John","age":30}

使用场景

  • 减小体积:去除空白字符可以减小 JSON 的体积,节省网络传输和存储空间
  • API 传输:发送给服务器的 JSON 通常是压缩的,不需要格式化
  • 配置文件:某些配置文件要求紧凑格式

错误处理:和格式化函数完全一样,如果 JSON 格式错误,显示错误信息并触发抖动动画。

验证函数

  const validate = () => {
    animateButton(2);
    try {
      JSON.parse(input);
      setError('');
      setOutput('✅ JSON格式正确');
      showOutput();
    } catch (e: any) {
      setError('❌ ' + e.message);
      setOutput('');
      showError();
    }
  };

验证功能只检查 JSON 格式是否正确,不输出转换结果。

执行流程

  1. animateButton(2):触发第三个按钮(验证)的动画
  2. JSON.parse(input):尝试解析 JSON
  3. 如果成功:显示"✅ JSON格式正确",触发输出动画
  4. 如果失败:显示"❌ 错误信息",触发错误动画

为什么不输出解析结果?因为验证的目的是检查格式,不是转换数据。用户只需要知道"对"或"错",不需要看到转换后的 JSON。

emoji 的使用:✅ 和 ❌ 是通用的符号,不需要解释就能理解。绿色的勾表示成功,红色的叉表示失败,直观易懂。

使用场景

  • 快速检查:不确定 JSON 格式是否正确,快速验证一下
  • 调试 API:API 返回的数据格式不对,用验证功能找出错误位置
  • 学习 JSON:初学者不确定自己写的 JSON 是否正确,用验证功能检查

抖动动画插值

  const shake = shakeAnim.interpolate({
    inputRange: [-1, 0, 1],
    outputRange: ['-3deg', '0deg', '3deg'],
  });

interpolate 把动画值映射成旋转角度:

  • 动画值 -1 → 旋转 -3 度(向左)
  • 动画值 0 → 旋转 0 度(正中)
  • 动画值 1 → 旋转 3 度(向右)

角度选择:3 度是一个很微妙的角度,足以让用户注意到抖动,但不会太夸张。如果用 10 度,错误框会剧烈摇晃,像是要翻倒了;如果用 1 度,抖动太小,可能注意不到。

为什么用度数而不是弧度?因为度数更直观,3 度大概是 5% 的倾斜,人类更容易理解。弧度(0.05 rad)对大多数人来说没有直观感受。

这个插值会应用到错误框的 transform: [{ rotate: shake }],让错误框左右旋转。

鸿蒙 ArkTS 对比:JSON 处理

format() {
  this.animateButton(0)
  try {
    const obj = JSON.parse(this.input)
    this.output = JSON.stringify(obj, null, 2)
    this.error = ''
    this.showOutput()
  } catch (e) {
    this.error = 'JSON格式错误: ' + e.message
    this.output = ''
    this.showError()
  }
}

minify() {
  this.animateButton(1)
  try {
    const obj = JSON.parse(this.input)
    this.output = JSON.stringify(obj)
    this.error = ''
    this.showOutput()
  } catch (e) {
    this.error = 'JSON格式错误: ' + e.message
    this.output = ''
    this.showError()
  }
}

validate() {
  this.animateButton(2)
  try {
    JSON.parse(this.input)
    this.error = ''
    this.output = '✅ JSON格式正确'
    this.showOutput()
  } catch (e) {
    this.error = '❌ ' + e.message
    this.output = ''
    this.showError()
  }
}

ArkTS 中的 JSON 处理逻辑完全一样,因为 JSON.parseJSON.stringify 是 JavaScript 标准 API,跨平台通用。这就是 React Native 的优势——核心业务逻辑可以直接复用,不需要为每个平台重写。

界面渲染:头部和输入框

  return (
    <ScrollView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.headerIcon}>📋</Text>
        <Text style={styles.headerTitle}>JSON 格式化</Text>
        <Text style={styles.headerSubtitle}>格式化、压缩、验证JSON</Text>
      </View>

      <View style={styles.inputCard}>
        <Text style={styles.label}>输入JSON</Text>
        <TextInput
          style={styles.input}
          value={input}
          onChangeText={setInput}
          multiline
          placeholder='{"key": "value"}'
          placeholderTextColor="#666"
        />
      </View>

头部区域包含图标、标题、副标题,和其他工具保持一致的风格。📋 剪贴板图标表示"数据"的概念,和 JSON 的用途相符。

输入框设计

  • 标签"输入JSON"明确告诉用户这里应该输入什么
  • multiline 让输入框支持多行输入,因为 JSON 通常是多行的
  • placeholder 提供一个简单的 JSON 示例,让用户知道格式
  • placeholderTextColor="#666" 设置占位符颜色为灰色,和实际输入的青蓝色文本形成对比

为什么用等宽字体?后面的样式中,输入框用了 fontFamily: 'monospace'(等宽字体)。等宽字体让 JSON 的结构更清晰,每个字符占据相同宽度,缩进对齐更明显。这是代码编辑器的标准做法。

颜色选择:输入框的文本颜色是 #9cdcfe(青蓝色),这是 VS Code 中字符串的颜色。程序员看到这个颜色会觉得很熟悉,有"在写代码"的感觉。

按钮渲染

      <View style={styles.btnRow}>
        {[
          { text: '格式化', icon: '✨', onPress: format },
          { text: '压缩', icon: '📦', onPress: minify },
          { text: '验证', icon: '✅', onPress: validate },
        ].map((btn, i) => (
          <Animated.View key={i} style={[styles.btnWrapper, { transform: [{ scale: buttonAnims[i] }] }]}>
            <TouchableOpacity style={styles.btn} onPress={btn.onPress} activeOpacity={0.8}>
              <Text style={styles.btnIcon}>{btn.icon}</Text>
              <Text style={styles.btnText}>{btn.text}</Text>
            </TouchableOpacity>
          </Animated.View>
        ))}
      </View>

三个按钮横向排列,用数组字面量定义按钮数据,map 遍历生成按钮。

按钮数据结构:每个按钮包含文字、图标、点击处理函数。这种数据驱动的设计让代码很简洁,如果要添加新按钮,只需要在数组里加一项。

图标选择

  • ✨ 星星表示"美化",格式化让 JSON 变漂亮
  • 📦 盒子表示"打包",压缩让 JSON 变紧凑
  • ✅ 勾号表示"检查",验证检查格式是否正确

这些 emoji 的含义很直观,不需要解释就能理解。

动画包裹:每个按钮用 Animated.View 包裹,应用独立的缩放动画。key={i} 用索引作为 key,因为按钮的顺序和数量是固定的,不会变化。

横向布局flexDirection: 'row' 让三个按钮横向排列,flex: 1 让它们平均分配宽度。无论屏幕多宽,三个按钮总是等宽的。

点击处理onPress={btn.onPress} 直接传入处理函数,不需要包装。因为处理函数已经定义好了(formatminifyvalidate),直接传入即可。

触摸反馈activeOpacity={0.8} 让按钮按下时稍微变暗,配合缩放动画,给用户双重反馈。

错误提示渲染

      {error ? (
        <Animated.View style={[styles.errorBox, { transform: [{ rotate: shake }] }]}>
          <Text style={styles.errorText}>{error}</Text>
        </Animated.View>
      ) : null}

错误提示只在有错误时才显示,用条件渲染实现。

条件渲染error ? 检查错误信息是否为空。如果为空(空字符串是 falsy),返回 null,不渲染任何内容;如果不为空,渲染错误框。

抖动动画transform: [{ rotate: shake }] 应用旋转动画。shake 是前面定义的插值,会在 -3 度到 3 度之间变化。

错误框样式:红色背景(半透明)、红色边框、红色文字,三重红色让用户立即知道"出错了"。半透明背景(rgba(231, 76, 60, 0.2))不会太刺眼,但足够醒目。

错误信息:直接显示 JSON.parse 抛出的错误信息,比如 "Unexpected token } in JSON at position 10"。这些信息虽然是英文,但对程序员来说很有用,能快速定位错误位置。

输出区域渲染

      {output ? (
        <Animated.View style={[styles.outputCard, {
          transform: [{ scale: outputAnim }, { perspective: 1000 }],
          opacity: outputAnim,
        }]}>
          <Text style={styles.label}>输出</Text>
          <View style={styles.outputBox}>
            <Text style={styles.output} selectable>{output}</Text>
          </View>
        </Animated.View>
      ) : null}
    </ScrollView>
  );
};

输出区域只在有输出时才显示,用条件渲染实现。

条件渲染output ? 检查输出内容是否为空。如果为空,返回 null;如果不为空,渲染输出卡片。

弹出动画:同时应用缩放和透明度动画:

  • scale: outputAnim:从 0 到 1,卡片从无到有
  • opacity: outputAnim:从 0 到 1,卡片从透明到不透明
  • perspective: 1000:添加 3D 透视,让缩放动画更有立体感

为什么同时用缩放和透明度?因为单独用缩放,卡片会从一个点突然出现,有点突兀;单独用透明度,卡片会原地淡入,不够生动。两者结合,卡片从小到大、从透明到不透明,形成"弹出"效果,更自然。

可选择文本selectable 属性让用户可以长按选择复制输出内容。这是一个很重要的功能,用户格式化 JSON 后通常需要复制结果,如果不能复制,就得手动输入,体验很差。

等宽字体:输出文本也用等宽字体(fontFamily: 'monospace'),让 JSON 的结构清晰。格式化后的 JSON 有缩进,等宽字体让缩进对齐,易于阅读。

颜色选择:输出文本用青蓝色(#9cdcfe),和输入框一致。输出框的背景色(#1e1e1e)比输入框更深,这是 VS Code 的深色主题配色,程序员看着很舒服。

鸿蒙 ArkTS 对比:界面渲染

Column() {
  // 头部
  Column() {
    Text('📋').fontSize(50).margin({ bottom: 8 })
    Text('JSON 格式化').fontSize(28).fontWeight(FontWeight.Bold).fontColor(Color.White)
    Text('格式化、压缩、验证JSON').fontSize(14).fontColor('#888888').margin({ top: 4 })
  }
  .width('100%')
  .alignItems(HorizontalAlign.Center)
  .margin({ bottom: 24 })
  
  // 输入框
  Column() {
    Text('输入JSON').fontSize(14).fontColor('#888888').margin({ bottom: 12 })
    TextArea({ text: this.input, placeholder: '{"key": "value"}' })
      .width('100%')
      .height(150)
      .backgroundColor('#252550')
      .fontColor('#9cdcfe')
      .fontFamily('monospace')
      .onChange((value: string) => {
        this.input = value
      })
  }
  .width('100%')
  .backgroundColor('#1a1a3e')
  .borderRadius(20)
  .padding(20)
  
  // 按钮
  Row() {
    ForEach([
      { text: '格式化', icon: '✨', action: () => this.format() },
      { text: '压缩', icon: '📦', action: () => this.minify() },
      { text: '验证', icon: '✅', action: () => this.validate() },
    ], (btn: ButtonItem, index: number) => {
      Button() {
        Row() {
          Text(btn.icon).fontSize(16).margin({ right: 6 })
          Text(btn.text).fontWeight(FontWeight.Medium)
        }
      }
      .layoutWeight(1)
      .backgroundColor('#4A90D9')
      .onClick(btn.action)
      .scale({ x: this.buttonScales[index], y: this.buttonScales[index] })
    })
  }
  .width('100%')
  
  // 错误提示
  if (this.error) {
    Column() {
      Text(this.error).fontColor('#e74c3c')
    }
    .width('100%')
    .backgroundColor('rgba(231, 76, 60, 0.2)')
    .borderRadius(12)
    .padding(16)
    .border({ width: 1, color: '#e74c3c' })
    .rotate({ angle: this.shakeAngle })
  }
  
  // 输出
  if (this.output) {
    Column() {
      Text('输出').fontSize(14).fontColor('#888888').margin({ bottom: 12 })
      Column() {
        Text(this.output)
          .fontColor('#9cdcfe')
          .fontFamily('monospace')
          .fontSize(12)
          .copyOption(CopyOptions.LocalDevice)
      }
      .width('100%')
      .backgroundColor('#1e1e1e')
      .borderRadius(12)
      .padding(16)
    }
    .width('100%')
    .backgroundColor('#1a1a3e')
    .borderRadius(20)
    .padding(20)
    .scale({ x: this.outputScale, y: this.outputScale })
    .opacity(this.outputOpacity)
  }
}

鸿蒙的布局结构和 React Native 类似,只是语法不同。Column 对应垂直布局,Row 对应水平布局,TextArea 对应多行输入框。条件渲染用 if 语句,动画用 scalerotateopacity 属性。

核心逻辑(JSON 处理、错误处理)完全一样,只是界面代码需要适配鸿蒙的组件和 API。

样式定义:容器和头部

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 },

容器用深蓝色背景 #0f0f23,这是整个应用的统一配色。padding: 20 在四周留出边距,让内容不会紧贴屏幕边缘。

头部元素全部居中对齐,图标、标题、副标题垂直排列,形成清晰的视觉层次。字号从大到小(50 → 28 → 14),颜色从亮到暗(emoji → 白色 → 灰色),引导用户的视线从上到下阅读。

样式定义:输入框

  inputCard: {
    backgroundColor: '#1a1a3e',
    borderRadius: 20,
    padding: 20,
    marginBottom: 20,
    borderWidth: 1,
    borderColor: '#3a3a6a',
  },
  label: { color: '#888', fontSize: 14, marginBottom: 12 },
  input: {
    backgroundColor: '#252550',
    padding: 16,
    borderRadius: 12,
    minHeight: 150,
    color: '#9cdcfe',
    fontSize: 14,
    fontFamily: 'monospace',
  },

输入框卡片用深蓝色背景,圆角 20,内边距 20,边框颜色比背景稍亮,形成立体感。

标签样式:灰色文字,14 号字体,下边距 12。标签的作用是说明输入框的用途,不需要太醒目,灰色刚好。

输入框样式

  • 背景色 #252550 比卡片背景更深,让用户知道"这是可以输入的区域"
  • 文字颜色 #9cdcfe 是青蓝色,这是 VS Code 中字符串的颜色,程序员看着很熟悉
  • 字号 14,比普通文本稍小,因为 JSON 通常内容较多,小字号可以显示更多内容
  • fontFamily: 'monospace' 使用等宽字体,让 JSON 的结构更清晰

为什么用等宽字体?因为 JSON 的缩进很重要,等宽字体让每个字符占据相同宽度,缩进对齐更明显。比如:

{
  "name": "John",
  "age": 30
}

如果用比例字体(proportional font),"name""age" 的宽度不同,缩进看起来不对齐。等宽字体让所有字符宽度相同,缩进完美对齐。

样式定义:按钮

  btnRow: { flexDirection: 'row', marginBottom: 20 },
  btnWrapper: { flex: 1, marginHorizontal: 4 },
  btn: {
    backgroundColor: '#4A90D9',
    padding: 14,
    borderRadius: 12,
    alignItems: 'center',
    flexDirection: 'row',
    justifyContent: 'center',
  },
  btnIcon: { fontSize: 16, marginRight: 6 },
  btnText: { color: '#fff', fontWeight: '600' },

按钮行用横向布局,三个按钮平均分配宽度。

按钮包裹器flex: 1 让三个按钮等宽,marginHorizontal: 4 在按钮之间留出 8 像素的间距(左右各 4)。

按钮样式

  • 蓝色背景 #4A90D9,这是应用的主题色,用于所有主要操作按钮
  • 内边距 14,让按钮有足够的点击区域
  • 圆角 12,和卡片的圆角一致
  • flexDirection: 'row' 让图标和文字横向排列
  • justifyContent: 'center'alignItems: 'center' 让内容居中

按钮内容

  • 图标 16 号字体,文字默认字体(通常是 14-16)
  • 图标和文字之间留 6 像素间距
  • 文字用中等粗细(fontWeight: '600'),比正常文字粗一点

为什么按钮用蓝色而不是深色?因为按钮是主要操作,需要突出显示。如果用深色(和卡片一样),按钮会"淹没"在背景中,不够醒目。蓝色在深色背景上很显眼,而且给人"可点击"的感觉。

样式定义:错误提示和输出

  errorBox: {
    backgroundColor: 'rgba(231, 76, 60, 0.2)',
    borderRadius: 12,
    padding: 16,
    marginBottom: 20,
    borderWidth: 1,
    borderColor: '#e74c3c',
  },
  errorText: { color: '#e74c3c' },
  outputCard: {
    backgroundColor: '#1a1a3e',
    borderRadius: 20,
    padding: 20,
    borderWidth: 1,
    borderColor: '#3a3a6a',
  },
  outputBox: {
    backgroundColor: '#1e1e1e',
    padding: 16,
    borderRadius: 12,
    minHeight: 150,
  },
  output: { color: '#9cdcfe', fontFamily: 'monospace', fontSize: 12 },
});

错误框样式

  • 背景色 rgba(231, 76, 60, 0.2) 是半透明的红色,不会太刺眼,但足够醒目
  • 边框颜色 #e74c3c 是不透明的红色,和背景形成对比
  • 文字颜色也是红色,三重红色让用户立即知道"出错了"

为什么用半透明背景?因为纯红色背景太刺眼,会让用户感到不舒服。半透明背景(20% 不透明度)柔和很多,但仍然能引起注意。这是一个常见的 UI 设计技巧。

输出卡片样式

  • 和输入卡片一样的背景色、圆角、边框,保持视觉一致性
  • 输出框的背景色 #1e1e1e 比输入框更深,这是 VS Code 的深色主题配色
  • 输出文字用等宽字体,字号 12,比输入框稍小

为什么输出文字更小?因为格式化后的 JSON 通常比输入的 JSON 长(增加了换行和缩进),用小字号可以在同样的空间里显示更多内容。而且输出是只读的,不需要编辑,小字号不会影响使用。

颜色一致性:输入框和输出框都用青蓝色文字(#9cdcfe)、等宽字体,让用户感觉它们是"配套"的。输入和输出在视觉上形成呼应,用户一眼就能看出它们的关系。

小结

这个 JSON 格式化工具展示了 JavaScript 内置 API 的强大。JSON.parseJSON.stringify 两个函数就能实现格式化、压缩、验证三种功能。错误处理用 try-catch 捕获异常,显示具体的错误信息。按钮的缩放动画、输出的弹出动画、错误的抖动动画,不同的操作结果有不同的视觉反馈。在 OpenHarmony 平台上,JSON.parseJSON.stringify 是 JavaScript 标准 API,跨平台通用。


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

Logo

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

更多推荐