README_zh.md
1# 多图片预览效果实现
2
3### 介绍
4
5图片预览在应用开发中是一种常见场景,这里实现了对于图片的 缩放、移动、旋转、多图片预览功能做了处理。
6
7### 效果图预览
8
9| 图片预览 |
10|------------------------------------------------------------------|
11| <img src="screenshots/picturepreview_example.gif" width="300" /> |
12
13
14**使用说明:**
15
161. 双指捏合对图片进行缩放
172. 双击图片进行图片的大小切换,在放大状态下,双击可恢复默认状态
183. 图片在放大模式下,滑动图片查看图片的对应位置
194. 点击切换图片底图颜色
205. 双指旋转对图片进行旋转
21
22### 工程目录
23
24```
25|entry/src/main/ets
26| |---entryablity
27| | |---EntryAbility.ts // 程序入口类
28| |---constants // 常量
29| |---model // 自定义数据模型
30| |---utils // 工具类
31| |---picturepreviewsample // 示例使用
32| | |---PicturePreviewSample.ets // 场景构建案例
33| |---view // 图片预览方案涉及的主要组件
34| | |---PicturePreview.ets // 图片预览组件
35| | |---PicturePreviewImage.ets // 单张图片的显示组件
36```
37
38### 实现思路
39
401. 使用matrix实现图片的缩放。详情见[PicturePreviewImage.ets](entry/src/main/ets/view/PicturePreviewImage.ets)。
41 ```typescript
42 @State matrix: matrix4.Matrix4Transit = matrix4.identity().copy();
43 Image(this.imageUrl)
44 .transform(this.matrix)
45 ```
462. 使用offset属性对图片进行偏移。详情见[PicturePreviewImage.ets](entry/src/main/ets/view/PicturePreviewImage.ets)。
47 ```typescript
48 @State imageOffsetInfo: OffsetModel = new OffsetModel(0, 0);
49 Image(this.imageUrl)
50 .offset({
51 x: this.imageOffsetInfo.currentX,
52 y: this.imageOffsetInfo.currentY
53 })
54 ```
553. Image的objectFit属性设置为Cover,锁定图片宽高比,并使其能够超出父组件边界显示。详情见[PicturePreviewImage.ets](entry/src/main/ets/view/PicturePreviewImage.ets)。
56 ```typescript
57 Image(this.imageUrl)
58 .objectFit(ImageFit.Cover)
59 ```
604. 提前计算图片信息,并通过Image的宽或高配合aspectRatio设置默认尺寸。详情见[PicturePreviewImage.ets](entry/src/main/ets/view/PicturePreviewImage.ets)。
61 ```typescript
62 Image(this.imageUrl)
63 .width(this.fitWH === "width" ? $r("app.string.image_default_width") : undefined)
64 .height(this.fitWH === "height" ? $r("app.string.image_default_height") : undefined)
65 .aspectRatio(this.imageWHRatio)
66 .onComplete((event) => {
67 if (event) {
68 let imageW = event.width;
69 let imageH = event.height;
70 let windowSize = windowSizeManager.get();
71 // 图片宽高比
72 this.imageWHRatio = imageW / imageH;
73 // 图片默认大小
74 this.imageDefaultSize = this.calcImageDefaultSize(this.imageWHRatio, windowSize);
75 // 图片宽度 等于 视口宽度 则图片使用宽度适配 否则 使用 高度适配
76 if (this.imageDefaultSize.width === windowSize.width) {
77 this.imageWH = ImageWH.width;
78 } else {
79 this.imageWH = ImageWH.height;
80 }
81 /**
82 * 1.5 的基本倍数上添加 撑满全屏需要多少倍数
83 * 1.5 是初始化时候给的值
84 * 在1.5上面加是为了让图片可以放的更大
85 */
86 this.imageScaleInfo.maxScaleValue += this.imageWH === ImageWH.width ?
87 (windowSize.height / this.imageDefaultSize.height) :
88 (windowSize.width / this.imageDefaultSize.width);
89 }
90 })
91 ```
925. 通过滑动手势来处理图片位置和边界判定。详情见[PicturePreviewImage.ets](entry/src/main/ets/view/PicturePreviewImage.ets)。
93 ```typescript
94 Image(this.imageUrl)
95 .gesture(
96 GestureGroup(
97 GestureMode.Exclusive,
98 // TODO:知识点:滑动图片
99 PanGesture({ fingers: 1 })
100 .onActionUpdate((event: GestureEvent) => {
101 if (this.imageWH != ImageWH.default) {
102 this.setCrossAxis(event);
103 this.setPrincipalAxis(event);
104 }
105 })
106 .onActionEnd((event: GestureEvent) => {
107 this.imageOffsetInfo.stash();
108 this.evaluateBound();
109 })
110
111 )
112 )
113 ```
1146. 通过旋转手势来处理图片旋转方向。详情见[PicturePreviewImage.ets](entry/src/main/ets/view/PicturePreviewImage.ets)。
115 ```typescript
116 Image(this.imageUrl)
117 .gesture(
118 GestureGroup(
119 GestureMode.Exclusive,
120 // TODO:知识点:双指旋转图片
121 RotationGesture({ angle: this.imageRotateInfo.startAngle })
122 .onActionUpdate((event: GestureEvent) => {
123 let angle = this.imageRotateInfo.lastRotate + event.angle;
124 if (event.angle > 0) {
125 angle -= this.imageRotateInfo.startAngle;
126 } else {
127 angle += this.imageRotateInfo.startAngle;
128 }
129 this.matrix = matrix4.identity()
130 .scale({
131 x: this.imageScaleInfo.scaleValue,
132 y: this.imageScaleInfo.scaleValue
133 })
134 .rotate({
135 x: 0,
136 y: 0,
137 z: 1,
138 angle: angle,
139 }).copy();
140 this.imageRotateInfo.currentRotate = angle;
141 })
142 .onActionEnd((event: GestureEvent) => {
143 let rotate = simplestRotationQuarter(this.imageRotateInfo.currentRotate);
144 runWithAnimation(() => {
145 this.imageRotateInfo.currentRotate = rotate;
146 this.matrix = matrix4.identity()
147 .rotate({
148 x: 0,
149 y: 0,
150 z: 1,
151 angle: this.imageRotateInfo.currentRotate,
152 }).copy();
153 this.imageRotateInfo.stash();
154 this.imageScaleInfo.reset();
155 this.imageOffsetInfo.reset();
156 })
157 })
158
159 )
160 )
161 ```
162
163### 相关权限
164
165不涉及
166
167### 依赖
168
169不涉及
170
171### 约束与限制
172
1731. 本示例仅支持标准系统上运行。
174
1752. 本示例为Stage模型,从API version 12开始支持。SDK版本号:5.0.0.71 Release,镜像版本号:OpenHarmony 5.0.1.107。
176
1773. 本示例需要使用DevEco Studio 5.0.2 Release (Build Version: 5.0.7.200, built on January 23, 2025)编译运行。
178
179### 下载
180
181如需单独下载本工程,执行如下命令:
182
183```shell
184git init
185git config core.sparsecheckout true
186echo code/UI/ImageViewer/ > .git/info/sparse-checkout
187git remote add origin https://gitee.com/openharmony/applications_app_samples.git
188git pull origin master
189```
190
191### 参考资料
192
1931. [image](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-image.md)
194
1952. [gesture](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-gesture-settings.md)
196
1973. [list](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-container-list.md)
198
1994. [window](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-arkui/js-apis-window.md)
200
2015. [matrix4](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-arkui/js-apis-matrix4.md)