
回复
上篇已经了解了捏合手势的基础使用和回调函数。在上篇的捏合手势中,我们可以发现,图片在缩放过程中都是居中缩放的,虽然捏合中心点不一样,但是缩放效果都是一样的。这显然不符合我们手指捏合的预期。
实际图片展示中,我们希望实现,手指捏合放大或缩小的过程中,可以让缩放中心位置保持不变,这样我们就可以直接看到我们想要放大展示的地方。
看一下最终实现效果演示:
实现思路:
1.由于我们的image放在stack布局中,居中显示,因此在默认放大之后,图片依然是居中显示的,要想实现跟手缩放,我们就需要在缩放的过程中,移动我们的图片,保持开始的缩放中心点位置保持不变
2.因此我们需要记录手指开始缩放时的中心点位置,相当于开始缩放状态时宽高的百分比
3.在缩放过程中,我们根据开始缩放中心点的百分比,计算出放大之后这个点相对于图片的位置和屏幕的位置
4.将计算出新的缩放中心的位置移动到开始时缩放中心点的位置,这样就实现了跟手缩放,达到了图片的缩放中心保持不变的效果。
实现过程:
1.定义图片的偏移量offsetX、offsetY,缩放开始时保存当前偏移量currentoffsetX、currentoffsetY
2.计算开始时捏合中心点相当于图片宽高的百分比startPercentX、startPercentY
3.计算开始捏合时的中心点坐标startPinchCenterX、startPinchCenterY
4.缩放过程中,根据缩放系数,计算开始的中心点在图片缩放后的位置的坐标点newPinchCenterX、newPinchCenterY
5.根据两个坐标计算图片的偏移量offsetX、offsetY
源码
import { getScreenHeight, getScreenWidth } from '../utils/DisplayUtil';
import { WindowUtils } from '../utils/WindowUtils';
@Entry
@ComponentV2
struct PinchGestureTest{
@Local imageWidth: number = getScreenWidth() // 图片默认原始宽度 屏幕宽度
@Local imageHeight: number = getScreenHeight() // 图片默认原始高度
@Local imageTrueWidth:number = 0 // 图片真实尺寸
@Local imageTrueHeight:number = 0
@Local showPinchCenter:boolean = false
@Local imageCurrentScale: number = 1 // 图片当前缩放比例
@Local imageScale: number = 1 // 图片缩放比例
@Local gestureScale:number = 1 //手势回调 缩放比例
@Local pinchCenterX:number = 0 //手势回调 捏合手势中心点的x轴坐标 vp
@Local pinchCenterY:number = 0 //手势回调 捏合手势中心点的y轴坐标
@Local fingerList: FingerInfo[] = [] // 触屏手指触发事件的所有触点信息
@Local imageTopleftX:number = 0 //图片左上角相对于父元素的坐标
@Local imageTopleftY:number = 0
@Local currentPinchCenterX:number=0 // 每次缩放时的中心点
@Local currentPinchCenterY:number=0 // 每次缩放时的中心点
@Local startPinchCenterX:number=0 //开始缩放时的缩放中心点 坐标
@Local startPinchCenterY:number=0
@Local startPinchimageWidth:number=0 // 开始捏合时图片的初始尺寸
@Local startPinchimageHeight:number=0
@Local startPinchimageTopleftX:number = 0
@Local startPinchimageTopleftY:number = 0
@Local startPercentX:number=0 // 捏合手势开始时占宽的百分比
@Local startPercentY:number=0
@Local newPinchCenterX:number=0 // 捏合过程中 开始时的中心点在图片缩放后的新位置
@Local newPinchCenterY:number=0 //
@Local offsetX:number=0;
@Local offsetY:number=0;
@Local currentoffsetX:number=0;
@Local currentoffsetY:number=0;
build() {
Stack(){
Image($r('app.media.imagetest'))
.width(this.imageWidth*this.imageScale)
.height(this.imageHeight*this.imageScale)
.draggable(false)
.onComplete((event)=>{
this.imageTrueWidth=event?.width??0
this.imageTrueHeight=event?.height??0
// 宽度铺满
this.imageHeight = this.imageTrueHeight*this.imageWidth / this.imageTrueWidth;
})
.onAreaChange((oldValue: Area, newValue: Area)=>{
this.imageTopleftX = newValue.position.x as number
this.imageTopleftY = newValue.position.y as number
})
.offset({x:this.offsetX,y:this.offsetY})
.gesture(
PinchGesture({fingers:2})
.onActionStart((event)=>{
//保存当前缩放系数
this.imageCurrentScale = this.imageScale
//捏合手势开始时图片的尺寸
this.startPinchimageWidth = this.imageWidth*this.imageScale
this.startPinchimageHeight = this.imageHeight*this.imageScale
//保存开始时图片左上角相当于屏幕的坐标
this.startPinchimageTopleftX = this.imageTopleftX
this.startPinchimageTopleftY = this.imageTopleftY
//计算开始捏合时的中心点坐标 用于绘制演示
this.startPinchCenterX=event.pinchCenterX-(this.imageWidth*this.imageCurrentScale-getScreenWidth())/2
this.startPinchCenterY=event.pinchCenterY-(this.imageHeight*this.imageCurrentScale-getScreenHeight())/2-px2vp(WindowUtils.getStatusHeight())
//保存开始时的偏移量
this.currentoffsetX = this.offsetX
this.currentoffsetY = this.offsetY
//计算开始时 捏合中心点相当于图片宽高的百分比 用于计算缩放后当前点的位置 计算捏合偏移量
this.startPercentX = event.pinchCenterX/this.startPinchimageWidth
this.startPercentY = event.pinchCenterY/this.startPinchimageHeight
})
.onActionUpdate((event)=>{
this.showPinchCenter = true
this.gestureScale = event.scale
this.fingerList = event.fingerList
this.pinchCenterX = event.pinchCenterX
this.pinchCenterY = event.pinchCenterY
// 通过保存的初始缩放系数 计算 新的缩放系数
this.imageScale = this.imageCurrentScale*event.scale
// 缩放过程中 由于手指滑动 会改变缩放中心点的坐标 用于演示
this.currentPinchCenterX=event.pinchCenterX-(this.imageWidth*this.imageCurrentScale-getScreenWidth())/2
this.currentPinchCenterY=event.pinchCenterY-(this.imageHeight*this.imageCurrentScale-getScreenHeight())/2-px2vp(WindowUtils.getStatusHeight())
// 计算开始的中心点在图片缩放后的位置 的坐标点
this.newPinchCenterX=this.imageWidth*this.imageScale*this.startPercentX-(this.imageWidth*this.imageScale-getScreenWidth())/2
this.newPinchCenterY=this.imageHeight*this.imageScale*this.startPercentY-(this.imageHeight*this.imageScale-getScreenHeight())/2-px2vp(WindowUtils.getStatusHeight())
// 通过缩放前的缩放中心点坐标和 按照缩放比例计算的新中心点的坐标 计算偏移量
this.offsetX =this.currentoffsetX+ this.startPinchCenterX- this.newPinchCenterX
this.offsetY =this.currentoffsetY+ this.startPinchCenterY- this.newPinchCenterY
})
.onActionEnd(()=>{
this.showPinchCenter = false
if (this.imageScale<1) {
this.imageScale=1
this.offsetX=0
this.offsetY=0
}else {
//可以在这里或者缩放过程中限制缩放边界
}
})
)
Column({space:10}){
Text('图片真实尺寸px 宽:'+this.imageTrueWidth.toFixed(0)+' 高:'+this.imageTrueHeight.toFixed(0) )
Text('图片显示尺寸vp 宽:'+(this.imageWidth*this.imageScale).toFixed(0)+' 高:'+(this.imageHeight*this.imageScale).toFixed(0) )
Text('开始捏合时图片尺寸 宽:'+this.startPinchimageWidth.toFixed(0)+' 高:'+this.startPinchimageHeight.toFixed(0))
Text('开始捏合时左上角坐标 X:'+this.startPinchimageTopleftX.toFixed(0)+' 高:'+this.startPinchimageTopleftY.toFixed(0))
Text('图片左上角坐标vp X:'+this.imageTopleftX.toFixed(0)+' Y:'+this.imageTopleftY.toFixed(0) )
Text('捏合缩放比例:'+this.gestureScale)
Text('图片缩放比例:'+this.imageScale)
Text('捏合中心点距离开始时左上角vp X:'+this.pinchCenterX.toFixed(0)+' Y:'+this.pinchCenterY.toFixed(0) )
}.height('100%').width('100%').hitTestBehavior(HitTestMode.None).alignItems(HorizontalAlign.Start)
Column(){
List(){
ForEach(this.fingerList,(item:FingerInfo,index:number)=>{
ListItem(){
Column({space:5}){
Text('手指'+(index+1))
Text('相对于窗口左上角的x轴'+item.globalX.toFixed(0))
Text('相对于窗口左上角的y轴'+item.globalY.toFixed(0))
// Text('相对于当前组件元素原始区域左上角的x轴坐标'+item.localX)
// Text('相对于当前组件元素原始区域左上角的y轴坐标'+item.localY)
// Text('相对于屏幕左上角的x轴坐标'+item.displayX)
// Text('相对于屏幕左上角的y轴坐标'+item.displayY)
}.alignItems(HorizontalAlign.Start)
}
})
}.height('20%').hitTestBehavior(HitTestMode.None)
}.height('100%').width('100%').justifyContent(FlexAlign.End).hitTestBehavior(HitTestMode.None)
Circle({width:20,height:20}).fill(Color.Green)
.position({
x:this.startPinchCenterX,
y:this.startPinchCenterY})
.visibility(this.showPinchCenter?Visibility.Visible:Visibility.Hidden)
Circle({width:20,height:20}).fill(Color.Red)
.position({
x:this.currentPinchCenterX,
y:this.currentPinchCenterY})
.visibility(this.showPinchCenter?Visibility.Visible:Visibility.Hidden)
Circle({width:20,height:20}).fill(Color.Yellow)
.position({
x:this.newPinchCenterX+this.offsetX,
y:this.newPinchCenterY+this.offsetY})
.visibility(this.showPinchCenter?Visibility.Visible:Visibility.Hidden)
}.height('100%')
}
}