鸿蒙开发案例:七巧板

server/2024/11/14 15:49:02/

【1】引言(完整代码在最后面)

本文介绍的拖动七巧板游戏是一个简单的益智游戏,用户可以通过拖动和旋转不同形状的七巧板块来完成拼图任务。整个游戏使用鸿蒙Next框架开发,利用其强大的UI构建能力和数据响应机制,实现了流畅的用户体验。

【2】环境准备

电脑系统:windows 10

开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

工程版本:API 12

真机:Mate 60 Pro

语言:ArkTS、ArkUI

【3】关键技术点

1. TangramBlock 类定义 游戏的核心在于TangramBlock类的定义,它封装了每个七巧板块的属性和行为。类中包含了宽度、高度、颜色、初始和当前偏移量、旋转角度等属性,并提供了重置数据的方法。这为后续的数据绑定和UI渲染奠定了基础。

2. 数据绑定与响应式更新 在鸿蒙Next中,使用@ObservedV2和@Trace装饰器可以轻松实现数据的观察和响应式更新。每当TangramBlock实例中的属性发生变化时,UI会自动更新以反映最新的状态。这种机制极大地简化了数据同步的工作,使得开发者可以专注于逻辑实现而无需担心UI更新的问题。

3. UI构建与布局管理 鸿蒙Next提供了丰富的UI组件和布局工具,使得构建复杂的用户界面变得简单。在这个项目中,我们使用了Column、Stack、Polygon等组件来构建七巧板块的布局。通过嵌套这些组件,我们可以灵活地控制每个板块的位置和大小。

4. 手势处理与交互 为了实现拖动和旋转功能,我们使用了PanGesture和rotate方法来处理用户的触摸和手势操作。当用户拖动板块时,通过更新initialOffsetX和initialOffsetY属性,可以实时反映板块的位置变化。同样,通过增加或减少rotationAngle属性,可以实现板块的旋转效果。

5. 动画与过渡 鸿蒙Next内置了丰富的动画和过渡效果,使得用户交互更加自然。在本项目中,我们使用了animateTo方法来平滑地更新板块的状态,从而提升了用户体验。

5.1 旋转动画属性

.rotate({angle: block.rotationAngle,
})

5.2 翻转动画属性

.rotate({x: 0,y: 1,z: 0,angle: block.flipAngle,centerX: block.width / 2, // 中心点X坐标centerY: block.height / 2, // 中心点Y坐标
})

5.3 平移动画属性

.translate({ x: block.initialOffsetX, y: block.initialOffsetY, z: 0 })

【完整代码】

@ObservedV2 // 监听数据变化的装饰器
class TangramBlock { // 定义七巧板类width: number; // 宽度height: number; // 高度points: Array<[number, number]>; // 点坐标数组color: string; // 颜色@Trace initialOffsetX: number; // 初始X偏移量@Trace initialOffsetY: number; // 初始Y偏移量@Trace currentOffsetX: number; // 当前X偏移量@Trace currentOffsetY: number; // 当前Y偏移量@Trace rotationAngle: number; // 旋转角度@Trace flipAngle: number = 0; // 翻转角度,默认为0@Trace rotateValue: number; // 旋转值defaultInitialOffsetX: number; // 默认初始X偏移量defaultInitialOffsetY: number; // 默认初始Y偏移量defaultRotationAngle: number; // 默认旋转角度constructor(color: string, width: number, height: number, initialOffsetX: number, initialOffsetY: number,rotationAngle: number, points: Array<[number, number]>) {this.initialOffsetX = this.currentOffsetX = this.defaultInitialOffsetX = initialOffsetX; // 初始化X偏移量this.initialOffsetY = this.currentOffsetY = this.defaultInitialOffsetY = initialOffsetY; // 初始化Y偏移量this.rotationAngle = this.rotateValue = this.defaultRotationAngle = rotationAngle; // 初始化旋转角度this.color = color; // 设置颜色this.width = width; // 设置宽度this.height = height; // 设置高度this.points = points; // 设置点坐标数组}resetData() { // 重置数据方法this.flipAngle = 0; // 重置翻转角度this.initialOffsetX = this.currentOffsetX = this.defaultInitialOffsetX; // 重置初始X偏移量this.initialOffsetY = this.currentOffsetY = this.defaultInitialOffsetY; // 重置初始Y偏移量this.rotationAngle = this.rotateValue = this.defaultRotationAngle; // 重置旋转角度}
}const baseUnitLength: number = 80; // 基本单位长度@Entry // 入口组件
@Component // 定义组件
export struct Index { // 主组件@State selectedBlockIndex: number = -1; // 当前选中位置@State blocks: TangramBlock[] = [// 七巧板数组// 小直角等腰三角形new TangramBlock("#fed8e5", baseUnitLength, baseUnitLength, -33.58, -58.02, 135,[[0, 0], [baseUnitLength, 0], [0, baseUnitLength]]),new TangramBlock("#0a0bef", baseUnitLength, baseUnitLength, 78.76, 54.15, 45,[[0, 0], [baseUnitLength, 0], [0, baseUnitLength]]),// 中直角等腰三角形new TangramBlock("#ff0d0c", baseUnitLength * Math.sqrt(2), baseUnitLength * Math.sqrt(2), -33.16, -1.43, -90,[[0, 0], [baseUnitLength * Math.sqrt(2), 0], [0, baseUnitLength * Math.sqrt(2)]]),// 大直角等腰三角形new TangramBlock("#ffa60a", baseUnitLength * 2, baseUnitLength * 2, 22.46, -172, -135,[[0, 0], [baseUnitLength * 2, 0], [0, baseUnitLength * 2]]),new TangramBlock("#3da56a", baseUnitLength * 2, baseUnitLength * 2, 135.65, -59.34, -45,[[0, 0], [baseUnitLength * 2, 0], [0, baseUnitLength * 2]]),// 正方形new TangramBlock("#ffff0b", baseUnitLength, baseUnitLength, 23.07, -1.84, -45,[[0, 0], [baseUnitLength, 0], [baseUnitLength, baseUnitLength], [0, baseUnitLength]]),// 平行四边形new TangramBlock("#5e0b9b", baseUnitLength * 2, baseUnitLength, -61.53, -85.97, 45,[[0, 0], [baseUnitLength, 0], [baseUnitLength * 2, baseUnitLength], [baseUnitLength, baseUnitLength]])];build() { // 构建方法Column({ space: 30 }) { // 创建垂直布局Stack() { // 创建堆叠布局ForEach(this.blocks, (block: TangramBlock, index: number) => { // 遍历七巧板数组Stack() { // 创建堆叠布局Polygon({ width: block.width, height: block.height })// 绘制多边形.points(block.points)// 设置多边形顶点坐标.fill(block.color)// 填充颜色.draggable(false)// 长按不可拖动.rotate({ angle: block.rotationAngle }) // 旋转角度}.rotate({// 旋转x: 0,y: 1,z: 0,angle: block.flipAngle,centerX: block.width / 2, // 中心点X坐标centerY: block.height / 2, // 中心点Y坐标}).width(block.width) // 设置宽度.height(block.height) // 设置高度.onTouch(() => { // 触摸事件this.selectedBlockIndex = index; // 设置选中索引}).draggable(false) // 长按不可拖动.translate({ x: block.initialOffsetX, y: block.initialOffsetY, z: 0 }) // 平移.gesture( // 手势操作PanGesture()// 拖动手势.onActionUpdate((event: GestureEvent | undefined) => { // 更新事件if (event) {block.initialOffsetX = block.currentOffsetX + event.offsetX; // 更新X偏移量block.initialOffsetY = block.currentOffsetY + event.offsetY; // 更新Y偏移量}}).onActionEnd((event: GestureEvent | undefined) => { // 结束事件if (event) {block.currentOffsetX = block.initialOffsetX; // 更新当前X偏移量block.currentOffsetY = block.initialOffsetY; // 更新当前Y偏移量}})).zIndex(this.selectedBlockIndex == index ? 1 : 0) // 设置层级.borderWidth(2) // 边框宽度.borderStyle(BorderStyle.Dashed) // 边框样式.borderColor(this.selectedBlockIndex == index ? "#80a8a8a8" : Color.Transparent) // 边框颜色})}.width('100%').height('750lpx') // 设置宽高.backgroundColor("#e4f2f5") // 背景颜色// 旋转角度计数器Column({ space: 5 }) { // 创建垂直布局,设置间距Text(`旋转角度(间隔5)`).fontColor(Color.Black) // 显示旋转角度文本,设置字体颜色Counter() { // 创建计数器组件Text(`${this.selectedBlockIndex != -1 ? this.blocks[this.selectedBlockIndex].rotationAngle :'-'}`)// 显示当前选中七巧板的旋转角度或占位符.fontColor(Color.Black) // 设置字体颜色}.width(300) // 设置计数器宽度.onInc(() => { // 增加按钮的点击事件if (this.selectedBlockIndex != -1) {animateTo({}, () => {this.blocks[this.selectedBlockIndex].rotationAngle += 5; // 增加旋转角度})}}).onDec(() => { // 减少按钮的点击事件if (this.selectedBlockIndex != -1) {animateTo({}, () => {this.blocks[this.selectedBlockIndex].rotationAngle -= 5; // 减少旋转角度})}});}// 旋转角度计数器Column({ space: 5 }) { // 创建垂直布局,设置间距Text(`旋转角度(间隔45)`).fontColor(Color.Black) // 显示旋转角度文本,设置字体颜色Counter() { // 创建计数器组件Text(`${this.selectedBlockIndex != -1 ? this.blocks[this.selectedBlockIndex].rotationAngle :'-'}`)// 显示当前选中七巧板的旋转角度或占位符.fontColor(Color.Black) // 设置字体颜色}.width(300) // 设置计数器宽度.onInc(() => { // 增加按钮的点击事件if (this.selectedBlockIndex != -1) {animateTo({}, () => {this.blocks[this.selectedBlockIndex].rotationAngle += 45; // 增加旋转角度})}}).onDec(() => { // 减少按钮的点击事件if (this.selectedBlockIndex != -1) {animateTo({}, () => {this.blocks[this.selectedBlockIndex].rotationAngle -= 45; // 减少旋转角度})}});}// 翻转按钮Row() { // 创建水平布局Button('向左翻转').onClick(() => { // 左翻转按钮点击事件animateTo({}, () => {if (this.selectedBlockIndex != -1) {this.blocks[this.selectedBlockIndex].flipAngle -= 180; // 减少翻转角度}});});Button('向右翻转').onClick(() => { // 右翻转按钮点击事件animateTo({}, () => {if (this.selectedBlockIndex != -1) {this.blocks[this.selectedBlockIndex].flipAngle += 180; // 增加翻转角度}});});}.width('100%').justifyContent(FlexAlign.SpaceEvenly) // 设置宽度和内容对齐方式// 重置和隐藏边框按钮Row() { // 创建水平布局Button('重置').onClick(() => { // 重置按钮点击事件animateTo({}, () => {for (let i = 0; i < this.blocks.length; i++) {this.blocks[i].resetData(); // 重置七巧板数据}this.selectedBlockIndex = -1; // 重置选中索引});});Button('隐藏边框').onClick(() => { // 隐藏边框按钮点击事件this.selectedBlockIndex = -1; // 重置选中索引});}.width('100%').justifyContent(FlexAlign.SpaceEvenly) // 设置宽度和内容对齐}.width('100%').height('100%')}
}


http://www.ppmy.cn/server/140816.html

相关文章

Leetcode 同构字符串

这道题的意思&#xff0c;其实就是要求&#xff0c;从s->t和从t->s这两个单向映射只要重复的字符映射关系保持一致就okay&#xff0c;所以我们需要创建2个哈希表。 是的&#xff0c;你理解得很正确&#xff01;这道题的核心要求就是确保两个字符串之间的映射关系是一致且…

Axure PR 9 多级下拉选择器 设计交互

​ 大家好&#xff0c;我是大明同学。 Axure选择器是一种在交互设计中常用的组件&#xff0c;这期内容&#xff0c;我们来探讨Axure中多级下拉选择器设计与交互技巧。 下拉列表选择输入框元件 创建选择输入框所需的元件 1.在元件库中拖出一个矩形元件。 2.选中矩形元件&…

【数学二】线性代数-向量-正交规范化、正交矩阵

考试要求 1、理解 n n n维向量、向量的线性组合与线性表示的概念. 2、理解向量组线性相关、线性无关的概念,掌握向量组线性相关、线性无关的有关性质及判别法. 3、了解向量组的极大线性无关组和向量组的秩的概念,会求向量组的极大线性无关组及秩. 4、了解向量组等价的概念,…

【架构师从入门到进阶】第一章:架构设计基础——第五节:架构演进(缓存到微服务)

【架构师从入门到进阶】第一章&#xff1a;架构设计基础——第五节&#xff1a;架构演进&#xff08;缓存到微服务&#xff09; cdn和反向代理分布式系统&#xff08;分库分表&#xff0c;分布式缓存&#xff0c;分布式文件系统&#xff09;搜索引擎和NoSQL数据库服务拆分微服务…

Pytorch从0复现worc2vec skipgram模型及fasttext训练维基百科语料词向量演示

目录 Skipgram架构 代码开源声明 Pytorch复现Skip-gram 导包及随机种子设置 维基百科数据读取 建立词频元组列表并根据词频排序 建立词频字典,word_id字典,id_word字典 二次采样 正采样与负采样 Skipgram模型类 模型训练 词向量输出 近义词寻找 fasttext训练Skip-…

就是这个样的粗爆,手搓一个计算器:完美平方三项式计算器

作为程序员&#xff0c;没有合适的工具&#xff0c;就得手搓一个&#xff0c;PC端&#xff0c;移动端均可适用。废话不多说&#xff0c;直接上代码。 HTML: <div class"calculator"><div class"input-group"><label for"termA"…

electron 中 contextBridge 作用

1. 安全地实现渲染进程和主进程之间的通信 在 Electron 应用中&#xff0c;主进程和渲染进程是相互隔离的&#xff0c;这是为了安全和稳定性考虑。 然而&#xff0c;在很多情况下&#xff0c;渲染进程需要访问主进程中的某些功能&#xff0c;例如系统级别的操作或者一些应用级…

Oracle RAC的thread

参考文档&#xff1a; Real Application Clusters Administration and Deployment Guide 3 Administering Database Instances and Cluster Databases Initialization Parameter Use in Oracle RAC Table 3-3 Initialization Parameters Specific to Oracle RAC THREAD Sp…