鸿蒙 ArkUI 实现敲木鱼小游戏

敲木鱼是一款具有禅意的趣味小游戏,本文将通过鸿蒙 ArkUI 框架的实现代码,逐步解析其核心技术点,包括动画驱动状态管理音效震动反馈等。

一、架构设计与工程搭建

1.1 项目结构解析

完整项目包含以下核心模块:

├── entry/src/main/ets/
│   ├── components/         // 自定义组件库
│   ├── model/              // 数据模型(如StateArray)
│   ├── pages/              // 页面组件(WoodenFishGame.ets)
│   └── resources/          // 多媒体资源(木鱼图标、音效)

通过模块化设计分离 UI层(pages)、逻辑层(model)、资源层(resources),符合鸿蒙应用开发规范。

1.2 组件化开发模式

使用 @Component 装饰器创建独立可复用的 UI 单元,@Entry 标记为页面入口。关键状态通过 @State 管理:

@Entry
@Component
struct WoodenFishGame {@State count: number = 0;                // 功德计数器@State scaleWood: number = 1;           // 木鱼缩放系数@State rotateWood: number = 0;          // 木鱼旋转角度@State animateTexts: Array<StateArray> = []; // 动画队列private audioPlayer?: media.AVPlayer;    // 音频播放器实例private autoPlay: boolean = false;       // 自动敲击标志位
}

@State 实现了 响应式编程:当变量值变化时,ArkUI 自动触发关联 UI 的重新渲染。

二、动画系统深度解析

2.1 木鱼敲击复合动画

动画分为 按压收缩(100ms)和 弹性恢复(200ms)两个阶段,通过 animateTo 实现平滑过渡:

playAnimation() {// 第一阶段:快速收缩+左旋animateTo({duration: 100, curve: Curve.Friction // 摩擦曲线模拟物理阻力}, () => {this.scaleWood = 0.9; // X/Y轴缩放到90%this.rotateWood = -2; // 逆时针旋转2度});// 第二阶段:弹性恢复setTimeout(() => {animateTo({duration: 200,curve: Curve.Linear // 线性恢复保证流畅性}, () => {this.scaleWood = 1; this.rotateWood = 0;});}, 100); // 延迟100ms衔接动画
}

曲线选择

  • Curve.Friction 模拟木槌敲击时的瞬间阻力
  • Curve.Linear 确保恢复过程无加速度干扰

2.2 功德文字飘浮动画

采用 动态数组管理 + 唯一ID标识 实现多实例独立控制:

countAnimation() {const animId = new Date().getTime(); // 时间戳生成唯一ID// 添加新动画元素this.animateTexts = [...this.animateTexts, { id: animId, opacity: 1, offsetY: 20 }];// 启动渐隐上移动画animateTo({duration: 800,curve: Curve.EaseOut // 缓出效果模拟惯性}, () => {this.animateTexts = this.animateTexts.map(item => item.id === animId ? { ...item, opacity: 0, offsetY: -100 } : item);});// 动画完成后清理内存setTimeout(() => {this.animateTexts = this.animateTexts.filter(t => t.id !== animId);}, 800); // 与动画时长严格同步
}

关键技术点

  1. 数据驱动:通过修改 animateTexts 数组触发 ForEach 重新渲染
  2. 分层动画opacity 控制透明度,offsetY 控制垂直位移
  3. 内存优化:定时清理已完成动画元素,防止数组膨胀

三、多模态交互实现

3.1 触觉震动反馈

调用 @kit.SensorServiceKit 的振动模块实现触觉反馈:

vibrator.startVibration({type: "time",       // 按时间模式振动duration: 50        // 50ms短震动
}, {id: 0,              // 振动器IDusage: 'alarm'      // 资源使用场景标识
});

参数调优建议

  • 时长:50ms 短震动模拟木鱼敲击的“清脆感”
  • 强度:鸿蒙系统自动根据 usage 分配最佳强度等级

3.2 音频播放与资源管理

通过 media.AVPlayer 实现音效播放:

aboutToAppear(): void {media.createAVPlayer().then(player => {this.audioPlayer = player;this.audioPlayer.url = ""; this.audioPlayer.loop = false; // 禁用循环播放});
}// 敲击时重置播放进度
if (this.audioPlayer) {this.audioPlayer.seek(0);    // 定位到0毫秒this.audioPlayer.play();     // 播放音效
}

最佳实践

  1. 预加载资源:在 aboutToAppear 阶段提前初始化播放器
  2. 避免延迟:调用 seek(0) 确保每次点击即时发声
  3. 资源释放:需在 onPageHide 中调用 release() 防止内存泄漏

四、自动敲击功能实现

4.1 定时器与状态联动

通过 Toggle 组件切换自动敲击模式:

// 状态切换回调
Toggle({ type: ToggleType.Checkbox, isOn: false }).onChange((isOn: boolean) => {this.autoPlay = isOn;if (isOn) {this.startAutoPlay();} else {clearInterval(this.intervalId); // 清除指定定时器}});// 启动定时器
private intervalId: number = 0;
startAutoPlay() {this.intervalId = setInterval(() => {if (this.autoPlay) this.handleTap();}, 400); // 400ms间隔模拟人类点击频率
}

关键改进点

  • 使用 intervalId 保存定时器引用,避免 clearInterval() 失效
  • 间隔时间 400ms 平衡流畅度与性能消耗

4.2 线程安全与性能保障

风险点:频繁的定时器触发可能导致 UI 线程阻塞
解决方案

// 在 aboutToDisappear 中清除定时器
aboutToDisappear() {clearInterval(this.intervalId);
}

确保页面隐藏时释放资源,避免后台线程持续运行。

五、UI 布局与渲染优化

5.1 层叠布局与动画合成

使用 Stack 实现多层 UI 元素的叠加渲染:

Stack() {// 木鱼主体(底层)Image($r("app.media.icon_wooden_fish")).width(280).height(280).margin({ top: -10 }).scale({ x: this.scaleWood, y: this.scaleWood }).rotate({ angle: this.rotateWood });// 功德文字(上层)ForEach(this.animateTexts, (item, index) => {Text(`+1`).translate({ y: -item.offsetY * index }) // 按索引错位显示});
}

渲染优化技巧

  • 为静态图片资源添加 fixedSize(true) 避免重复计算
  • 使用 translate 代替 margin 实现位移,减少布局重排

5.2 状态到 UI 的高效绑定

通过 链式调用 实现样式动态绑定:

Text(`功德 +${this.count}`).fontSize(20).fontColor('#4A4A4A').margin({ top: 20 + AppUtil.getStatusBarHeight() // 动态适配刘海屏})

适配方案

  • AppUtil.getStatusBarHeight() 获取状态栏高度,避免顶部遮挡
  • 使用鸿蒙的 弹性布局(Flex)自动适应不同屏幕尺寸

六、完整代码

import { media } from '@kit.MediaKit';
import { vibrator } from '@kit.SensorServiceKit';
import { AppUtil, ToastUtil } from '@pura/harmony-utils';
import { StateArray } from '../model/HomeModel';@Entry
@Component
struct WoodenFishGame {@State count: number = 0;@State scaleWood: number = 1;@State rotateWood: number = 0;audioPlayer?: media.AVPlayer;// 添加自动敲击功能autoPlay: boolean = false;// 新增状态变量@State animateTexts: Array<StateArray> = []aboutToAppear(): void {media.createAVPlayer().then(player => {this.audioPlayer = playerthis.audioPlayer.url = ""})}startAutoPlay() {setInterval(() => {if (this.autoPlay) {this.handleTap();}}, 400);}// 敲击动画playAnimation() {animateTo({duration: 100,curve: Curve.Friction}, () => {this.scaleWood = 0.9;this.rotateWood = -2;});setTimeout(() => {animateTo({duration: 200,curve: Curve.Linear}, () => {this.scaleWood = 1;this.rotateWood = 0;});}, 100);}// 敲击处理handleTap() {this.count++;this.playAnimation();this.countAnimation();// 在handleTap中添加:vibrator.startVibration({type: "time",duration: 50}, {id: 0,usage: 'alarm'});// 播放音效if (this.audioPlayer) {this.audioPlayer.seek(0);this.audioPlayer.play();}}countAnimation(){// 生成唯一ID防止动画冲突const animId = new Date().getTime()// 初始化动画状态this.animateTexts = [...this.animateTexts, {id: animId, opacity: 1, offsetY: 20}]// 执行动画animateTo({duration: 800,curve: Curve.EaseOut}, () => {this.animateTexts = this.animateTexts.map(item => {if (item.id === animId) {return { id:item.id, opacity: 0, offsetY: -100 }}return item})})// 动画完成后清理setTimeout(() => {this.animateTexts = this.animateTexts.filter(t => t.id !== animId)}, 800)}build() {Column() {// 计数显示Text(`功德 +${this.count}`).fontSize(20).margin({ top: 20+AppUtil.getStatusBarHeight() })// 木鱼主体Stack() {// 可敲击部位Image($r("app.media.icon_wooden_fish")).width(280).height(280).margin({ top: -10 }).scale({ x: this.scaleWood, y: this.scaleWood }).rotate({ angle: this.rotateWood }).onClick(() => this.handleTap())// 功德文字动画容器ForEach(this.animateTexts, (item:StateArray,index) => {Text(`+1`).fontSize(24).fontColor('#FFD700').opacity(item.opacity).margin({ top: -100}) // 初始位置调整.translate({ y: -item.offsetY*index }) // 使用translateY实现位移.animation({ duration: 800, curve: Curve.EaseOut })})}.margin({ top: 50 })Row(){// 自动敲击开关(扩展功能)Toggle({ type: ToggleType.Checkbox, isOn: false }).onChange((isOn: boolean) => {// 可扩展自动敲击功能this.autoPlay = isOn;if (isOn) {this.startAutoPlay();} else {clearInterval();}})Text("自动敲击")}.alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center).width("100%").position({bottom:100})}.width('100%').height('100%').backgroundColor('#f0f0f0')}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/26771.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式日志和责任链路

目录 日志问题 责任链问题 分布式日志 GrayLog简介 部署安装 收集日志 配置Inputs 集成微服务 日志回收策略 搜索语法 搜索语法 自定义展示字段 日志统计仪表盘 创建仪表盘 链路追踪 APM 什么是APM 原理 技术选型 Skywalking简介 部署安装 微服务探针 整合…

进程间通信(IPC)与匿名管道

目录 一、进程间通信&#xff08;IPC&#xff09;概述 1. 核心概念 2. 核心目的 3. IPC分类 二、匿名管道 1. 什么是管道 示例&#xff1a;Shell中的管道 2. 匿名管道的原理 3. 匿名管道的实现 3.1 创建管道&#xff1a;pipe()函数 3.2 使用 fork 共享管道 3.3 站在…

构建智能 SQL 查询代理agent,把整个查询过程模块化,既能自动判断使用哪些表,又能自动生成 SQL 语句,最终返回查询结果

示例代码&#xff1a; import os import getpass from dotenv import load_dotenv from pyprojroot import here from typing import List from pprint import pprint from pydantic import BaseModel from langchain_core.tools import tool from langchain_core.runnables i…

从矩阵乘法探秘Transformer

目录 前言1. transformer背景1.1 回顾线性代数的知识1.1.1 矩阵和行向量1.1.2 矩阵相乘和算子作用1.1.3 从分块矩阵的乘法来看 Q K T V QK^TV QKTV 1.2 encoder-decoder1.3 低阶到高阶语义向量的转换1.4 核心的问题 2. transformer网络结构2.1 基于KV查询的相似性计算2.2 在一个…

用友NC系列漏洞检测利用工具

声明&#xff01;本文章所有的工具分享仅仅只是供大家学习交流为主&#xff0c;切勿用于非法用途&#xff0c;如有任何触犯法律的行为&#xff0c;均与本人及团队无关&#xff01;&#xff01;&#xff01; 目录标题 YongYouNcTool启动及适配环境核心功能界面预览一键检测命令执…

MacBook Pro使用FFmpeg捕获摄像头与麦克风推流音视频

FFmpeg查看macos系统音视频设备列表 ffmpeg -f avfoundation -list_devices true -i "" 使用摄像头及麦克风同时推送音频及视频流: ffmpeg -f avfoundation -pixel_format yuyv422 -framerate 30 -i "0:1" -c:v libx264 -preset ultrafast -b:v 1000k -…

zookeeper-docker版

Zookeeper-docker版 1 zookeeper概述 1.1 什么是zookeeper Zookeeper是一个分布式的、高性能的、开源的分布式系统的协调&#xff08;Coordination&#xff09;服务&#xff0c;它是一个为分布式应用提供一致性服务的软件。 1.2 zookeeper应用场景 zookeeper是一个经典的分…

【数据结构】LRUCache|并查集

目录 一、LRUCache 1.概念 2.实现:哈希表双向链表 3.JDK中类似LRUCahe的数据结构LinkedHashMap &#x1f525;4.OJ练习 二、并查集 1. 并查集原理 2.并查集代码实现 3.并查集OJ 一、LRUCache 1.概念 最近最少使用的&#xff0c;一直Cache替换算法 LRU是Least Recent…

AUTOSAR简介

目录 核心目标 架构分层 核心优势 经典AUTOSAR vs 自适应AUTOSAR 典型应用场景 挑战与未来发展 相关企业介绍 1. 传统汽车电子供应商&#xff08;Tier1&#xff09; 2. 软件服务商与工具链企业 3. 新兴科技公司与自动驾驶企业 4. 基础软件与工具链企业 5. 高校与研…

国产开源AI平台Cherry Studio详解:联网搜索升级与ChatBox对比指南

文章概述 Cherry Studio是一款功能强大的国产开源AI工具&#xff0c;支持本地部署、知识库管理、多模型聚合和联网搜索等特性。本文将详细介绍Cherry Studio的核心功能、1.0版本新特性及与ChatBox的对比分析&#xff0c;帮助用户选择最适合自己的AI助手工具。 Cherry Studio核…

【Python 初级函数详解】—— 参数沙漠与作用域丛林的求生指南

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

【计算机网络入门】初学计算机网络(六)

目录 1.回忆数据链路层作用 2. 组帧 2.1 四种组帧方法 2.1.1 字符计数法 2.1.2 字节填充法 2.1.3 零比特填充法 2.1.4 违规编码法 3. 差错控制 3.1 检错编码 3.1.1 奇偶校验码 3.1.2 CRC&#xff08;循环冗余校验&#xff09;校验码 3.2 纠错编码 3.2.1 海明校验码…

二叉树的核心技术与C++实现:存储、遍历与递归应用

目录 一、二叉树基础概念与常见类型 1.1 二叉树核心概念 1.2 四种常见二叉树类型 类型1&#xff1a;满二叉树 类型2&#xff1a;完全二叉树 类型3&#xff1a;二叉搜索树&#xff08;BST&#xff09; 类型4&#xff1a;平衡二叉树&#xff08;AVL&#xff09; 类型5&…

《白帽子讲 Web 安全:点击劫持》

目录 摘要&#xff1a; 一、点击劫持概述 二、点击劫持的实现示例&#xff1a;诱导用户收藏指定淘宝商品 案例 构建恶意页面&#xff1a; 设置绝对定位和z - index&#xff1a; 控制透明度&#xff1a; 三、其他相关攻击技术 3.1图片覆盖攻击与 XSIO 3.2拖拽劫持与数据…

IDEA 使用codeGPT+deepseek

一、环境准备 1、IDEA 版本要求 安装之前确保 IDEA 处于 2023.x 及以上的较新版本。 2、Python 环境 安装 Python 3.8 或更高版本 为了确保 DeepSeek 助手能够顺利运行&#xff0c;您需要在操作系统中预先配置 Python 环境。具体来说&#xff0c;您需要安装 Python 3.8 或更高…

Linux:进程替换

目录 进程程序替换 替换原理 进程替换相关函数 环境变量与进程替换函数 命令行解释器(my_xshell) 进程程序替换 上一篇进程控制讲到&#xff0c;父进程创建子进程就是为了让子进程去做一些另外的事情&#xff0c;但是不管怎么说&#xff0c;子进程的部分代码也还是父进程…

Navicat连接虚拟机数据库详细教程

Navicat连接虚拟机数据库详细教程 以Windows主机 上的navicat 连接ubuntu虚拟机为例 确认虚拟机ip地址和主机ip地址 主机地址查询 cmd输入ipconfig 登录mysql 创建用户 CREATE USER newuserlocalhost IDENTIFIED BY password; CREATE USER newuser% IDENTIFIED BY passwor…

Java内存管理与性能优化实践

Java内存管理与性能优化实践 Java作为一种广泛使用的编程语言&#xff0c;其内存管理和性能优化是开发者在日常工作中需要深入了解的重要内容。Java的内存管理机制借助于垃圾回收&#xff08;GC&#xff09;来自动处理内存的分配和释放&#xff0c;但要实现高效的内存管理和优…

解码中国AI双雄突围:DeepSeek破壁与英伟达反攻背后的算力暗战

一、算力困局下的中国突围术 2024年夏季的科技界暗流涌动&#xff1a;北京中关村的服务器机房里&#xff0c;寒武纪最新MLU300X芯片正以每秒120万亿次运算支撑着自动驾驶系统的实时决策&#xff1b;上海张江的AI实验室中&#xff0c;DeepSeek团队通过神经元分块技术将模型参数压…

【Python · Pytorch】Conda介绍 DGL-cuda安装

本文仅涉及DGL库介绍与cuda配置&#xff0c;不包含神经网络及其训练测试。 起因&#xff1a;博主电脑安装了 CUDA 12.4 版本&#xff0c;但DGL疑似没有版本支持该CUDA版本。随即想到可利用Conda创建CUDA12.1版本的虚拟环境。 1. Conda环境 1.1 Conda环境简介 Conda&#xff1…