# 鸿蒙 HarmonyOS 应用开发实战(第25期)|骰子(Dice Roller)— Unicode 符号与动画渲染精讲
一、应用概述骰子Dice Roller是一款经典的休闲娱乐应用模拟了真实掷骰子的过程。应用投掷两个骰子六面标准骰使用 Unicode 骰面符号直观展示每个骰子的点数并伴有快速滚动的动画效果。每次掷骰后应用会自动计算点数总和并根据特殊的点数组合如蛇眼、Lucky 7、双六给出趣味描述。应用基于 HarmonyOS 的 ArkTS 语言和声明式 UI 框架构建完美展示了 Unicode 特殊符号渲染、animation 动画属性、setInterval 定时器、以及 get 计算属性等核心技术的综合运用。对于鸿蒙开发者而言骰子应用是一个展示符号渲染与动画技巧的绝佳案例——它让你在约85行代码中同时掌握Unicode 符号映射、定时器驱动动画、条件描述逻辑、统计追踪四大核心能力这些能力广泛适用于游戏、抽奖、模拟器等场景。1.1 功能特性双骰子投掷模拟两个六面骰子的随机投掷Unicode 骰面显示使用 ⚀⚁⚂⚃⚄⚅ 符号直观展示点数滚动动画60ms 间隔快速变化模拟真实骰子滚动点数总和计算自动计算两个骰子点数和趣味描述特殊组合2点/3点/7点/12点触发趣味文案投掷统计记录总共投掷次数动画锁定动画进行中防止重复点击1.2 适用场景桌游电子化辅助工具聚会活动抽奖环节概率统计教学演示儿童数学启蒙教育二、技术架构2.1 整体架构概览┌─────────────────────────────────────────┐ │ 数据层 │ │ dice1 / dice2 (1~6 整数) │ │ total (点数总和) │ │ isRolling (动画状态) │ │ rollCount (投掷统计) │ ├─────────────────────────────────────────┤ │ 状态管理层 │ │ State 驱动 UI 重新渲染 │ ├─────────────────────────────────────────┤ │ 业务逻辑层 │ │ roll() —— 掷骰子 动画 │ │ diceFace() —— 数字转 Unicode 符号 │ │ get d1/d2 —— 动画期间的实时随机值 │ │ get description —— 特殊组合描述 │ ├─────────────────────────────────────────┤ │ UI 表示层 │ │ 两个骰子显示 / 点数总和 / 趣味描述 / │ │ 投掷统计 / 掷骰按钮 │ └─────────────────────────────────────────┘2.2 动画状态机静止状态 (isRollingfalse) │ │ 点击掷骰子 ▼ 动画状态 (isRollingtrue) │ │ setInterval(60ms) × 12次 │ 每次更新 dice1/dice2 为随机值 │ UI 显示实时随机值 ▼ 判定状态 (count 12) │ │ clearInterval │ isRolling false │ 计算 total / description │ rollCount ▼ 静止状态 (等待下次投掷)三、核心代码深度分析3.1 完整的页面源码import { router } from kit.ArkUI; Entry Component struct index25 { State dice1: number 1; State dice2: number 6; State total: number 7; State isRolling: boolean false; State rollCount: number 0; // 动画期间的实时随机值 get d1(): number { return this.isRolling ? Math.floor(Math.random() * 6) 1 : this.dice1; } get d2(): number { return this.isRolling ? Math.floor(Math.random() * 6) 1 : this.dice2; } roll() { if (this.isRolling) return; this.isRolling true; let count 0; const intervalId setInterval(() { this.dice1 Math.floor(Math.random() * 6) 1; this.dice2 Math.floor(Math.random() * 6) 1; count; if (count 12) { clearInterval(intervalId); this.isRolling false; this.total this.dice1 this.dice2; this.rollCount; } }, 60); } // 数字转 Unicode 骰面符号 diceFace(val: number): string { const faces [⚀, ⚁, ⚂, ⚃, ⚄, ⚅]; return faces[val - 1]; } // 趣味描述 get description(): string { const t this.total; if (t 2) return 蛇眼; if (t 7) return Lucky 7!; if (t 12) return 双六; if (t 3) return 霉运; return ; } build() { Column() { Row() { Text(‹ 返回).fontSize(16).fontColor(#0077FF).onClick(() router.back()) Text( 骰子).fontSize(18).fontWeight(FontWeight.Bold) Blank() }.width(92%).padding({ top: 16, bottom: 8 }) Column() { // 双骰子展示 Row({ space: 20 }) { Column() { Text(this.diceFace(this.isRolling ? this.d1 : this.dice1)) .fontSize(80).animation({ duration: 60 }) Text(骰子 1).fontSize(14).fontColor(#888) } Column() { Text(this.diceFace(this.isRolling ? this.d2 : this.dice2)) .fontSize(80).animation({ duration: 60 }) Text(骰子 2).fontSize(14).fontColor(#888) } }.margin({ bottom: 16 }) // 点数显示 Text(点数: ${this.isRolling ? ... : this.total}) .fontSize(36).fontWeight(FontWeight.Bold).fontColor(#F44336) // 趣味描述 Text(this.description).fontSize(18).fontColor(#FF9800).margin({ top: 4 }) // 投掷统计 Text(已掷 ${this.rollCount} 次).fontSize(14).fontColor(#888).margin({ top: 8 }) // 掷骰按钮 Button( 掷骰子).width(200).height(56).borderRadius(28) .backgroundColor(#4CAF50).fontSize(22).fontWeight(FontWeight.Bold) .margin({ top: 16 }).onClick(() this.roll()) } .layoutWeight(1).justifyContent(FlexAlign.Center) } .width(100%).height(100%).backgroundColor(#f8f8f8) } }3.2 Unicode 骰面符号映射diceFace(val: number): string { const faces [⚀, ⚁, ⚂, ⚃, ⚄, ⚅]; return faces[val - 1]; }这是本应用最独特的设计——使用 Unicode 的弥生骰子符号Miscellaneous Symbols来展示骰面数字Unicode字符描述1U2680⚀白色骰子12U2681⚁白色骰子23U2682⚂白色骰子34U2683⚃白色骰子45U2684⚄白色骰子56U2685⚅白色骰子6这些 Unicode 字符在支持它们的字体中会渲染为带点阵的骰面图案无需使用图片资源即可获得良好的视觉效果。这种纯文本符号的方案比图片方案具有以下优势无资源依赖不需要额外加载图片减小应用体积矢量缩放任意放大倍数下保持清晰无锯齿主题适配颜色可以通过 CSS 属性自由控制3.3 动画期间实时随机值get d1(): number { return this.isRolling ? Math.floor(Math.random() * 6) 1 : this.dice1; } get d2(): number { return this.isRolling ? Math.floor(Math.random() * 6) 1 : this.dice2; }这是一个精妙的设计——在动画期间每次 UI 渲染时都实时生成新的随机值而非等待setInterval更新State变量。这样做的好处更高的刷新率脱离 60ms 定时器的限制随 UI 渲染帧率同步更新更流畅的动画渲染引擎每帧都看到新值视觉上更加流畅资源节约无需额外的定时器管理动画结束后的判定逻辑if (count 12) { clearInterval(intervalId); this.isRolling false; // 停止实时随机 this.total this.dice1 this.dice2; this.rollCount; }当isRolling设为false后get d1()返回固定的this.dice1值即setInterval最后一次设置的值骰子显示定格。3.4 animation 动画属性Text(this.diceFace(...)) .fontSize(80).animation({ duration: 60 })animation()是 HarmonyOS 中用于为组件添加过渡动画的属性方法。它的参数是一个AnimationOptions对象包含以下常用属性duration动画持续时间毫秒默认1000curve动画曲线Curve.EaseInOut等默认Curve.Easedelay动画延迟开始的时间毫秒iterations动画重复次数-1表示无限循环playMode播放模式PlayMode.Normal/PlayMode.Reverse/ …在本应用中animation({ duration: 60 })设置为60ms的过渡动画。当骰子符号从 ⚀ 变为 ⚅ 时这个过渡动画让变化过程更加平滑而非生硬的瞬间切换。需要注意的是这里的animation属性作用于组件属性变化时的过渡效果。当diceFace()的返回值变化时文字内容变化动画会尝试在变化前后之间进行过渡。但对于文字内容变化过渡效果可能不如数值属性如width、opacity、rotate等明显。3.5 趣味描述逻辑get description(): string { const t this.total; if (t 2) return 蛇眼; // 两个1点 if (t 7) return Lucky 7!; // 最常见的点数 if (t 12) return 双六; // 两个6点 if (t 3) return 霉运; // 12 最小非蛇眼组合 return ; // 其他组合不显示描述 }四种特殊组合的判定规则蛇眼Snake Eyes两个骰子都是1点总和2 —— 骰子中最小的组合名字源于两个1点看起来像蛇的眼睛Lucky 7总和7 —— 两个骰子点数和为7的概率最高1/6被视为幸运数字双六Boxcars两个骰子都是6点总和12 —— 最大的组合霉运总和312—— 仅次于蛇眼的坏运气组合其他的组合4、5、6、8、9、10、11不显示趣味描述保持界面简洁。四、HarmonyOS 特性深度解析4.1 get 计算属性的实时求值机制本应用中使用了三个get计算属性d1、d2、description它们在每次 UI 渲染时被重新求值。这种机制的关键理解是// 在模板中引用 get 属性 Text(this.diceFace(this.isRolling ? this.d1 : this.dice1))ArkTS 的渲染引擎在每次渲染周期中检测到State变量变化如isRolling从false变为true触发重新渲染重新求值所有模板表达式计算this.d1→ 调用get d1()→ 如果是动画状态则生成随机值计算this.diceFace(随机值)→ 映射为 Unicode 符号更新 UI 显示这个链条中get d1()在动画期间每次渲染都生成不同的随机值产生了持续的视觉变化。4.2 setInterval 的正确清理时机roll() { if (this.isRolling) return; this.isRolling true; let count 0; const intervalId setInterval(() { // ... if (count 12) { clearInterval(intervalId); // 在回调内部清理 this.isRolling false; } }, 60); }定时器的清理发生在回调函数内部通过count计数达到阈值时。这种自清理模式的好处确定性清理清理时机明确且可控无需外部干预不需要在aboutToDisappear中额外清理避免内存泄漏定时器在完成任务后立即释放相比而言如果定时器是持续运行的如时钟应用则必须在aboutToDisappear中清理。4.3 条件渲染的不同模式本应用在三个地方使用了条件渲染展示了三种不同的场景// 模式1三元表达式 - 用于文字内容 Text(点数: ${this.isRolling ? ... : this.total}) // 模式2三元表达式 - 用于 get 属性选择 Text(this.diceFace(this.isRolling ? this.d1 : this.dice1)) // 模式3get 属性条件返回 - 用于描述文字 get description(): string { if (...) return ...; return ; }这三种模式分别适用于内联条件简单的文字内容切换值选择器根据状态选择不同的数据源计算属性复杂的多分支逻辑4.4 绿色主题的按钮设计Button( 掷骰子).width(200).height(56).borderRadius(28) .backgroundColor(#4CAF50).fontSize(22).fontWeight(FontWeight.Bold)按钮使用了 Material Design 的绿色#4CAF50在心理学上绿色代表开始和行动。大尺寸200×56、大圆角28px和大字体22px的设计让按钮成为页面中最重要的视觉焦点吸引用户点击。五、UI/UX 设计模式5.1 页面布局结构┌─────────────────────────────────┐ │ Row: 导航栏 │ ├─────────────────────────────────┤ │ Column: 内容区 (居中) │ │ ├── Row: 两个骰子 │ │ │ ├── 骰子1 (⚀~⚅) │ │ │ └── 骰子2 (⚀~⚅) │ │ ├── Text: 点数总和 (大号红色) │ │ ├── Text: 趣味描述 (橙色) │ │ ├── Text: 投掷次数 (灰色) │ │ └── Button: 掷骰子 │ └─────────────────────────────────┘5.2 视觉层次与色彩搭配元素颜色作用骰子符号默认黑色核心视觉元素80px超大字号点数总和#F44336红色最重要的结果信息趣味描述#FF9800橙色次要的趣味性信息掷骰按钮#4CAF50绿色行动召唤CTA统计次数#888灰色辅助信息5.3 信息层级设计信息按照重要性从上到下排列骰子符号最大最醒目—— 核心视觉点数总和36px红色—— 核心数据趣味描述18px橙色—— 情感化反馈投掷次数14px灰色—— 辅助统计每个层级之间通过margin、字号和颜色差异形成清晰的区分。六、最佳实践与经验总结6.1 随机数的正确生成Math.floor(Math.random() * 6) 1这是生成 1~6 范围内随机整数的标准写法。分解如下Math.random()生成 [0, 1) 区间的浮点数含0不含1* 6扩展到 [0, 6) 区间Math.floor()向下取整到 [0, 5] 整数 1偏移到 [1, 6]重要提示在鸿蒙开发中Math.random()是伪随机数生成器对于游戏类应用足够了。6.2 动画期间的防重入roll() { if (this.isRolling) return; this.isRolling true; // ... }isRolling标志位实现了按钮防重入。在动画进行中即使用户多次点击掷骰子按钮roll()方法也会在入口处直接返回不会启动新的动画循环。这是交互设计中防止意外操作的关键机制。6.3 Unicode 符号的兼容性Unicode 骰面符号U2680~U2685属于 Unicode 3.0 标准在现代设备和操作系统中都有良好的支持。但在某些老旧设备或不支持这些符号的字体中可能会显示为空白或方框。在生产环境中建议提供降级方案检测符号渲染情况如果无法正常显示则使用数字替代使用自定义字体打包包含这些符号的字体文件图片备选在极端情况下回退到图片显示6.4 代码组织建议对于更复杂的骰子应用可以考虑以下优化方向多种骰子类型支持4面D4、8面D8、10面D10、12面D12、20面D20等角色扮演游戏常用骰子投掷历史记录保存最近几次投掷的结果统计图表展示点数分布的概率统计shake 传感器利用设备加速度传感器通过摇动手机触发掷骰联机对战通过分布式能力实现多人掷骰比大小七、总结骰子应用在约85行代码中精彩展示了 ArkTS 声明式 UI 开发的多个核心概念Unicode 符号映射使用 ⚀⚁⚂⚃⚄⚅ 纯文本方式渲染骰面get 实时随机值动画期间每次渲染生成新随机数setInterval 定时器动画60ms 间隔的快速滚动效果animation 过渡动画属性变化的平滑过渡防重入机制isRolling 标志位的按钮锁定条件描述逻辑特殊点数组合的趣味反馈这个应用以掷骰子这一简单的游戏需求为切入点展示了纯文本符号渲染和定时器动画的核心技术。它不仅是桌游爱好者的实用工具更是学习 ArkTS 动画编程和符号处理的优秀教学案例。简洁的代码背后蕴含着丰富的交互设计智慧和性能优化技巧。

相关新闻

面试官大笑:“一个任务拆给 5 个 Subagent 并行跑,不比 1 个快 5 倍?“我摇头:“快不了,还可能更慢“

面试官大笑:“一个任务拆给 5 个 Subagent 并行跑,不比 1 个快 5 倍?“我摇头:“快不了,还可能更慢“

前两个月,我在重构 AlgoMooc 网站过程中,发现一个问题:在 Claude Code 里把一个任务拆给 5 个 Subagent 并行跑,结果可能比 1 个 agent 从头干到尾还慢? 大多数人的第一反应是反过来的:活是并行干的&#…

2026/7/29 0:15:49阅读更多 →
Blender贝塞尔曲线插件:3大神奇工具彻底改变你的建模体验

Blender贝塞尔曲线插件:3大神奇工具彻底改变你的建模体验

Blender贝塞尔曲线插件:3大神奇工具彻底改变你的建模体验 【免费下载链接】blenderbezierutils Blender Add-on with Bezier Utility Ops 项目地址: https://gitcode.com/gh_mirrors/bl/blenderbezierutils 想要在Blender中轻松绘制完美的贝塞尔曲线吗&#…

2026/7/29 0:15:49阅读更多 →
Adobe软件免费激活终极指南:三步解锁完整功能

Adobe软件免费激活终极指南:三步解锁完整功能

Adobe软件免费激活终极指南:三步解锁完整功能 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP 想要免费使用Adobe全家桶却不知从何入手?Adobe…

2026/7/29 0:13:48阅读更多 →
从矢量场变成强度以后,还能再变回来吗?光学仿真中最容易被忽视的信息不可逆性

从矢量场变成强度以后,还能再变回来吗?光学仿真中最容易被忽视的信息不可逆性

在光学仿真中,我们经常对数据进行简化。完整电磁场可能被转换为:强度分布;总功率;衍射效率;模态功率;远场角分布;平均偏振参数。这样做非常常见,也往往是必要的。因为完整矢量电磁场…

2026/7/29 1:26:08阅读更多 →
算法稳定性与数据分布的内在联系研究

算法稳定性与数据分布的内在联系研究

这里写自定义目录标题算法稳定性与数据分布的内在联系研究引言算法稳定性的定义与分类数据分布的特性与影响算法稳定性与数据分布的理论联系实验验证与案例分析提升稳定性的方法未来研究方向结论参考文献基础算法选择与优化数据结构优化并行化与分布式设计硬件加速技术性能调优…

2026/7/29 1:26:08阅读更多 →
2026 安卓文件/照片备份最全教程

2026 安卓文件/照片备份最全教程

一、前言 安卓备份的核心痛点相较于 iOS 统一的 iCloud 备份体系,安卓生态机型繁杂、备份规则碎片化,绝大多数用户都会遇到四类常见问题:备份不完整:仅同步相册照片,遗漏办公文档、压缩包、聊天缓存等自定义文件画质被…

2026/7/29 1:26:08阅读更多 →
STM32平衡巡迹小车:双PID控制与多传感器融合实战

STM32平衡巡迹小车:双PID控制与多传感器融合实战

1. 项目概述:当“走钢丝”遇上“扫雷”最近在捣鼓一个特别有意思的玩意儿,我把它叫做“走钢丝扫雷的巡迹小车”。光听这个名字,是不是就觉得有点东西?这可不是一个简单的循着黑线跑的玩具车。它的核心挑战在于,要在一条…

2026/7/29 1:26:08阅读更多 →
SpringBoot+Vue+MySQL电商系统与协同过滤算法实践

SpringBoot+Vue+MySQL电商系统与协同过滤算法实践

1. 项目概述黔醉酒业白酒销售系统是一个基于SpringBootVueMySQL技术栈的电商平台,主要面向白酒销售行业。系统采用前后端分离架构,后端使用SpringBoot框架提供RESTful API接口,前端采用Vue.js构建用户界面,数据库选用MySQL存储业务…

2026/7/29 1:26:07阅读更多 →
设计模式中的原则

设计模式中的原则

本文是【GoF设计模式】系列的前置篇前言本篇可以当做设计模式学习前的入门,也可以当成设计模式学习后的复习。先看一段典型的"面条代码": // 一个臃肿的 UserService:校验、持久化、通知、日志全堆在一起 public class UserService…

2026/7/29 1:24:07阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“! 在构建复杂的 Agent 系统时,我们经常会遇到这样的场景:Agent 正在执行一个多步骤的任务,比如“下单购买商品”,但执行到一半时,我们…

2026/7/29 0:01:46阅读更多 →
自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…

2026/7/29 0:01:46阅读更多 →
【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:01:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →