【HarmonyOS】九宫格拼图游戏
构建一个简易九宫格拼图游戏应用程序,利用picker从相册选择图片、使用fs拷贝路径、使用PixelMap切分图片
import picker from '@ohos.file.picker'; import fs from '@ohos.file.fs'; import image from '@ohos.multimedia.image'; import { common } from '@kit.AbilityKit'; import { promptAction } from '@kit.ArkUI'; // 定义拼图组件接口 interface PuzzlePiece { // 拼图块的像素地图 pixelMap: image.PixelMap; // 原始图片中的索引位置 originalIndex: number; } // 使用装饰器定义页面组件 @Entry @Component struct Page30 { // 状态变量:选中图片的URI @State imgUri: string = ''; // 状态变量:原始图片的URI @State imgOriginal: string = ''; // 状态变量:存储拼图块的数组 @State puzzlePieces: Array<PuzzlePiece> = []; // 状态变量:记录当前选中的拼图块索引 @State selectedPieceIndex: number = -1; // 弹出图片选择器方法 async openPicker() { try { // 设置图片选择器选项 const photoSelectOptions = new picker.PhotoSelectOptions(); // 限制只能选择一张图片 photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; photoSelectOptions.maxSelectNumber = 1; // 创建并实例化图片选择器 const photoViewPicker = new picker.PhotoViewPicker(); // 选择图片并获取图片URI let uris: picker.PhotoSelectResult = await photoViewPicker.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.imgUri = newUrl; // 更新状态变量:保存原始图片的URI this.imgOriginal = uri; // 图片更改时触发的方法 this.imgChange(); } catch (e) { console.error('openPicker', JSON.stringify(e)); } } // 图片更改处理方法 async imgChange() { try { // 创建图片源对象 const imageSource: image.ImageSource = image.createImageSource(this.imgUri); // 图片解码选项 let decodingOptions: image.DecodingOptions = { editable: true, desiredPixelFormat: 3, }; // 创建像素地图 let mPixelMap: image.PixelMap = await imageSource.createPixelMap(decodingOptions); // 获取图片信息 let mImageInfo: image.ImageInfo = await mPixelMap.getImageInfo(); // 计算每个拼图块的大小 const cropSize: image.Size = { width: mImageInfo.size.width / 3, height: mImageInfo.size.height / 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.imgUri); // 创建新像素地图 let mPixelMap = await imageSource.createPixelMap(decodingOptions); // 计算裁剪区域 const cropRect: image.Region = { x: col * cropSize.width, y: row * cropSize.height, size: cropSize, }; // 裁剪像素地图 await mPixelMap.crop(cropRect); // 创建并添加拼图块至数组 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.imgOriginal) { Text('原始图片↓'); Image(this.imgOriginal) .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.selectedPieceIndex == index ? { x: 0.5, y: 0.5 } : { x: 1, y: 1 }) // 添加点击事件处理 .onClick(() => { // 处理拼图交换逻辑 if (this.selectedPieceIndex == -1) { this.selectedPieceIndex = index; } else if (this.selectedPieceIndex == index) { this.selectedPieceIndex = -1; } else { let temp: PuzzlePiece = this.puzzlePieces[this.selectedPieceIndex]; this.puzzlePieces[this.selectedPieceIndex] = this.puzzlePieces[index]; this.puzzlePieces[index] = temp; this.selectedPieceIndex = -1; // 检查拼图是否完成 let isSucc: boolean = true; for (let i = 0; i < this.puzzlePieces.length; i++) { console.info('====item', this.puzzlePieces[i].originalIndex, i); if (this.puzzlePieces[i].originalIndex !== i) { isSucc = false; break; } } // 如果拼图完成,弹出提示对话框 if (isSucc) { promptAction.showDialog({ message: '拼图完成!', }); } } }); } }) // End of ForEach } // End of Grid .backgroundColor("#fafafa"); // 设置网格背景色 } } // End of Column .width('100%'); // 设置列宽度为100% } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了