拼图
介绍
该示例通过@ohos.multimedia.image和@ohos.file.photoAccessHelper接口实现获取图片,以及图片裁剪分割的功能。
效果预览
利用说明:
- 利用预置相机照相后启动应用,应用首页会读取设备内的图片文件并展示获取到的第一个图片,没有图片时图片位置表现空白;
- 点击开始按钮开始后,时间开始倒计时,在规定时间内未完成拼图则游戏结束。在游戏中,玩家点击重新开始举行游戏重置;
- 点击开始游戏后,玩家可以根据上方的大图,点击黄格四周的图片移动,点击后图片和黄格互换位置,最终拼成完整的图片;
- 不在游戏中时,玩家可以点击上方大图,选择自定义图片来举行拼图游戏。
详细实现
- 游戏中图片裁剪分割的效果实如今ImageModel中,源码参考[ImageModel]:
- /*
- * Copyright (c) 2022 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import { image } from '@kit.ImageKit';
- import { photoAccessHelper } from '@kit.MediaLibraryKit';
- import { dataSharePredicates } from '@kit.ArkData';
- import { Context } from '@kit.AbilityKit';
- import { fileIo } from '@kit.CoreFileKit';
- import Logger from './Logger';
- import PictureItem from './PictureItem';
- import { CommonConstants as Common } from '../common/CommonConstants';
- const TAG = '[ImageModel]';
- export default class ImageModel {
- private phAccessHelper: photoAccessHelper.PhotoAccessHelper | null = null;
- constructor(context: Context) {
- this.phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
- }
- async getAllImg(): Promise<photoAccessHelper.PhotoAsset[]> {
- Logger.info('getAllImg');
- let photoList: Array<photoAccessHelper.PhotoAsset> = [];
- if (this.phAccessHelper === null) {
- Logger.info('phAccessHelper fail');
- return photoList;
- }
- let fileKeyType = photoAccessHelper.PhotoKeys.PHOTO_TYPE;
- let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
- Logger.info(fileKeyType);
- let fetchOptions: photoAccessHelper.FetchOptions = {
- fetchColumns: [],
- predicates: predicates
- };
- try {
- let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
- await this.phAccessHelper.getAssets(fetchOptions);
- if (fetchResult != undefined) {
- Logger.info('fetchResult success');
- let photoAsset: Array<photoAccessHelper.PhotoAsset> = await fetchResult.getAllObjects();
- if (photoAsset != undefined && photoAsset.length > 0) {
- for (let i = 0; i < photoAsset.length; i++) {
- if (photoAsset[i].photoType === 1) {
- photoList.push(photoAsset[i]);
- }
- }
- }
- }
- } catch (err) {
- Logger.error('getAssets failed, message = ', err);
- }
- Logger.info('photoList success');
- return photoList;
- }
- async splitPic(index: number): Promise<PictureItem[]> {
- let imagePixelMap: PictureItem[] = [];
- let imagesData: Array<photoAccessHelper.PhotoAsset> = await this.getAllImg();
- let imagePackerApi = image.createImagePacker();
- fileIo.open(imagesData[index].uri, fileIo.OpenMode.READ_ONLY).then(async (file: fileIo.File) => {
- let fd: number = file.fd;
- let imageSource = image.createImageSource(fd);
- let imageInfo = await imageSource.getImageInfo();
- Logger.info(TAG, `sizeImg createImageSource ${JSON.stringify(imageSource)}`);
- let height = imageInfo.size.height / Common.SPLIT_COUNT;
- for (let i = 0; i < Common.SPLIT_COUNT; i++) {
- for (let j = 0; j < Common.SPLIT_COUNT; j++) {
- let picItem: PictureItem;
- if (i === Common.SPLIT_COUNT - 1 && j === Common.SPLIT_COUNT - 1) {
- picItem = new PictureItem(Common.PICTURE_ITEM_NUMBER, {} as image.PixelMap);
- imagePixelMap.push(picItem);
- } else {
- Logger.info(TAG, `sizeImg x = ${imageInfo.size.width / Common.SPLIT_COUNT} y = ${height}`);
- let decodingOptions: image.DecodingOptions = {
- desiredRegion: {
- size: {
- height: height,
- width: imageInfo.size.width / Common.SPLIT_COUNT
- }, x: j * imageInfo.size.width / Common.SPLIT_COUNT, y: i * height
- }
- }
- imagePixelMap.push(
- new PictureItem(i * Common.SPLIT_COUNT + j, await imageSource.createPixelMap(decodingOptions)));
- }
- }
- }
- imagePackerApi.release();
- fileIo.closeSync(fd);
- })
- return imagePixelMap;
- }
- }
复制代码
- 获取本地图片:首先利用getPhotoAccessHelper获取相册管理模块实例,然后利用getAssets方法获取文件资源,最后利用getAllObjects获取检索效果中的所有文件资产方便展示;
- 裁剪图片准备:裁剪图片必要利用@ohos.multimedia.image接口,裁剪前必要申请图片编辑权限,利用requestPermissionsFromUser申请,源码参考[Index.ets];
- /*
- * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import { mediaquery } from '@kit.ArkUI';
- import { Permissions, abilityAccessCtrl } from '@kit.AbilityKit';
- import { emitter } from '@kit.BasicServicesKit';
- import { photoAccessHelper } from '@kit.MediaLibraryKit';
- import GameRules from '../model/GameRules';
- import ImagePicker from '../common/ImagePicker';
- import Logger from '../model/Logger';
- import PictureItem from '../model/PictureItem';
- import ImageModel from '../model/ImageModel';
- import { CommonConstants as Common } from '../common/CommonConstants';
- const PERMISSIONS: Array<Permissions> = [
- 'ohos.permission.READ_MEDIA',
- 'ohos.permission.WRITE_MEDIA',
- 'ohos.permission.MEDIA_LOCATION',
- 'ohos.permission.MANAGE_MISSIONS'
- ];
- @Entry
- @Component
- struct Index {
- @State numArray: PictureItem[] = [];
- @State imgData: Array<photoAccessHelper.PhotoAsset> = [];
- @State @Watch('onTimeOver') gameTime: number = Common.GAME_TIME;
- @State @Watch('onImageChange') index: number = Common.ZERO;
- @State isLand: boolean = false;
- @StorageLink('isGameStart') isGameStart: boolean = false;
- private listener = mediaquery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)');
- private ImageModel: ImageModel = new ImageModel(getContext(this));
- private game: GameRules = new GameRules();
- private timer: number = -1;
- private isRefresh: boolean = false;
- onLand = (mediaQueryResult: mediaquery.MediaQueryResult) => {
- Logger.info(`[eTSMediaQuery.Index]onLand: mediaQueryResult.matches=${mediaQueryResult.matches}`);
- this.isLand = mediaQueryResult.matches;
- };
- over = () => {
- Logger.info(Common.TAG, 'emitter on , eventID = 0');
- for (let i = Common.ZERO; i < Common.LOOP; i++) {
- this.numArray[i].index = i;
- }
- this.gameOver();
- };
- timeEnd = () => {
- Logger.info(Common.TAG, 'emitter on , eventID = 1');
- this.gameTime = Common.ZERO;
- };
- async aboutToAppear() {
- this.listener.on('change', this.onLand);
- await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this), PERMISSIONS);
- this.imgData = await this.ImageModel.getAllImg();
- Logger.info(Common.TAG, `images = ${this.imgData.length}`);
- this.numArray = await this.ImageModel.splitPic(this.index);
- // Test case. The game is simulated successfully.
- emitter.on({ eventId: Common.ZERO, priority: Common.ZERO }, this.over);
- // Test case. End of simulation time.
- emitter.on({ eventId: Common.ONE, priority: Common.ZERO }, this.timeEnd);
- }
- onTimeOver() {
- if (this.gameTime === Common.ZERO) {
- this.isGameStart = false;
- AlertDialog.show({ message: 'TimeOver' });
- clearInterval(this.timer);
- }
- }
- async onImageChange() {
- this.isRefresh = true;
- this.dialogController.close();
- this.numArray = [];
- this.numArray = await this.ImageModel.splitPic(this.index);
- this.init();
- this.isGameStart = false;
- this.isRefresh = false;
- }
- init() {
- this.gameTime = Common.GAME_TIME;
- clearInterval(this.timer);
- }
- gameOver() {
- let count = Common.ZERO;
- for (let i = Common.ZERO; i < Common.LOOP; i++) {
- if (this.numArray[i].index === i) {
- count++;
- } else {
- count = Common.ZERO;
- break;
- }
- }
- if (count === Common.LOOP) {
- this.isGameStart = false;
- AlertDialog.show({ message: $r('app.string.congratulations') });
- clearInterval(this.timer);
- this.gameTime = Common.GAME_TIME;
- }
- }
- start() {
- this.init();
- this.timer = setInterval(() => {
- this.gameTime--;
- }, Common.TIME_NUMBER)
- }
- dialogController: CustomDialogController = new CustomDialogController({
- builder: ImagePicker({
- imagesData: this.imgData,
- index: $index
- }),
- autoCancel: true,
- gridCount: Common.GRID_COUNT
- })
- @Builder
- ImageShow() {
- Image(this.imgData[this.index].uri)
- .id(Common.IMAGE_SHOW)
- .width(Common.IMAGE_WIDTH)
- .height($r('app.float.imageShow_height'))
- .objectFit(ImageFit.Fill)
- .onClick(async () => {
- if (this.isRefresh) {
- return;
- }
- this.imgData = await this.ImageModel.getAllImg();
- setTimeout(() => {
- this.dialogController.open();
- }, Common.TIME);
- })
- }
- @Builder
- ImageGrid(leftMargin: number, topMargin: number) {
- Grid() {
- ForEach(this.numArray, (item: PictureItem, index) => {
- GridItem() {
- Image(item.pixelMap)
- .width(Common.GRID_IMAGE_WIDTH)
- .objectFit(ImageFit.Fill)
- .height($r('app.float.grid_image_height'))
- }
- .id(`image${index}`)
- .backgroundColor(item.pixelMap === undefined ? $r('app.color.blank_picture_background') : $r('app.color.picture_background'))
- .onClick(() => {
- if (this.isRefresh) {
- return;
- }
- if (this.isGameStart) {
- this.isRefresh = true;
- this.numArray = this.game.gameInit(index, this.numArray);
- this.gameOver();
- this.isRefresh = false;
- }
- })
- }, (item: PictureItem) => JSON.stringify(item))
- }
- .id(Common.IMAGE_GRID)
- .columnsTemplate(Common.COLUMN_TEMPLATE)
- .columnsGap($r('app.float.gap'))
- .rowsGap($r('app.float.gap'))
- .width(Common.IMAGE_WIDTH)
- .height($r('app.float.grid_height'))
- .margin({ left: leftMargin, top: topMargin })
- }
- build() {
- Column() {
- Row() {
- Text(`Time:0${Math.floor(this.gameTime / Common.SECOND)}:${this.gameTime % Common.SECOND < Common.TEN
- ? Common.STRING_ZERO + this.gameTime % Common.SECOND : this.gameTime % Common.SECOND}`)
- .id(Common.TIME_STRING)
- .margin({ top: Common.MARGIN, bottom: Common.MARGIN })
- }
- if (this.imgData.length > Common.ZERO) {
- if (this.isLand) {
- Row() {
- this.ImageShow()
- this.ImageGrid(Common.TEN, Common.ZERO)
- }
- .margin({ top: Common.MARGIN })
- } else {
- Column() {
- this.ImageShow()
- this.ImageGrid(Common.ZERO, Common.FIVE)
- }
- .margin({ top: Common.MARGIN })
- }
- }
- Button($r('app.string.start'), { type: ButtonType.Capsule, stateEffect: true })
- .id(Common.START_BUTTON)
- .height($r('app.float.button_height'))
- .width(Common.FULL_WIDTH)
- .fontSize($r('app.float.button_font_size'))
- .margin({ top: Common.MARGIN })
- .backgroundColor(this.isGameStart ? $r('app.color.forbid') : $r('app.color.allow'))
- .enabled(!this.isGameStart)
- .onClick(() => {
- this.isGameStart = true;
- this.start();
- this.numArray = this.game.gameBegin(this.numArray);
- })
- Button($r('app.string.restart'), { type: ButtonType.Capsule, stateEffect: true })
- .id(Common.RESTART_BUTTON)
- .height($r('app.float.button_height'))
- .width(Common.FULL_WIDTH)
- .fontSize($r('app.float.button_font_size'))
- .margin({ top: Common.MARGIN })
- .backgroundColor(this.isGameStart ? $r('app.color.allow') : $r('app.color.forbid'))
- .enabled(this.isGameStart)
- .onClick(() => {
- this.isGameStart = true;
- this.start();
- this.numArray = this.game.gameBegin(this.numArray);
- })
- }
- .width(Common.FULL_WIDTH)
- .height(Common.FULL_HEIGHT)
- .padding({ left: Common.PERCENT, right: Common.PERCENT })
- }
- }
复制代码
- 图片编辑:首先利用createImagePacker创建ImagePacker实例,然后利用fs.open打开文件,调用createImageSource接口创建图片源实例方便操作图片,接下来利用getImageInfo方法获取图片巨细便于分割,最后利用createPixelMap方法传入每一份的尺寸参数完成图片裁剪。
以上就是本篇文章所带来的鸿蒙开辟中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在末端找我可全部拿到!
下面是鸿蒙的完整学习门路,展示如下:
除此之外,根据这个学习鸿蒙全栈学习门路,也附带一整套完整的学习【文档+视频】,内容包含如下:
内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开辟、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开辟、鸿蒙项目实战)等技术知识点。资助各人在学习鸿蒙路上快速成长!
鸿蒙【北向应用开辟+南向系统层开辟】文档
鸿蒙【底子+实战项目】视频
鸿蒙面经
为了避免各人在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢各人观看!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |