• Home
Name Date Size #Lines LOC

..--

AppScope/22-Oct-2025-3532

entry/22-Oct-2025-1,8611,704

hvigor/22-Oct-2025-2322

screenshots/22-Oct-2025-

.gitignoreD22-Oct-2025133 1212

README_zh.mdD22-Oct-20257.5 KiB201171

build-profile.json5D22-Oct-20251.3 KiB5756

code-linter.json5D22-Oct-20251.4 KiB4746

hvigorfile.tsD22-Oct-2025842 215

oh-package.json5D22-Oct-2025808 2524

ohosTest.mdD22-Oct-2025818 107

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)