鸿蒙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 * scaleoffsetX’ 平移带来的偏移 缩放中心不在中心而带来的偏移。 (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下offsetxy方向上的范围是固定的所以只需在手势交互时根据当前的scale计算得出offsetRange即可超出边界时取边界作为结果。假定下面两个图中显示区域为黑框区域当前放大倍率是curScale。如下图当X方向offset到达上界时图片上边缘x坐标等于0到达下界时图片右边缘与显示区域右边缘重合// Calculate the actual display size of the scaled image.letscaledImageWidththis.imageWidth*this.curScale;letscaledImageHeightthis.imageHeight*this.curScale;// Calculation of X-axis boundariesif(scaledImageWidththis.componentWidth){// If the width of the picture exceeds the width of the screen, you can move it left and right.letmaxXOffset(scaledImageWidth-this.componentWidth)/2;this.maxOffsetXmaxXOffset;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.maxOffsetX0;this.minOffsetX0;}计算缩放图像的宽度和高度通过当前缩放比例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.letscaledImageWidththis.imageWidth*this.curScale;letscaledImageHeightthis.imageHeight*this.curScale;// ...// Y-axis boundary calculationif(scaledImageHeightthis.componentHeight){// If the image height exceeds the screen height, it can be scrolled up and down.letmaxYOffset(scaledImageHeight-this.componentHeight)/2;this.maxOffsetYmaxYOffset;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.maxOffsetY0;this.minOffsetY0;}计算缩放图像的宽度和高度通过当前缩放比例this.curScale乘以原始图像宽度this.imageWidth和高度this.imageHeight来得到缩放后的图像尺寸。计算Y轴边界偏移量检查缩放后的图像高度是否超过组件高度this.componentHeight。如果超过计算最大和最小Y轴偏移量this.maxOffsetY和this.minOffsetY以允许图像上下滚动。如果未超过将最大和最小Y轴偏移量设置为0使图像垂直居中。检查缩放后的图像高度是否超过组件高度this.componentHeight。代码中涉及的变量和逻辑确保了图像在不同尺寸下的显示效果提供了良好的用户体验。