鸿蒙NEXT开发案例:指尖轮盘

【1】引言
“指尖轮盘”是一个简单而有趣的互动游戏(类似抓阄),这个应用通过触摸屏幕的方式,让玩家参与一个激动人心的游戏,最终选出幸运的赢家。未来可以进一步扩展功能,如增加游戏模式、优化动画效果、增加音效等,提升用户体验。
【2】环境准备
电脑系统:windows 10
开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
工程版本:API 12
真机:mate60 pro
语言:ArkTS、ArkUI
【功能概述】
1. 显示标题和游戏说明,引导玩家参与游戏。
2. 支持多位玩家参与,每位玩家以不同颜色的圆形表示。
3. 根据触摸屏幕的手指数量,动态更新界面状态。
4. 实现倒计时功能,倒计时结束后随机选择一位玩家作为赢家。
【技术实现要点】
1. 使用鸿蒙系统提供的组件和状态管理功能,构建界面和处理用户交互。
2. 利用动画效果,为玩家圆形添加缩放动画,增强视觉效果。
3. 通过定时器实现倒计时和随机选择玩家的功能。
4. 处理触摸事件,根据手指数量更新玩家位置和界面状态。
【开发流程】
1. 创建玩家位置类(PlayerPosition),用于管理玩家属性和动画效果。
2. 设计入口组件(WheelGamePage),包含玩家列表、倒计时、触摸事件处理等功能。
3. 构建UI界面,显示标题、说明文本和玩家圆形,实现动态更新和交互效果。
4. 实现倒计时和随机选择玩家的逻辑,提升游戏体验。
【完整代码】
@ObservedV2
// 观察类,用于观察属性变化
class PlayerPosition {
@Trace isVisible: boolean = false // 玩家是否可见
@Trace startX: number = 0 // 玩家起始X坐标
@Trace startY: number = 0 // 玩家起始Y坐标
@Trace scaleOptions: ScaleOptions = { x: 0.5, y: 0.5 } // 玩家缩放选项
cellWidth: number = 100 // 玩家圆形的宽度
color: string // 玩家颜色 constructor(color: string) { // 构造函数,初始化颜色
this.color = color
} isRunningAnimation: boolean = false // 动画是否正在运行 setShowAnimation() { // 设置显示动画
if (!this.isRunningAnimation) { // 如果动画未运行
this.isRunningAnimation = true // 标记为正在运行
animateToImmediately({
// 开始动画
delay: 0,
duration: 500,
curve: Curve.Linear,
iterations: 1,
onFinish: () => { // 动画完成后的回调
console.info(`onFinish 1`)
animateToImmediately({
// 开始第二个动画
delay: 0,
duration: 300,
iterations: -1,
curve: Curve.Linear,
onFinish: () => { // 第二个动画完成后的回调
console.info(`onFinish 2`)
}
}, () => {
this.scaleOptions = { x: 1.1, y: 1.1 } // 设置缩放
})
}
}, () => {
this.scaleOptions = { x: 1.0, y: 1.0 } // 动画结束时重置缩放
})
}
}
} @Entry
// 入口组件
@Component
struct WheelGamePage {
@State playerList: PlayerPosition[] = [// 玩家列表
new PlayerPosition("#26c2ff"),
new PlayerPosition("#978efe"),
new PlayerPosition("#c389fe"),
new PlayerPosition("#ff85bd"),
new PlayerPosition("#ff7051"),
new PlayerPosition("#fea800"),
new PlayerPosition("#ffcf18"),
new PlayerPosition("#a9c92a"),
]
@State showTitle: boolean = true // 是否显示标题
@State showInstructions: boolean = true // 是否显示说明
@State showCountdown: boolean = false // 是否显示倒计时
@State @Watch('countdownNumberChanged') countdownNumber: number = 3 // 倒计时数字
@State selectIndex: number = -1 // 最终选中的玩家下标
countdownTimerId: number = 0 // 倒计时定时器ID
randomSelectionTimerId: number = 0 // 随机选择定时器ID countdownNumberChanged() { // 倒计时变化的处理函数
if (this.countdownNumber <= 0) { // 如果倒计时结束
this.startRandomSelection(); // 开始随机选择
} else {
this.selectIndex = -1; // 结束随机显示
}
} startRandomSelection() { // 开始随机选择玩家
const len = this.playerList.length + Math.floor(Math.random() * this.playerList.length); // 随机长度
const visiblePlayers = this.playerList.filter(player => player.isVisible); // 可见玩家列表
console.info(`visiblePlayers:${JSON.stringify(visiblePlayers)}`); let count = 0; // 当前计数
let iteration = 0; // 当前迭代次数 clearInterval(this.randomSelectionTimerId); // 清除之前的定时器
this.randomSelectionTimerId = setInterval(() => { // 设置新的定时器
console.info(`count:${count}`);
console.info(`iteration:${iteration}`);
console.info(`len:${len}`); if (iteration >= len) { // 如果达到迭代次数
clearInterval(this.randomSelectionTimerId); // 清除定时器
return;
} this.selectIndex = count++ % visiblePlayers.length; // 随机选择玩家
iteration++; // 增加迭代次数
}, 150); // 每150毫秒执行一次
} updatePlayerPositions(touchPoints: TouchObject[]) { // 更新玩家位置
this.playerList.forEach((player, index) => { // 遍历玩家列表
if (index < touchPoints.length) { // 如果触摸点数量大于玩家索引
player.isVisible = true // 设置玩家可见
player.setShowAnimation() // 设置动画
player.startX = touchPoints[index].x // 更新X坐标
player.startY = touchPoints[index].y // 更新Y坐标
} else {
player.isVisible = false // 设置玩家不可见
}
})
} updateTextState(touchCount: number) { // 更新文本状态
this.countdownNumber = 3 // 重置倒计时
if (touchCount === 0) { // 如果没有触摸
this.showTitle = true // 显示标题
this.showInstructions = true // 显示说明
this.showCountdown = false // 隐藏倒计时
} else if (touchCount === 1) { // 如果有一个触摸
this.showTitle = false // 隐藏标题
this.showInstructions = true // 显示说明
this.showCountdown = false // 隐藏倒计时
} else if (touchCount >= 2) { // 如果有两个或更多触摸
this.showTitle = false // 隐藏标题
this.showInstructions = false // 隐藏说明
this.showCountdown = true // 显示倒计时
clearInterval(this.countdownTimerId) // 清除之前的倒计时
this.countdownTimerId = setInterval(() => { // 设置新的倒计时
if (this.countdownNumber >= 0) { // 如果倒计时未结束
this.countdownNumber-- // 倒计时减一
} else {
clearInterval(this.countdownTimerId) // 倒计时结束,清除定时器
}
}, 1000) // 每秒执行一次
}
} build() { // 构建UI
Stack() { // 创建堆栈布局
Text("指尖轮盘")// 显示标题文本
.width('100%')// 宽度100%
.height(80)// 高度80
.textAlign(TextAlign.Center)// 文本居中
.fontSize(20)// 字体大小20
.fontColor("#0c0c0c")// 字体颜色
.visibility(this.showTitle ? Visibility.Visible : Visibility.Hidden)// 根据状态设置可见性
.draggable(false) // 不可拖动
Stack() { // 创建另一个堆栈布局
Text(`1. 邀请您的朋友一起加入这场激动人心的游戏吧!只需2到${this.playerList.length}位玩家即可开始。\n
2. 准备好后,请每位参与者伸出一根手指轻轻按住手机屏幕。倒计时3秒后,游戏自动启动,或者您也可以手动点击“开始”按钮。\n
3. 紧紧握住你的手指,直到动画结束。幸运之神将会眷顾其中一位玩家,成为本局的赢家!`)// 显示说明文本
.textAlign(TextAlign.JUSTIFY)// 文本对齐
.fontSize(20)// 字体大小20
.fontColor("#0c0c0c")// 字体颜色
.padding(20)// 内边距20
.visibility(this.showInstructions ? Visibility.Visible : Visibility.None)// 根据状态设置可见性
.draggable(false) // 不可拖动 Text(this.countdownNumber > 0 ? `倒计时${this.countdownNumber}秒后开始` : ``)// 显示倒计时文本
.textAlign(TextAlign.Center)// 文本居中
.fontSize(20)// 字体大小20
.fontColor("#0c0c0c")// 字体颜色
.padding(20)// 内边距20
.visibility(this.showCountdown ? Visibility.Visible : Visibility.None)// 根据状态设置可见性
.draggable(false)
}.width('100%').height('100%') // 设置堆栈宽高
.draggable(false) ForEach(this.playerList, (player: PlayerPosition, index: number) => { // 遍历玩家列表
Stack() { // 创建堆栈布局
Circle()// 创建外圈圆形
.width(player.cellWidth + 10)// 外圈宽度比玩家宽度大10
.height(player.cellWidth + 10)// 外圈高度比玩家高度大10
.fill(player.color)// 填充外圈颜色
.fillOpacity(0.5)// 设置外圈透明度为0.5
.visibility(player.isVisible ? Visibility.Visible : Visibility.None)// 根据玩家可见性设置外圈可见性
.draggable(false)// 外圈不可拖动
.scale(player.scaleOptions) // 设置外圈缩放选项 Circle()// 创建内圈圆形
.width(player.cellWidth)// 内圈宽度
.height(player.cellWidth)// 内圈高度
.fill(player.color)// 填充内圈颜色
.fillOpacity(1)// 设置内圈透明度为1
.visibility(player.isVisible ? Visibility.Visible : Visibility.None)// 根据玩家可见性设置内圈可见性
.draggable(false)// 内圈不可拖动
.scale(player.scaleOptions) // 设置内圈缩放选项 }.draggable(false) // 堆栈布局不可拖动
.scale(this.selectIndex == index ? { x: 1.5, y: 1.5 } : { x: 1.0, y: 1.0 }) // 根据选中状态设置缩放
.margin({ left: player.startX - player.cellWidth / 2, top: player.startY - player.cellWidth / 2 }) // 设置玩家位置
})
}
.align(Alignment.TopStart) // 设置堆栈对齐方式
.height('100%') // 设置堆栈高度为100%
.width('100%') // 设置堆栈宽度为100%
.draggable(false) // 堆栈不可拖动
.onTouch((event: TouchEvent) => { // 处理触摸事件
if (event.type == TouchType.Down) { // 按下事件
this.updatePlayerPositions(event.touches) // 更新玩家位置
this.updateTextState(event.touches.length) // 更新文本状态
} else if (event.type == TouchType.Move) { // 移动事件
this.updatePlayerPositions(event.touches) // 更新玩家位置
} else if (event.type == TouchType.Up) { // 抬起事件
this.updateTextState(event.touches.length - 1) // 更新文本状态
if (event.touches.length - 1 === 0) { // 如果没有触摸
this.updatePlayerPositions([]) // 清空玩家位置
}
} else if (event.type == TouchType.Cancel) { // 取消事件
this.updatePlayerPositions(event.touches) // 更新玩家位置
}
})
}
}
鸿蒙NEXT开发案例:指尖轮盘的更多相关文章
- 最全华为鸿蒙 HarmonyOS 开发资料汇总
开发 本示例基于 OpenHarmony 下的 JavaScript UI 框架,进行项目目录解读,JS FA.常用和自定义组件.用户交互.JS 动画的实现,通过本示例可以基本了解和学习到 JavaS ...
- 使用Jquery+EasyUI 进行框架项目开发案例讲解之五 模块(菜单)管理源码分享
http://www.cnblogs.com/huyong/p/3454012.html 使用Jquery+EasyUI 进行框架项目开发案例讲解之五 模块(菜单)管理源码分享 在上四篇文章 ...
- 使用Jquery+EasyUI 进行框架项目开发案例讲解之四 组织机构管理源码分享
http://www.cnblogs.com/huyong/p/3404647.html 在上三篇文章 <使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享> ...
- 使用Jquery+EasyUI 进行框架项目开发案例讲解之三---角色管理源码分享
使用Jquery+EasyUI 进行框架项目开发案例讲解之三 角色管理源码分享 在上两篇文章 <使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享> ...
- 使用Jquery+EasyUI 进行框架项目开发案例讲解之二---用户管理源码分享
使用Jquery+EasyUI 进行框架项目开发案例讲解之二 用户管理源码分享 在上一篇文章<使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享>我们分享 ...
- 【推荐】使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享
使用Jquery+EasyUI 进行框架项目开发案例讲解之一 员工管理源码分享 在开始讲解之前,我们先来看一下什么是Jquery EasyUI?jQuery EasyUI是一组基于jQuery的U ...
- 使用Jquery+EasyUI进行框架项目开发案例解说之中的一个---员工管理源代码分享
使用Jquery+EasyUI 进行框架项目开发案例解说之中的一个 员工管理源代码分享 在開始解说之前,我们先来看一下什么是Jquery EasyUI?jQuery EasyUI是一组基于jQuery ...
- 百度UEditor开发案例(JSP)
本案例的开发环境:MyEclipse+tomcat+jdk 本案例的开发内容: 用百度编辑器发布新闻(UEditor的初始化开发部署) 编辑已发过的新闻(UEditor的应用——编辑旧文章) ...
- 使用Jquery+EasyUI 进行框架项目开发案例解说之二---用户管理源代码分享
使用Jquery+EasyUI 进行框架项目开发案例解说之二 用户管理源代码分享 在上一篇文章<使用Jquery+EasyUI进行框架项目开发案例解说之中的一个---员工管理源代码分享> ...
- 使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享
使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享 使用Jquery+EasyUI 进行框架项目开发案例讲解之一 员工管理源码分享 在开始讲解之前,我们先来看一下什 ...
随机推荐
- 组合逻辑环(Combinational Logic Loop)
组合逻辑电路 组合逻辑电路是数字电子学中一类基本的电路类型,它由一系列逻辑门组成,用于实现特定的逻辑功能.与时序逻辑电路不同,组合逻辑电路的输出完全取决于当前的输入信号,而不受之前输入的影响.换句话说 ...
- freertos学习(九)软件定时器
软件定时器 软件定时器是freeRTOS通过一个硬件定时器,实现的定时器.可以实现不同时长的多个定时任务 不从中断上下文中执行定时器回调函数(不消耗任何处理时间) 实现流程 设置软件定时器,推入定时器 ...
- .NET 9 的新亮点:AI就绪 ,拥抱她
.NET 9 即将发布 RC1, 今年初.NET 团队在发布.NET 9 Preview 1版本时写了一篇文章<我们对 .NET 9 的愿景>,其中特别提到了对AI的展望 .NET 9,我 ...
- 上架难、买量贵?即构 Web 端 1v1 方案全力加速泛娱乐出海
1v1 视频社交以其功能简洁.互动性强.匹配效率高等特点,在陌生人社交玩法中饱受用户欢迎,平台仅需让两个用户通过匹配.选择等形式连通后,用户即可在房内进行 1v1 视频社交,且在社交的同时又极具私密性 ...
- ZEGO 自研客户端配置管理系统 —— 云控
一.常规客户端配置的弊端 客户端配置信息通常会通过一个静态文件进行管理,或存放在本地或者通过远程获取.存在本地最大的问题是不易更新,所以通常做法是通过远程获取. 我们通过两种常见的场景来看看静态文件管 ...
- TypeScript – 冷知识
当 generic return 遇上 parameter 报错了.原因是 querySelector 默认返回类型是抽象的 Element. 而 method 参数要求的是具体的 InputElem ...
- EF Core – ExecuteUpdate and ExecuteDelete (Bulk updates 批量更新和删除)
前言 EF Core 在 SaveChanges 之后会一句一句的去更新和删除数据. 有时候这个效率是很差的. 而 SQL 本来就支持批量更新和删除, 所以是 EF Core 的缺失. 在 EF Co ...
- Naive UI 2.38.1
https://www.naiveui.com/zh-CN/os-theme/docs/i18n 配置字体 Naive UI 可以和 vfonts 配合,你可以简单的引入 vfonts 中的字体,包含 ...
- OAuth2.0授权-gitee授权码模式
OAuth2.0授权验证-gitee授权码模式 本文主要介绍如何笔者自己是如何使用gitee提供的OAuth2.0协议完成授权验证并登录到自己的系统,完整模式如图 1.创建应用 打开gitee个人中心 ...
- QT6框架也能开发Web浏览器应用程序:QT6框架如何编译运行测试WebAssembly应用程序?
QT6框架也能开发Web浏览器应用程序:QT6框架如何编译运行测试WebAssembly应用程序? 简介 本文将介绍如何使用QT6框架开发Web浏览器应用程序,并介绍具体的编译运行测试WebAssem ...