news 2026/4/23 15:31:21

RN for OpenHarmony 小工具 App 实战:屏幕尺子实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RN for OpenHarmony 小工具 App 实战:屏幕尺子实现

这一篇实现一个「屏幕尺子」小工具:在手机屏幕上绘制 cm / inch 两条刻度尺,并展示当前设备屏幕宽度(px)与“换算后的大致厘米数”。

需要先说明一个现实限制:

  • 屏幕尺子的精度取决于设备的 PPI(像素密度)与系统缩放
  • 代码里用的是近似换算(pxPerCm = 37.8),因此适合“粗略参考”,不适合精密测量。

本文所有代码片段均来自仓库真实文件:

  • src/pages/Ruler.tsx
  • src/tools/index.ts
  • src/screens/ToolScreen.tsx

你要求“每一段代码后面跟着的文字解释多一些”,本文会按“代码块 -> 紧跟解释”的方式组织。


01. 这个小工具的设计目标

屏幕尺子是典型的“看起来简单,细节很多”的小工具。目标不是做实验室级精度,而是:

  • 可用:能滑动、能看清刻度
  • 直观:整数刻度有数字,半刻度/四分之一刻度有不同长度
  • 自解释:明确告诉用户精度可能不准

同时,它非常适合作为 RN UI 练习:

  • 刻度生成(循环渲染)
  • ScrollView 横向滚动
  • px 与 cm/inch 的比例换算
  • 小而美的入场动画

02. 工具箱接入:id=54 映射到 Ruler

2.1 工具列表注册

文件:src/tools/index.ts

{id:54,name:'尺子',description:'屏幕尺子工具',icon:'📏',component:'Ruler'},

解释:

  • component: 'Ruler'是 ToolScreen 查表渲染的 key
  • 📏图标在工具网格里容易识别
  • description简洁明确:这是一个屏幕尺子工具

2.2 ToolScreen 映射

文件:src/screens/ToolScreen.tsx

Ruler: Pages.Ruler,

解释:

  • 工具箱会根据配置中的字符串RulercomponentMap找到Pages.Ruler
  • 这种“配置驱动”的结构意味着:文章和代码的对应关系非常稳定

03. 屏幕像素信息:Dimensions.get(‘window’)

文件:src/pages/Ruler.tsx

import { View, Text, StyleSheet, ScrollView, Dimensions, Animated } from 'react-native'; const { width: screenWidth } = Dimensions.get('window');

解释:

  • Dimensions.get('window')返回当前窗口的尺寸信息(像素单位)
  • 这里我们只取width,用于:
    • “屏幕宽度 px”展示
    • 估算“屏幕宽度约等于多少 cm”

为什么用window而不是screen

  • window更接近应用可用区域
  • screen可能包含状态栏等不可用区域(不同平台行为有差异)

对于“尺子”这种工具,精度本来就不是严格保证,所以取 window 是更合理的工程选择。


04. 核心参数:cmCount 与 pxPerCm

const cmCount = 20; const pxPerCm = 37.8;

解释:

4.1cmCount的意义

cmCount=20代表我们要画 0~20cm 的刻度。

  • 每 1cm 需要 10 个小刻度(0.1cm)
  • 因此后续会生成cmCount * 10 + 1个 tick

为什么要+1?因为 0 也要画出来。

4.2pxPerCm = 37.8从哪来?

这个数字来自一个常见近似:

  • 96 dpi 的换算:1 inch = 96 px
  • 1 inch = 2.54 cm
  • 所以 1 cm ≈ 96 / 2.54 ≈ 37.8 px

它不是“设备真实 PPI”,但作为“参考级工具”足够。

如果你想做得更准,必须知道设备真实 dpi / ppi。


05. 入场动画:fade + slide(并行动画)

const fadeAnim = useRef(new Animated.Value(0)).current; const slideAnim = useRef(new Animated.Value(-20)).current; useEffect(() => { Animated.parallel([ Animated.timing(fadeAnim, { toValue: 1, duration: 500, useNativeDriver: true }), Animated.spring(slideAnim, { toValue: 0, friction: 6, useNativeDriver: true }), ]).start(); }, []);

解释:

  • fadeAnim控制 header 的透明度:0 -> 1
  • slideAnim控制 header 的 Y 位移:-20 -> 0(从上往下滑入)
  • Animated.parallel让两个动画同时执行

为什么这里用timing + spring的组合?

  • 淡入用timing就够了,稳定、可控
  • 位移用spring会更自然,有一点“落下来的惯性”

为什么只动画 header?

  • 主尺子是 ScrollView 内容,动画它会影响滚动体验
  • header 动一下就足够让页面“有启动感”,而不会干扰主要功能

06. cm 刻度渲染:把 1cm 切成 10 个 tick

{Array.from({ length: cmCount * 10 + 1 }).map((_, i) => { const isCm = i % 10 === 0; const isHalfCm = i % 5 === 0 && !isCm; return ( <View key={i} style={styles.tickContainer}> <View style={[styles.tick, isCm && styles.tickCm, isHalfCm && styles.tickHalf]} /> {isCm && <Text style={styles.tickLabel}>{i / 10}</Text>} </View> ); })}

解释(这是这页最核心的“刻度生成”):

6.1 为什么是cmCount * 10 + 1

因为我们把 1cm 切成 10 个小刻度(每格 0.1cm)。

  • 20cm 就是 200 个小刻度
  • +1是为了把 0cm 的刻度画出来

6.2isCmisHalfCm的判定思路

  • isCm:每 10 个 tick 一个整厘米
  • isHalfCm:每 5 个 tick 一个 0.5cm(但要排除整厘米)

这样你会得到三种高度:

  • 0.1cm:短线
  • 0.5cm:中线
  • 1cm:长线 + 数字

6.3 为什么数字只在整厘米显示?

如果每个小刻度都显示数字,UI 会极度拥挤。

整厘米显示数字是尺子类工具的通用设计:

  • 信息密度合理
  • 用户读数不会困惑

07. inch 刻度渲染:1 inch 切成 8 份(1/8)

{Array.from({ length: 8 * 8 + 1 }).map((_, i) => { const isInch = i % 8 === 0; const isHalf = i % 4 === 0 && !isInch; const isQuarter = i % 2 === 0 && !isHalf && !isInch; return ( <View key={i} style={[styles.tickContainer, { width: pxPerCm * 2.54 / 8 }]}> <View style={[styles.tick, isInch && styles.tickCm, isHalf && styles.tickHalf, isQuarter && styles.tickQuarter]} /> {isInch && <Text style={styles.tickLabel}>{i / 8}</Text>} </View> ); })}

解释:

7.1 为什么 1 inch 切成 8 份?

常见尺子会使用 1/8 inch 或 1/16 inch 作为最小刻度。

这里选择 1/8 是一个折中:

  • 刻度足够细
  • 但不会多到难以辨认

7.2 tick 宽度为何是pxPerCm * 2.54 / 8

  • 1 inch = 2.54 cm
  • 1/8 inch = 2.54/8 cm
  • 再乘以pxPerCm就得到每个 tick 的宽度(px)

这样 inch 刻度就会与 cm 刻度在同一套 px 换算体系下保持一致。

7.3 1/2 与 1/4 刻度高度区分

  • isInch:整英寸(最高)
  • isHalf:半英寸(次高)
  • isQuarter:四分之一英寸(中等)
  • 其他:八分之一(最短)

这也是典型的英制尺子视觉层次。


08. 尺子横向滚动:为什么用 ScrollView horizontal?

<ScrollView horizontal style={styles.rulerContainer} showsHorizontalScrollIndicator={false}> <View style={styles.ruler}> {/* ticks */} </View> </ScrollView>

解释:

  • 刻度尺天然是横向延伸的
  • horizontal可以让用户像使用真实尺子一样拖动查看
  • showsHorizontalScrollIndicator={false}隐藏滚动条,让视觉更像“尺子”而不是“列表”

这里有个设计细节:

  • 内层用View来承载 ticks,而不是直接在 ScrollView 里 map
  • 这样刻度背景(黄色/蓝色)可以作为一整条条带显示

09. 屏幕信息展示:px 与 cm 的换算

<Text style={styles.infoValue}>{screenWidth.toFixed(0)} px</Text> <Text style={styles.infoValue}>{(screenWidth / pxPerCm).toFixed(1)} cm</Text>

解释:

  • screenWidth是 px
  • screenWidth / pxPerCm得到“估算厘米”

为什么只保留 1 位小数?

  • 因为pxPerCm本身就是近似值
  • 显示过多小数会给用户一种“很精确”的错觉

这类工具更应该强调“参考”,而不是制造误导。


10. 关键样式:刻度宽度与刻度高度

来自src/pages/Ruler.tsx

tickContainer: { width: 3.78, alignItems: 'center' }, tick: { width: 1, height: 10, backgroundColor: '#333' }, tickCm: { height: 30, width: 2 }, tickHalf: { height: 20 }, tickQuarter: { height: 15 },

解释:

10.1 为什么tickContainer.width = 3.78

它对应“0.1cm”的宽度:

  • 1cm ≈ 37.8px
  • 0.1cm ≈ 3.78px

所以 cm 尺子每个小刻度正好占用 3.78px。

10.2 不同刻度高度的意义

如果所有刻度高度一样,用户会难以快速定位整数刻度。

高度分层会形成“视觉导航”:

  • 找到长线 -> 读数字
  • 在长线之间用中线/短线估算

这就是尺子类 UI 的核心可读性来源。


11. 小结

这个「屏幕尺子」工具的实现重点在于:

  • 通过pxPerCm把“物理长度”映射到“屏幕像素”
  • cm / inch 两套刻度各自按常见规则分桶渲染
  • 用 ScrollView horizontal 提供自然的尺子滑动体验
  • 明确提示“精度因设备而异”,避免误用

如果你后续想做得更精确,关键不在 UI,而在拿到真实 PPI并计算pxPerCm


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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 14:02:23

办公自动化实战:MinerU镜像批量处理PDF转Markdown技巧

办公自动化实战&#xff1a;MinerU镜像批量处理PDF转Markdown技巧 1. 引言 在现代办公环境中&#xff0c;文档处理占据了大量时间。尤其是面对海量的PDF文件时&#xff0c;如何高效地将其转换为可编辑、易分享的格式成为了一个普遍痛点。传统的手动复制粘贴不仅效率低下&…

作者头像 李华
网站建设 2026/4/18 9:44:08

ACE-Step实战分享:为动画项目定制主题曲的完整流程

ACE-Step实战分享&#xff1a;为动画项目定制主题曲的完整流程 1. 背景与需求&#xff1a;为何选择ACE-Step生成动画主题曲 在动画项目的制作过程中&#xff0c;配乐是塑造氛围、强化情感表达的关键环节。传统音乐创作依赖专业作曲团队&#xff0c;周期长、成本高&#xff0c…

作者头像 李华
网站建设 2026/4/23 13:59:51

GPEN人像修复实战:游戏NPC人脸真实化改造项目

GPEN人像修复实战&#xff1a;游戏NPC人脸真实化改造项目 1. 镜像环境说明 本镜像基于 GPEN人像修复增强模型 构建&#xff0c;预装了完整的深度学习开发环境&#xff0c;集成了推理及评估所需的所有依赖&#xff0c;开箱即用。适用于图像超分辨率、人脸细节增强、低质量图像…

作者头像 李华
网站建设 2026/4/23 13:57:49

Live Avatar Gradio界面无法访问?端口冲突解决方法

Live Avatar Gradio界面无法访问&#xff1f;端口冲突解决方法 1. 引言 1.1 技术背景与问题提出 Live Avatar 是由阿里巴巴联合多所高校共同开源的数字人生成模型&#xff0c;基于14B参数规模的DiT&#xff08;Diffusion in Transformer&#xff09;架构&#xff0c;能够实现…

作者头像 李华
网站建设 2026/4/23 15:27:02

VibeThinker-1.5B工具推荐:适合算法竞赛的AI助手部署方案

VibeThinker-1.5B工具推荐&#xff1a;适合算法竞赛的AI助手部署方案 1. 技术背景与应用场景 在算法竞赛和编程挑战日益普及的今天&#xff0c;开发者对高效、低成本且具备强推理能力的AI辅助工具需求不断上升。LeetCode、Codeforces、AtCoder等平台上的问题不仅要求代码实现…

作者头像 李华
网站建设 2026/4/20 15:10:02

CosyVoice-300M Lite实战:语音合成在AR/VR中的应用

CosyVoice-300M Lite实战&#xff1a;语音合成在AR/VR中的应用 1. 引言&#xff1a;轻量级TTS如何赋能沉浸式体验 随着增强现实&#xff08;AR&#xff09;与虚拟现实&#xff08;VR&#xff09;技术的快速发展&#xff0c;用户对交互自然性的要求日益提升。传统预录音频已难…

作者头像 李华