回复
鸿蒙NEXT开发案例:拼图
zhongcx
发布于 2024-12-1 09:21
浏览
0收藏
【引言】
在本案例中,我们将展示如何使用鸿蒙NEXT框架开发一个简单的拼图游戏。该游戏允许用户选择一张图片,并将其分割成多个拼图块,用户可以通过点击交换拼图块的位置,最终完成拼图。
【环境准备】
电脑系统:windows 10
开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
工程版本:API 12
真机:Mate 60 Pro
语言:ArkTS、ArkUI
【项目结构】
本项目主要由以下几个部分组成:
图片选择:用户可以从设备中选择一张图片。
拼图生成:将选中的图片分割成多个拼图块。
拼图交换:用户可以通过点击拼图块进行交换。
拼图完成检测:检查用户是否完成拼图,并弹出提示。
【关键代码解析】
- 导入必要的模块
我们首先导入了处理文件、图像、用户界面等功能所需的模块。
import { fileIo as fs } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
- 定义拼图块接口
我们定义了一个 PuzzlePiece 接口,用于描述拼图块的属性,包括像素地图和原始图片中的索引位置。
interface PuzzlePiece {
pixelMap: image.PixelMap;
originalIndex: number;
}
- 图片选择功能
在 openPicker 方法中,我们使用 photoAccessHelper 创建一个图片选择器,用户选择图片后,我们将其复制到应用的文件目录中,并更新状态变量。
async openPicker() {
// 创建图片选择选项
let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 设置只选择图片类型
PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 设置最大选择数量为1
PhotoSelectOptions.maxSelectNumber = 1;
// 执行图片选择操作
let uris: photoAccessHelper.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
// 处理选择的图片
}
- 拼图块生成
在 imgChange 方法中,我们将选中的图片分割成9个拼图块,并将其存储在 puzzlePieces 数组中。
async imgChange() {
// 创建图片源对象
const imageSource: image.ImageSource = image.createImageSource(this.selectedImageUrl);
// 计算每个拼图块的大小
const pieceSize: image.Size = {
width: mImageInfo.size.width / 3,
height: mImageInfo.size.height / 3,
};
// 遍历生成拼图块
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
// 裁剪像素地图
await mPixelMap.crop(cutRegion);
// 添加拼图块至数组
this.puzzlePieces.push(piece);
}
}
}
- 拼图交换逻辑
在 build 方法中,我们构建了用户界面,并为每个拼图块添加了点击事件处理逻辑,实现拼图块的交换。
.onClick(() => {
// 处理拼图交换逻辑
if (this.selectedPiece == -1) {
this.selectedPiece = index; // 第一次点击选择拼图块
} else {
// 交换拼图块
let temp: PuzzlePiece = this.puzzlePieces[this.selectedPiece];
this.puzzlePieces[this.selectedPiece] = this.puzzlePieces[index];
this.puzzlePieces[index] = temp;
this.selectedPiece = -1;
// 检查拼图是否完成
}
});
【结论】
通过本案例,我们展示了如何使用鸿蒙NEXT框架开发一个简单的拼图游戏。该项目涵盖了图片选择、拼图生成、拼图交换等基本功能,适合初学者学习和实践鸿蒙NEXT开发。希望本案例能够帮助您更好地理解鸿蒙NEXT的开发流程。
【完整代码】
// 导入必要的模块
import { fileIo as fs } from '@kit.CoreFileKit'; // 文件操作模块
import { common } from '@kit.AbilityKit'; // 应用能力模块
import { promptAction } from '@kit.ArkUI'; // 用户界面提示模块
import { image } from '@kit.ImageKit'; // 图像处理模块
import { photoAccessHelper } from '@kit.MediaLibraryKit'; // 媒体库访问助手模块
// 定义拼图块的接口
interface PuzzlePiece {
// 拼图块的像素地图
pixelMap: image.PixelMap;
// 原始图片中的索引位置
originalIndex: number;
}
// 使用装饰器定义页面组件
@Entry // 表明这是一个入口组件
@Component // 表明这是一个组件
struct Page30 {
// 状态变量:选中图片的URI
@State selectedImageUrl: string = '';
// 状态变量:原始图片的URI
@State originalImageUrl: string = '';
// 状态变量:存储拼图块的数组
@State puzzlePieces: Array<PuzzlePiece> = [];
// 状态变量:记录当前选中的拼图块索引
@State selectedPiece: number = -1;
// 弹出图片选择器方法
async openPicker() {
try {
// 创建图片选择选项
let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 设置只选择图片类型
PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 设置最大选择数量为1
PhotoSelectOptions.maxSelectNumber = 1;
// 创建图片选择器实例
let photoPicker = new photoAccessHelper.PhotoViewPicker();
// 执行图片选择操作
let uris: photoAccessHelper.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
// 如果没有选择任何图片则返回
if (!uris || uris.photoUris.length === 0) return;
// 获取选中图片的第一张URI
let uri: string = uris.photoUris[0];
// 打开文件读取流
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
// 获取当前上下文
let context = getContext(this) as common.UIAbilityContext;
// 新建一个保存裁剪后图片的路径
let newUrl = context.filesDir + '/test' + new Date().getTime() + '.jpg';
// 复制图片到新的路径
fs.copyFileSync(file.fd, newUrl);
// 关闭文件读取流
fs.closeSync(file);
// 更新状态变量:设置显示图片的URI
this.selectedImageUrl = newUrl;
// 更新状态变量:保存原始图片的URI
this.originalImageUrl = uri;
// 图片更改时触发的方法
this.imgChange();
} catch (e) {
// 错误处理
console.error('openPicker', JSON.stringify(e));
}
}
// 图片更改处理方法
async imgChange() {
try {
// 创建图片源对象
const imageSource: image.ImageSource = image.createImageSource(this.selectedImageUrl);
// 图片解码选项
let decodingOptions: image.DecodingOptions = {
editable: true, // 允许编辑
desiredPixelFormat: 3, // 设置期望的像素格式
};
// 创建像素地图
let mPixelMap: image.PixelMap = await imageSource.createPixelMap(decodingOptions);
// 获取图片信息
let mImageInfo: image.ImageInfo = await mPixelMap.getImageInfo();
// 计算每个拼图块的大小
const pieceSize: image.Size = {
width: mImageInfo.size.width / 3, // 每个拼图块宽度为图片宽度的1/3
height: mImageInfo.size.height / 3, // 每个拼图块高度为图片高度的1/3
};
// 清空已有拼图块数据
this.puzzlePieces.splice(0);
// 遍历图片生成9个拼图块
let count = 0;
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
// 创建基于原图的新图片源
const imageSource = image.createImageSource(this.selectedImageUrl);
// 创建新像素地图
let mPixelMap = await imageSource.createPixelMap(decodingOptions);
// 计算裁剪区域
const cutRegion: image.Region = {
x: col * pieceSize.width, // 裁剪起始X坐标
y: row * pieceSize.height, // 裁剪起始Y坐标
size: pieceSize, // 裁剪区域大小
};
// 裁剪像素地图
await mPixelMap.crop(cutRegion);
// 创建并添加拼图块至数组
const piece: PuzzlePiece = {
pixelMap: mPixelMap,
originalIndex: count++, // 记录拼图块在原始图片中的索引
};
this.puzzlePieces.push(piece);
}
}
// 打乱拼图块顺序
for (let i = this.puzzlePieces.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); // 随机选择一个位置
let temp: PuzzlePiece = this.puzzlePieces[i]; // 临时存储当前拼图块
this.puzzlePieces[i] = this.puzzlePieces[j]; // 交换位置
this.puzzlePieces[j] = temp;
}
} catch (e) {
// 错误处理
console.error('imgChange', JSON.stringify(e));
}
}
// 构建UI界面
build() {
Column() { // 创建垂直布局
// 添加选择图片按钮,点击后调用打开图片选择器方法
Button('选择图片→').onClick(() => {
this.openPicker();
});
// 显示原始图片(如果已选择)
if (this.originalImageUrl) {
Text('原始图片↓'); // 文本提示
Image(this.originalImageUrl) // 显示原始图片
.width('180lpx') // 设置图片宽度
.height('180lpx') // 设置图片高度
.objectFit(ImageFit.Contain); // 图片适应方式
}
// 如果有拼图块,则显示游戏区
if (this.puzzlePieces.length > 0) {
Text('游戏图片↓'); // 文本提示
// 游戏区域采用网格布局
Grid() {
// 遍历所有拼图块并创建网格项
ForEach(this.puzzlePieces, (item: PuzzlePiece, index: number) => {
GridItem() {
// 显示拼图块图像
Image(item.pixelMap)
.width('200lpx') // 设置图片宽度
.height('200lpx') // 设置图片高度
.margin('5lpx') // 设置边距
// 根据是否选中调整缩放比例
.scale(this.selectedPiece == index ? { x: 0.5, y: 0.5 } : { x: 1, y: 1 })
.animation({})
// 添加点击事件处理
.onClick(() => {
// 处理拼图交换逻辑
if (this.selectedPiece == -1) {
this.selectedPiece = index; // 第一次点击选择拼图块
} else if (this.selectedPiece == index) {
this.selectedPiece = -1; // 再次点击取消选择
} else {
let temp: PuzzlePiece = this.puzzlePieces[this.selectedPiece];
this.puzzlePieces[this.selectedPiece] = this.puzzlePieces[index];
this.puzzlePieces[index] = temp;
this.selectedPiece = -1;
// 检查拼图是否完成
let isSucc: boolean = true;
for (let i = 0; i < this.puzzlePieces.length; i++) {
if (this.puzzlePieces[i].originalIndex !== i) {
isSucc = false;
break;
}
}
// 如果拼图完成,弹出提示对话框
if (isSucc) {
promptAction.showDialog({
message: '拼图完成!',
});
}
}
});
}
})
}
.backgroundColor("#fafafa"); // 设置网格背景色
}
}
.width('100%'); // 设置列宽度为100%
}
}
分类
标签
赞
收藏
回复
相关推荐