在前两篇文章中,我们介绍了图片预览组件的单指拖动、双指缩放和双指旋转手势实现。本文将继续介绍双击缩放手势的实现细节,以及手势之间的协同工作机制。
TapGesture({ count: 2 })
.onAction(() => {
let fn: Function;
// 当前大小倍数 大于 默认的倍数,则是放大状态需要缩小
if (this.imageScaleInfo.scaleValue > this.imageScaleInfo.defaultScaleValue) {
fn = () => {
// 恢复默认大小
this.imageScaleInfo.reset();
// 重置偏移量
this.imageOffsetInfo.reset();
// 设置一个新的矩阵
this.matrix = matrix4.identity().copy().rotate({
z: 1,
angle: this.imageRotateInfo.lastRotate
});
}
} else {
fn = () => {
// 这里是正常状态 -- 需要放大
// 获取放大倍数
const ratio: number = this.calcFitScaleRatio(this.imageDefaultSize, windowSizeManager.get());
// 设置当前放大倍数
this.imageScaleInfo.scaleValue = ratio;
// 重置偏移量
this.imageOffsetInfo.reset();
// 设置矩阵元素
this.matrix = matrix4.identity().scale({
x: ratio,
y: ratio,
}).rotate({
z: 1,
angle: this.imageRotateInfo.lastRotate
}).copy();
// 设置最后放大倍数设置为当前的倍数
this.imageScaleInfo.stash();
}
}
runWithAnimation(fn);
})
双击缩放手势的核心逻辑包括:
calcFitScaleRatio(imageSize: image.Size, windowSize: window.Size): number {
let ratio: number = 1.0;
if (windowSize.width > imageSize.width) {
ratio = windowSize.width / imageSize.width;
} else {
ratio = windowSize.height / imageSize.height;
}
return ratio;
}
这个方法计算图片适配屏幕的缩放比例,确保图片能够填满屏幕。计算逻辑如下:
图片预览组件使用GestureGroup将多种手势组合在一起,实现复杂的交互效果:
.gesture(
GestureGroup(
GestureMode.Parallel,
// 双击切换图片大小
TapGesture({ count: 2 }),
// 拖动图片
PanGesture({ fingers: 1 })
)
)
// 两根手指操作
.gesture(
GestureGroup(
GestureMode.Parallel,
// 双指旋转图片
RotationGesture({ angle: this.imageRotateInfo.startAngle }),
// 双指缩放图片
PinchGesture({ fingers: 2, distance: 1 })
)
)
组件将手势分为两组:
通过GestureMode.Parallel模式,允许同一组内的手势并行识别和处理,提供更自然的交互体验。
不同手势之间通过共享数据模型实现状态协同:
// 图片旋转信息
@State imageRotateInfo: RotateModel = new RotateModel();
// 图片缩放信息
@State imageScaleInfo: ScaleModel = new ScaleModel(1.0, 1.0, 1.5, 0.3);
// 图片偏移信息
@State imageOffsetInfo: OffsetModel = new OffsetModel(0, 0);
这些状态模型在不同手势之间共享,确保手势操作的一致性和连贯性:
所有手势操作最终都通过matrix4矩阵变换应用到图片上:
// 本模块提供矩阵变换功能,可对图形进行平移、旋转和缩放等
@State matrix: matrix4.Matrix4Transit = matrix4.identity().copy();
// 应用矩阵变换
Image(this.imageUrl)
.transform(this.matrix)
.offset({
x: this.imageOffsetInfo.currentX,
y: this.imageOffsetInfo.currentY
})
矩阵变换和偏移量的组合使用,实现了图片的复合变换效果:
// 当小于默认大小时,恢复为默认大小
if (this.imageScaleInfo.scaleValue < this.imageScaleInfo.defaultScaleValue) {
runWithAnimation(() => {
this.imageScaleInfo.reset();
this.imageOffsetInfo.reset();
this.matrix = matrix4.identity().rotate({
x: 0,
y: 0,
z: 1,
angle: this.imageRotateInfo.currentRotate,
}).copy();
})
}
// 当大于最大缩放因子时,恢复到最大
if (this.imageScaleInfo.scaleValue > this.imageScaleInfo.maxScaleValue) {
runWithAnimation(() => {
this.imageScaleInfo.scaleValue = this.imageScaleInfo.maxScaleValue;
this.matrix = matrix4.identity()
.scale({
x: this.imageScaleInfo.maxScaleValue,
y: this.imageScaleInfo.maxScaleValue
}).rotate({
x: 0,
y: 0,
z: 1,
angle: this.imageRotateInfo.currentRotate,
});
})
}
缩放边界处理确保图片的缩放值在合理范围内,并在超出范围时平滑恢复。
let rotate = simplestRotationQuarter(this.imageRotateInfo.currentRotate);
runWithAnimation(() => {
this.imageRotateInfo.currentRotate = rotate;
// 应用旋转变换
})
旋转边界处理将旋转角度对齐到最接近的90度倍数,提供更整齐的视觉效果。
evaluateBound(): void {
const xBol = constrainOffsetAndAnimation({
dimensionWH: ImageFitType.TYPE_WIDTH,
// 其他参数...
});
const yBol = constrainOffsetAndAnimation({
dimensionWH: ImageFitType.TYPE_HEIGHT,
// 其他参数...
});
// 处理边界情况...
}
拖动边界处理确保图片不会被拖出视口,并在达到边界时触发图片切换。
图片预览组件的手势处理实现了丰富的交互功能,包括单指拖动、双指缩放、双指旋转和双击缩放等。通过合理的手势组合和状态协同,提供了流畅自然的用户体验。
手势处理的核心技术包括:
这些技术的综合应用,使图片预览组件能够提供接近原生应用的交互体验,满足用户对图片预览的各种需求。