鸿蒙6.0应用开发——图片预览器的实现
鸿蒙6.0应用开发——图片预览器的实现
文章目录
概述
图片预览器是常见的开发应用场景。在诸多日常使用的软件中,图片预览器都是提升用户体验的关键组件。它允许用户在上传、分享或编辑图片之前,先对图片进行预览,从而确保图片的质量和效果符合预期。本文章将深入探讨实现图片预览器过程中的几个复杂场景,具体包括:图片如何“跟手”,如何计算并合理限制图片的边界,以及如何解决Swiper组件与滑动手势之间产生的冲突问题。

实现原理
场景描述
基础的图片预览器功能包括如下操作:
- 双指捏合图片,即实现对图片以双指中心点为基准点的缩放操作。
- 双击图片即可切换其大小,当图片处于放大状态时,再次双击即恢复至默认尺寸。
- 大图片支持左右滑动查看。
- 点击或滑动图片指示器,主图会随之更新。
其中,缩放图片是通过矩阵变换功能matrix4来实现的,图片的平移是通过属性translate来实现的。
关键技术
图片预览器中的图片查看功能,主要由大图界面来承担,交互操作相对复杂。下面,简要梳理一下大图界面中基本手势的处理与计算方式。
“跟手”的原理
“跟手”操作细分为两大类别:平移“跟手”与缩放“跟手”。
在平移“跟手”中,无论用户的手指如何在屏幕上滑动,其触摸点相对于图片所保持的百分比位置始终保持不变。缩放“跟手”,则是在图片依据用户手势进行缩放调整时,用户手势的中心点不仅相对于屏幕上的坐标保持不变,而且相对于图片内容的百分比位置也保持不变。如下图所示,屏幕是蓝色区域,初始图片是橙色区域,放大后的图片是灰色区域。

假定当前图片位置是<lastScale, offsetX, offsetY>,控件原始宽高为<w, h>,本次缩放图片的缩放值为scale,缩放的中心点百分比位置为<centerX, centerY>,偏移为<offX, offY>,计算终点位置设为<scale’, offsetX’, offsetY’>。

如上图所示,假定缩放时,未发生偏移,蓝色看作交互开始时的控件,橙色是交互后的控件,如果缩放中心点在图片中心(图1),那么控件最终的offset没有任何变化;如果缩放中心在最左边缘(图3),在放大的过程中,整个控件的中心向右发生了偏移。由此,可以计算出图片的最终位置。其中,图2、图3中的问号代表图片的偏移量,而图中的橙色圆点是图片缩放操作的中心点。在以下计算公式中,0.5 表示图片中心点的百分比位置,即 50% = 0.5。
-
scale’ = 上次手势结束时的缩放值 * 本次缩放图片的缩放值。
= lastScale * scale
-
offsetX’ = 平移带来的偏移 + 缩放中心不在中心而带来的偏移。
= (offsetX + offX) + (0.5 - centerX) * 控件大小变化之差
= (offsetX + offX) + (0.5 - centerX) * (w * lastScale - w * lastScale * scale)
= (offsetX + offX) + (0.5 - centerX) * w * (scale - 1) * lastScale
= (offsetX + offX) + (0.5 - centerX) * w * (1 - scale) * lastScale
-
同理 offsetY’ = 平移带来的偏移 + 缩放中心不在中心而带来的偏移。
= (offsetY + offY) + (0.5 - centerY) * h * (1 - scale) * lastScale
缩放中心百分比位置<centerX , centerY>计算。如下图,橙色为手机屏幕,触摸点反馈的坐标(x,y)是相较屏幕左上角的(假设控件布满全屏)。

-
centerX = ( x - imgX ) / imgWidth
= ( 触摸点坐标x- X方向图片左上角的坐标)/ 图片的宽度
= ( 触摸点坐标x- ( ( 组件屏幕的宽度 - 当前图片的宽度) / 2 +上次图片X方向的偏移量)) / 图片的宽度
-
同理 centerY = ( y - imgY ) / imgHeight
= ( 触摸点坐标y- Y方向图片左上角的坐标 ) / 图片的高度
= ( 触摸点坐标y- ( ( 组件屏幕的高度 - 当前图片的高度) / 2 +上次图片Y方向的偏移量)) / 图片的高度
边界限制的原理
边界计算涉及两个方面:当前图片显示边界计算、offset范围计算。
- 当前图片显示边界计算可得出当前图片显示的位置,左右上下是否与显示区域边界对齐,如果已经对齐,则不能继续往某个方向继续拖动。
- 平移/缩放时必须对offset作限制,否则图片将被移出显示区域或是手势结束后周围有黑边。从原理上看,某个scale下offset(x&y方向上)的范围是固定的,所以只需在手势交互时根据当前的scale计算得出offsetRange即可(超出边界时取边界作为结果)。
假定下面两个图中显示区域为黑框区域,当前放大倍率是curScale。
如下图,当X方向offset到达上界时图片上边缘x坐标等于0:

到达下界时,图片右边缘与显示区域右边缘重合:

// Calculate the actual display size of the scaled image.
let scaledImageWidth = this.imageWidth * this.curScale;
let scaledImageHeight = this.imageHeight * this.curScale;
// Calculation of X-axis boundaries
if (scaledImageWidth > this.componentWidth) {
// If the width of the picture exceeds the width of the screen, you can move it left and right.
let maxXOffset = (scaledImageWidth - this.componentWidth) / 2;
this.maxOffsetX = maxXOffset;
this.minOffsetX = -maxXOffset;
} else {
// The width of the image does not exceed the width of the screen. It should be centered and no X-axis offset is allowed.
this.maxOffsetX = 0;
this.minOffsetX = 0;
}
- 计算缩放图像的宽度和高度:
- 通过当前缩放比例(
this.curScale)乘以原始图像宽度(this.imageWidth)和高度(this.imageHeight)来获取缩放后的图像尺寸。
- 通过当前缩放比例(
- 计算X轴边界偏移量:
- 判断缩放后的图像宽度是否大于组件宽度。
- 如果图像宽度超过组件宽度,计算最大和最小X轴偏移量(
this.maxOffsetX和this.minOffsetX),以允许图像在屏幕内左右移动。 - 如果图像宽度不超过组件宽度,将最大和最小X轴偏移量设置为0,使图像居中显示。
- 代码中涉及的变量包括:
scaledImageWidth和scaledImageHeight:存储缩放后的图像宽度和高度。maxXOffset:存储图像宽度超过组件宽度时的最大X轴偏移量。this.maxOffsetX和this.minOffsetX:存储X轴的边界偏移量。
Y方向同理,上下界为:
// Calculate the actual display size of the scaled image.
let scaledImageWidth = this.imageWidth * this.curScale;
let scaledImageHeight = this.imageHeight * this.curScale;
// ...
// Y-axis boundary calculation
if (scaledImageHeight > this.componentHeight) {
// If the image height exceeds the screen height, it can be scrolled up and down.
let maxYOffset = (scaledImageHeight - this.componentHeight) / 2;
this.maxOffsetY = maxYOffset;
this.minOffsetY = -maxYOffset;
} else {
// The height of the image does not exceed the height of the screen. It should be centered and no Y-axis offset is allowed.
this.maxOffsetY = 0;
this.minOffsetY = 0;
}
- 计算缩放图像的宽度和高度:
- 通过当前缩放比例(
this.curScale)乘以原始图像宽度(this.imageWidth)和高度(this.imageHeight)来得到缩放后的图像尺寸。
- 通过当前缩放比例(
- 计算Y轴边界偏移量:
- 检查缩放后的图像高度是否超过组件高度(
this.componentHeight)。 - 如果超过,计算最大和最小Y轴偏移量(
this.maxOffsetY和this.minOffsetY),以允许图像上下滚动。 - 如果未超过,将最大和最小Y轴偏移量设置为0,使图像垂直居中。
- 检查缩放后的图像高度是否超过组件高度(
this.componentHeight)。
- 检查缩放后的图像高度是否超过组件高度(
- 代码中涉及的变量和逻辑确保了图像在不同尺寸下的显示效果,提供了良好的用户体验。
更多推荐



所有评论(0)