
鸿蒙Next图片预览缩放拖动一镜到底实现 原创 精华
实现目标:
一组图片点击其中一张可以跳转到全屏预览,并且使用一镜到底转场动画,预览界面可以左右滑动查看其他图片,单张图片可以放大查看,向下拖动退出预览。
实现原理:
1.一镜到底
设置geometryTransition属性将图片首页和大图页面的图片绑定同一id值,结合属性动画效果实现一镜到底效果
组件内转场主要通过transition属性配置转场参数,在组件插入和删除时显示过渡动效。通过设置opacity,设置组件转场时的透明度效果,为插入时起点和删除时终点的值。
2.拖动手势PanGesture
滑动达到最小滑动距离(默认值为5vp)时拖动手势识别成功,三个可选参数:
fingers:用于声明触发拖动手势所需要的最少手指数量,最小值为1,最大值为10,默认值为1。
direction:用于声明触发拖动的手势方向,此枚举值支持逻辑与(&)和逻辑或(|)运算。默认值为Pandirection.All。
distance:用于声明触发拖动的最小拖动识别距离,单位为vp,默认值为5。
三个可选事件:
onActionStart:Pan手势识别成功回调
onActionUpdate:Pan手势移动过程中回调,fingerList为多根手指时,该回调监听每次只会更新一根手指的位置信息
onActionEnd:Pan手势识别成功,手指抬起后触发回调
3.捏合手势PinchGesture
触发捏合手势的最少手指为2指,最大为5指,最小识别距离为5vp。两个可选参数:
fingers:用于声明触发捏合手势所需要的最少手指数量,最小值为2,最大值为5,默认值为2。
distance:用于声明触发捏合手势的最小距离,单位为vp,默认值为5
可选事件同上。
实现过程:
不想看过程的同学可以直接看最后的全部源码,辛苦总结,免费取用,麻烦转发分享点赞,感谢
1.实现简单的一镜到底转场
/// 图片九宫格页面
export const imageList: (string | Resource)[] = [
$r('app.media.img1'),
$r('app.media.img2'),
$r('app.media.img3'),
$r('app.media.img4'),
$r('app.media.img5'),
$r('app.media.img6'),
$r('app.media.img7'),
$r('app.media.img8'),
$r('app.media.img9'),
];
@Builder
export function ImagePreviewTestBuilder() {
ImagePreviewTest()
}
@ComponentV2
struct ImagePreviewTest{
pathStack: NavPathStack = new NavPathStack()
onItemClick(index: number): void {
let param: Record<string, Object> = {};
param['selectedIndex'] = index;
this.getUIContext().animateTo({
duration: 250,
curve: Curve.EaseIn,
}, () => {
this.pathStack.pushPath({
name: 'ImagePreviewPage', param: param
},false)
})
}
build() {
NavDestination(){
Grid() {
ForEach(imageList, (item: string | Resource, index: number) => {
GridItem() {
Image(item)
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover)
.id('item' + index)
.borderRadius(5)
.onClick(() => {
this.onItemClick(index);
})
.geometryTransition('app.media.img' + (index+1))
.transition(TransitionEffect.opacity(0.99))
}
.width(80)
.height(80)
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr')
.width('100%')
.height('auto')
}
.hideTitleBar(true)
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
/// 图片预览详情页
import { imageList } from "../view/ImagePreviewTest";
import { display } from "@kit.ArkUI"
@Builder
export function ImagePreviewPageBuilder() {
ImagePreviewPage()
}
@ComponentV2
struct ImagePreviewPage{
private pathStack: NavPathStack = new NavPathStack();
@Local selectedIndex: number = 0;
@Local imageWidth: number = display.getDefaultDisplaySync().width;
@Local imageHeight: number = display.getDefaultDisplaySync().width;
private backToFirstPage: () => void = () => {
this.getUIContext().animateTo({
duration: 300,
curve: Curve.EaseIn,
}, () => {
this.pathStack.pop(false);
})
}
build() {
NavDestination(){
Swiper() {
ForEach(imageList, (item: string | Resource, index: number) => {
Stack(){
Column()
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
.transition(TransitionEffect.OPACITY)
Image(item)
.width("100%")
.height("100%")
.onComplete((event)=>{
let imageWidth = event?.width ??0
let imageHeight = event?.height ??0
this.imageHeight =imageHeight*this.imageWidth / imageWidth
})
.objectFit(ImageFit.Contain)
.geometryTransition('app.media.img' + (index+1))
.transition(TransitionEffect.OPACITY)
}
.width('100%')
.height('100%')
}, (item: string) => item)
}
.transition(TransitionEffect.opacity(0.99))
.width('100%')
.height('100%')
.clip(false)
.index(this.selectedIndex)
.onChange((index: number) => {
this.selectedIndex = index;
})
.indicator(false)
}
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
let param = context.pathInfo?.param as Record<string, Object>;
if (param === undefined) {
return;
}
this.selectedIndex = param['selectedIndex'] as number;
})
.width('100%')
.height('100%')
.mode(NavDestinationMode.DIALOG)
.hideTitleBar(true)
.expandSafeArea([SafeAreaType.SYSTEM])
.onBackPressed(() => {
this.backToFirstPage();
return true;
})
.transition(TransitionEffect.opacity(0.99))
}
}
2.增加拖动手势,改变图片的位置
PanGesture({ direction: this.panDirection })
.onActionStart((event: GestureEvent) => {
//初始识别的触发方向是垂直方向,识别成功后改为所有方向可拖动
this.panDirection = PanDirection.All;
this.startGestureOffsetX = px2vp(this.imageTranslateX);
this.startGestureOffsetY = px2vp(this.imageTranslateY);
console.log('---PanGesture--onActionStart() startGestureOffsetX:'+this.startGestureOffsetX+'startGestureOffsetY:'+this.startGestureOffsetY)
})
.onActionUpdate((event: GestureEvent) => {
let offsetX = event.offsetX;
let offsetY = event.offsetY;
// 拖拽状态下,图片跟随手指移动.
this.imageTranslateX = vp2px(this.startGestureOffsetX + offsetX);
this.imageTranslateY = vp2px(this.startGestureOffsetY + offsetY);
//根据垂直方向的拖动距离偏移量计算背景的透明度
this.imagePullingDownScale = 1 - Math.abs(event.offsetY) / this.getUIContext().px2vp(display.getDefaultDisplaySync().height);
})
.onActionEnd((event: GestureEvent) => {
//手势结束 恢复,后续需要增加判断 垂直方向滑动一定距离退出当前页面
this.panDirection = PanDirection.Vertical;
this.imagePullingDownScale = 1;
this.imageTranslateX = 0;
this.imageTranslateY = 0;
})
))
3.增加捏合手势,放大缩小
PinchGesture()
.onActionStart((event: GestureEvent) => {
this.startGestureScale = this.imageScale;
})
.onActionUpdate((event: GestureEvent) => {
this.imageScale = this.startGestureScale * event.scale;
})
.onActionEnd(() => {
if (this.imageScale < 1) {
// 如果捏合缩小 默认恢复到初始状态
this.getUIContext().animateTo({ duration: 250, curve: Curve.EaseOut }, () => {
this.imageScale = 1;
})
}
})
4.双击手势恢复,单击手势退出
//双击手势
TapGesture({ count: 2})
.onAction((event: GestureEvent) => {
if (this.imageScale > 1) {
this.imageScale = 1
}
this.onAllGestureFinish()
}),
//单击手势
TapGesture({ count: 1 })
.onAction(() => {
if ((this.imageScale == 1)) {
this.backToFirstPage()
}
})
源码
九宫格页
export const imageList: (string | Resource)[] = [
$r('app.media.img1'),
$r('app.media.img2'),
$r('app.media.img3'),
$r('app.media.img4'),
$r('app.media.img5'),
$r('app.media.img6'),
$r('app.media.img7'),
$r('app.media.img8'),
$r('app.media.img9'),
];
@Builder
export function ImagePreviewTestBuilder() {
ImagePreviewTest()
}
@ComponentV2
struct ImagePreviewTest{
pathStack: NavPathStack = new NavPathStack()
onItemClick(index: number): void {
let param: Record<string, Object> = {};
param['selectedIndex'] = index;
this.getUIContext().animateTo({
duration: 250,
curve: Curve.EaseIn,
}, () => {
this.pathStack.pushPath({
name: 'ImagePreviewPage', param: param
},false)
})
}
build() {
NavDestination(){
Grid() {
ForEach(imageList, (item: string | Resource, index: number) => {
GridItem() {
Image(item)
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover)
.borderRadius(5)
.onClick(() => {
this.onItemClick(index);
})
.geometryTransition('app.media.img' + (index+1))
.transition(TransitionEffect.opacity(0.99))
}
.width(90)
.height(90)
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr')
.width('100%')
.height('100%')
}
.hideTitleBar(true)
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
详情页
import { imageList } from "../view/ImagePreviewTest";
import { display } from "@kit.ArkUI";
import { PhotoPreviewItem } from "library_common";
@Builder
export function ImagePreviewPageBuilder() {
ImagePreviewPage()
}
@ComponentV2
struct ImagePreviewPage{
private pathStack: NavPathStack = new NavPathStack();
@Local selectedIndex: number = 0;
@Local imageWidth: number = display.getDefaultDisplaySync().width;
@Local imageHeight: number = display.getDefaultDisplaySync().width;
private backToFirstPage: () => void = () => {
this.getUIContext().animateTo({
duration: 300,
curve: Curve.EaseIn,
}, () => {
this.pathStack.pop(false);
})
}
build() {
NavDestination(){
Swiper() {
ForEach(imageList, (item: string | Resource, index: number) => {
PhotoPreviewItem({
imageUrl: 'app.media.img' + (index+1),
backToFirstPage: this.backToFirstPage
})
}, (item: string) => item)
}
.transition(TransitionEffect.opacity(0.99))
.width('100%')
.height('100%')
.clip(false)
.index(this.selectedIndex)
.onChange((index: number) => {
this.selectedIndex = index;
})
.indicator(false)
}
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
let param = context.pathInfo?.param as Record<string, Object>;
if (param === undefined) {
return;
}
this.selectedIndex = param['selectedIndex'] as number;
})
.width('100%')
.height('100%')
.mode(NavDestinationMode.DIALOG)
.hideTitleBar(true)
.expandSafeArea([SafeAreaType.SYSTEM])
.onBackPressed(() => {
this.backToFirstPage();
return true;
})
.transition(TransitionEffect.opacity(0.99))
}
}
自定义图片组件
import { display, matrix4 } from "@kit.ArkUI";
enum Status {
IDLE, //初始
PINCHING, //捏合
PAN_ONLY, //拖动
READY_TO_BACK // 返回
}
@ComponentV2
export struct PhotoPreviewItem {
@Local status: Status = Status.IDLE;
@Local gestureCount: number = 0;
@BuilderParam imageUrl: string;
@Local imageScale: number = 1; // 缩放手势 改变图片缩放比例
@Local imagePullingDownScale: number = 1; // 拖拽手势 改变缩放比例
@Local imageTranslateX: number = 0; // 拖拽手势偏移 距离
@Local imageTranslateY: number = 0; // 拖拽手势偏移 距离
@Local geometryPositionX: number = 0;
@Local geometryPositionY: number = 0;
@Local centerX: number = 0;
@Local centerY: number = 0;
@Local imageWidth: number = display.getDefaultDisplaySync().width;
@Local imageHeight: number = display.getDefaultDisplaySync().height;
@Local panDirection: PanDirection = PanDirection.Vertical; //滑动手势 触发方向
@Local gestureDisabled: boolean = false; //是否想要手势
//图片拖动偏移量
@Local maxOffsetX: number = 0;
@Local minOffsetX: number = 0;
@Local maxOffsetY: number = 0;
@Local minOffsetY: number = 0;
//实际显示边界由图像边界和显示边界共同控制。
private realDisplayBoundsLeft: number = 0;
private realDisplayBoundsTop: number = 0;
private realDisplayBoundsRight: number = 0;
private realDisplayBoundsBottom: number = 0;
// 记录手势的初始状态。偏移量的单位是(vp)
private startGestureOffsetX: number = 0;
private startGestureOffsetY: number = 0;
private startGestureScale: number = 1;
private imagePositionX: number = 0; // 图像变换前的坐标
private imagePositionY: number = 0; // 图像变换前的坐标
//当前组件左上角的边界坐标 屏幕的左上角和右下角
private displayLeft: number = 0;
private displayTop: number = 0;
private displayRight: number = display.getDefaultDisplaySync().width
private displayBottom: number = display.getDefaultDisplaySync().height
private lastCenterX: number = 0;
private lastCenterY: number = 0;
private geometryScale: number = 1;
private firstStarted = true;
@BuilderParam backToFirstPage: () => void
@Monitor('gestureCount')
onAllGestureFinish(): void {
if (this.firstStarted) {
this.firstStarted = false;
return;
}
if (this.gestureCount !== 0) {
return;
}
if (this.status === Status.IDLE) {
this.disableGesture();
this.getUIContext().animateTo({
duration: 300,
onFinish: () => {
this.updatePanDirection();
this.resumeGesture();
}
}, () => {
this.imagePullingDownScale = 1;
let leftTop = this.calculateLeftTopPoint();
let rightBottom = this.calculateRightBottomPoint();
let imageWidth = this.imageWidth * this.imageScale;
if (imageWidth < this.displayRight - this.displayLeft) {
// 如果图片宽度小于显示范围的宽度,则居中显示
this.centerX = 0;
this.imageTranslateX = 0;
} else if (leftTop[0] > this.displayLeft) {
this.imageTranslateX += (this.displayLeft - leftTop[0]);
} else if (rightBottom[0] < this.displayRight) {
this.imageTranslateX += (this.displayRight - rightBottom[0]);
}
let imageHeight = this.imageHeight * this.imageScale;
if (imageHeight < this.displayBottom - this.displayTop) {
this.centerY = 0;
this.imageTranslateY = 0;
} else if (leftTop[1] > this.displayTop) {
this.imageTranslateY += (this.displayTop - leftTop[1]);
} else if (rightBottom[1] < this.displayBottom) {
this.imageTranslateY += (this.displayBottom - rightBottom[1]);
}
})
}
if (this.status === Status.READY_TO_BACK) {
this.status = Status.IDLE;
this.disableGesture();
this.getUIContext().animateTo({
duration: 300,
onFinish: () => {
this.resumeGesture();
this.updatePanDirection();
}
}, () => {
let leftTopPoint = this.calculateLeftTopPoint();
this.geometryPositionX = leftTopPoint[0] - this.imagePositionX;
this.geometryPositionY = leftTopPoint[1] - this.imagePositionY;
this.geometryScale = this.imageScale * this.imagePullingDownScale;
this.resetTransform();
this.backToFirstPage();
})
}
}
//根据中心的变化,调整平移,以确保图像的位置在变换后保持不变
updateTranslateAccordingToCenter() {
if (this.lastCenterX === this.centerX && this.lastCenterY === this.centerY) {
return;
}
let lastTranslateX = this.imageTranslateX;
let lastTranslateY = this.imageTranslateY;
let matrixOld = matrix4.identity()
.scale({
x: this.imageScale * this.imagePullingDownScale,
y: this.imageScale * this.imagePullingDownScale,
centerX: this.lastCenterX,
centerY: this.lastCenterY
})
.translate({ x: lastTranslateX, y: lastTranslateY })
let matrixNew = matrix4.identity()
.scale({
x: this.imageScale * this.imagePullingDownScale,
y: this.imageScale * this.imagePullingDownScale,
centerX: this.centerX,
centerY: this.centerY
})
.translate({
x: this.imageTranslateX,
y: this.imageTranslateY
})
let leftTopOld = matrixOld.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
let leftTopNew = matrixNew.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
this.imageTranslateX += (leftTopOld[0] - leftTopNew[0]);
this.imageTranslateY += (leftTopOld[1] - leftTopNew[1]);
}
updateExtremeOffset() {
let totalScale = this.imageScale * this.imagePullingDownScale;
let matrix = matrix4.identity()
.scale({
x: totalScale,
y: totalScale,
centerX: this.centerX,
centerY: this.centerY
})
let p = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
let leftTopPointX = p[0] + this.imageWidth / 2 + this.imagePositionX;
let leftTopPointY = p[1] + this.imageHeight / 2 + this.imagePositionY;
let rightBottomPointX = leftTopPointX + this.imageWidth * totalScale;
let rightBottomPointY = leftTopPointY + this.imageHeight * totalScale;
console.log('---leftTopPointX:'+leftTopPointX+' leftTopPointY: '+leftTopPointY)
this.realDisplayBoundsLeft = Math.max(this.displayLeft, leftTopPointX);
this.realDisplayBoundsRight = Math.min(this.displayRight, rightBottomPointX);
this.realDisplayBoundsTop = Math.max(this.displayTop, leftTopPointY);
this.realDisplayBoundsBottom = Math.min(this.displayBottom, rightBottomPointY);
console.log('---realDisplayBoundsLeft:'+this.realDisplayBoundsLeft+' realDisplayBoundsRight: '+this.realDisplayBoundsRight)
this.minOffsetX = px2vp(this.realDisplayBoundsRight - rightBottomPointX);
this.maxOffsetX = px2vp(this.realDisplayBoundsLeft - leftTopPointX);
this.minOffsetY = px2vp(this.realDisplayBoundsBottom - rightBottomPointY);
this.maxOffsetY = px2vp(this.realDisplayBoundsTop - leftTopPointY);
console.log('---minOffsetX:'+this.minOffsetX+' maxOffsetX:'+this.maxOffsetX)
console.log('---minOffsetY:'+this.minOffsetY+' maxOffsetY:'+this.maxOffsetY)
}
//重置滑动手势触发方向
updatePanDirection(): void {
this.panDirection = PanDirection.Vertical;
let leftTop = this.calculateLeftTopPoint();
let rightBottom = this.calculateRightBottomPoint();
//根据顶点左边 设置滑动方向
if (leftTop[0] < this.displayLeft - 1) {
this.panDirection = this.panDirection | PanDirection.Right;
}
if (rightBottom[0] > this.displayRight + 1) {
this.panDirection = this.panDirection | PanDirection.Left;
}
}
//滑动结束后 图片恢复到原来的位置和缩放比例
resetTransform(): void {
this.imageTranslateX = 0;
this.imageTranslateY = 0;
this.imageScale = 1;
this.imagePullingDownScale = 1;
this.centerX = 0;
this.centerY = 0;
this.updateExtremeOffset();
}
//左上角的坐标
updateCenter(gestureCenterPoint: [number, number]) {
this.lastCenterX = this.centerX;
this.lastCenterY = this.centerY;
let matrix = matrix4.identity()
.scale({
x: this.imageScale * this.imagePullingDownScale,
y: this.imageScale * this.imagePullingDownScale,
centerX: this.lastCenterX,
centerY: this.lastCenterY
})
.translate({
x: this.imageTranslateX,
y: this.imageTranslateY
})
let leftTop = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]); // px
let leftTopPointX = leftTop[0] + this.imageWidth / 2; // px
let leftTopPointY = leftTop[1] + this.imageHeight / 2;
this.centerX = (gestureCenterPoint[0] - leftTopPointX) / this.imageScale - this.imageWidth / 2;
this.centerY = (gestureCenterPoint[1] - leftTopPointY) / this.imageScale - this.imageHeight / 2;
}
//计算变换后图像左上角的坐标
calculateLeftTopPoint(): [number, number] {
let matrix = matrix4.identity()
.scale({
x: this.imageScale * this.imagePullingDownScale,
y: this.imageScale * this.imagePullingDownScale,
centerX: this.centerX,
centerY: this.centerY
})
.translate({ x: this.imageTranslateX, y: this.imageTranslateY })
let leftTop = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
let leftTopPointX = leftTop[0] + this.imageWidth / 2 + this.imagePositionX;
let leftTopPointY = leftTop[1] + this.imageHeight / 2 + this.imagePositionY;
console.log('calculateLeftTopPoint() leftTopPointX:'+leftTopPointX+' leftTopPointY:'+leftTopPointY)
return [leftTopPointX, leftTopPointY];
}
//计算图像变换后右下角的坐标
calculateRightBottomPoint(): [number, number] {
let matrix = matrix4.identity()
.scale({
x: this.imageScale * this.imagePullingDownScale,
y: this.imageScale * this.imagePullingDownScale,
centerX: this.centerX,
centerY: this.centerY
})
.translate({
x: this.imageTranslateX,
y: this.imageTranslateY
})
let rightBottom = matrix.transformPoint([this.imageWidth / 2, this.imageHeight / 2]);
let rightBottomPointX = rightBottom[0] + this.imageWidth / 2 + this.imagePositionX;
let rightBottomPointY = rightBottom[1] + this.imageHeight / 2 + this.imagePositionY;
return [rightBottomPointX, rightBottomPointY];
}
disableGesture(): void {
this.gestureDisabled = true;
}
resumeGesture(): void {
this.gestureDisabled = false;
}
cannotPan(): boolean {
return this.gestureDisabled;
}
cannotPinch(): boolean {
return this.status === Status.PAN_ONLY || this.gestureDisabled;
}
aboutToAppear(): void {
this.updateExtremeOffset();
this.updatePanDirection();
}
build() {
Stack() {
Column()
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
.transition(TransitionEffect.OPACITY)
.opacity(this.imagePullingDownScale)
Stack() {
Image($r(this.imageUrl))
.width(px2vp(this.imageWidth) * this.geometryScale)
.height(px2vp(this.imageHeight) * this.geometryScale)
.onComplete((event) => {
let imageWidth = event?.width ?? 0
let imageHeight = event?.height ?? 0
this.imageHeight = imageHeight * this.imageWidth / imageWidth
})
.transform(matrix4.identity()
.scale({
x: this.imageScale * this.imagePullingDownScale,
y: this.imageScale * this.imagePullingDownScale,
centerX: this.centerX,
centerY: this.centerY
})
.translate({
x: this.imageTranslateX,
y: this.imageTranslateY
}))
.geometryTransition(this.imageUrl)
.transition(TransitionEffect.OPACITY)
.position({
x: px2vp(this.geometryPositionX),
y: px2vp(this.geometryPositionY)
})
}
.id(this.imageUrl)
.width(px2vp(this.imageWidth))
.height(px2vp(this.imageHeight))
.parallelGesture(GestureGroup(GestureMode.Exclusive,
PinchGesture()
.onActionStart((event: GestureEvent) => {
this.status = Status.PINCHING;
this.updateCenter([vp2px(event.pinchCenterX), vp2px(event.pinchCenterY)]);
//根据中心的变化,调整平移量,以确保变换后图像的位置保持不变
this.updateTranslateAccordingToCenter();
this.startGestureScale = this.imageScale;
this.gestureCount++;
})
.onActionUpdate((event: GestureEvent) => {
this.imageScale = this.startGestureScale * event.scale;
this.updateExtremeOffset();
})
.onActionEnd(() => {
this.status = Status.IDLE;
if (this.imageScale < 1) {
// 如果捏合缩小 默认恢复到初始状态
this.getUIContext().animateTo({ duration: 250, curve: Curve.EaseOut }, () => {
this.imageScale = 1;
})
}
this.gestureCount--;
}),
PanGesture({ direction: this.panDirection })
.onActionStart((event: GestureEvent) => {
//初始识别的触发方向是垂直方向,识别成功后改为所有方向可拖动
this.panDirection = PanDirection.All;
let leftTop = this.calculateLeftTopPoint();
// 图片的没有显示超过屏幕 不是放大状态
if (leftTop[1] >= this.displayTop - 1 && this.status !== Status.PINCHING) {
this.status = Status.PAN_ONLY;
}
if (this.status !== Status.PINCHING) {
let currentX: number = event.offsetX;
let currentY: number = event.offsetY;
if (event.fingerList[0] !== undefined) {
currentX = event.fingerList[0].globalX;
currentY = event.fingerList[0].globalY;
}
this.updateCenter([vp2px(currentX- this.imagePositionX), vp2px(currentY-this.imagePositionY) ]);
this.updateTranslateAccordingToCenter();
this.updateExtremeOffset();
}
this.startGestureOffsetX = px2vp(this.imageTranslateX);
this.startGestureOffsetY = px2vp(this.imageTranslateY);
console.log('---PanGesture--onActionStart() startGestureOffsetX:'+this.startGestureOffsetX+'startGestureOffsetY:'+this.startGestureOffsetY)
this.gestureCount++;
})
.onActionUpdate((event: GestureEvent) => {
let offsetX = event.offsetX;
let offsetY = event.offsetY;
// 拖拽状态下,图片跟随手指移动.
if (this.status === Status.PAN_ONLY) {
this.imageTranslateX = vp2px(this.startGestureOffsetX + offsetX);
this.imageTranslateY = vp2px(this.startGestureOffsetY + offsetY);
//根据垂直方向的拖动距离偏移量计算背景的透明度
this.imagePullingDownScale = 1 - Math.abs(event.offsetY) / px2vp(display.getDefaultDisplaySync().height);
}
if (this.status !== Status.PAN_ONLY) {
if (offsetX + this.startGestureOffsetX > this.maxOffsetX) {
let distance = offsetX + this.startGestureOffsetX - this.maxOffsetX;
offsetX = this.maxOffsetX + distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetX;
}
if (offsetX + this.startGestureOffsetX < this.minOffsetX) {
let distance = this.minOffsetX - (offsetX + this.startGestureOffsetX)
offsetX = this.minOffsetX - distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetX;
}
if (offsetY + this.startGestureOffsetY > this.maxOffsetY) {
let distance = offsetY + this.startGestureOffsetY - this.maxOffsetY;
offsetY = this.maxOffsetY + distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetY;
}
if (offsetY + this.startGestureOffsetY < this.minOffsetY) {
let distance = this.minOffsetY - (offsetY + this.startGestureOffsetY)
offsetY = this.minOffsetY - distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetY;
}
this.imageTranslateX = vp2px(this.startGestureOffsetX + offsetX);
this.imageTranslateY = vp2px(this.startGestureOffsetY + offsetY);
}
})
.onActionEnd((event: GestureEvent) => {
//手势结束 恢复,垂直方向滑动一定距离退出当前页面
if (this.status === Status.PAN_ONLY) {
if (event.offsetY > 100) {
this.status = Status.READY_TO_BACK;
} else {
this.status = Status.IDLE;
}
}
this.gestureCount--;
}),
//双击手势
TapGesture({ count: 2})
.onAction((event: GestureEvent) => {
if (this.imageScale > 1) {
this.imageScale = 1
}
this.onAllGestureFinish()
}),
//单击手势
TapGesture({ count: 1 })
.onAction(() => {
if ((this.imageScale == 1)) {
this.backToFirstPage()
}
}),
))
.onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => {
if (gestureInfo.type === GestureControl.GestureType.PAN_GESTURE) {
return this.cannotPan() ? GestureJudgeResult.REJECT : GestureJudgeResult.CONTINUE;
}
if (gestureInfo.type === GestureControl.GestureType.PINCH_GESTURE) {
return this.cannotPinch() ? GestureJudgeResult.REJECT : GestureJudgeResult.CONTINUE;
}
return GestureJudgeResult.CONTINUE;
})
}
.width('100%')
.height('100%')
}
}
