鸿蒙Next手势详解系列:旋转手势 原创

auhgnixgnahz
发布于 2025-10-20 09:29
浏览
0收藏

这篇文章介绍一下旋转手势(RotationGesture)的触发条件和回调信息。还有控制组件旋转的rotate介绍。
旋转手势处理器配置参数:RotationGestureHandlerOptions

名称 说明
fingers 触发捏合的最少手指数[2, 5]
angle 触发旋转手势的最小改变度数
isFingerCountLimited 是否检查触摸屏幕的手指数量

GestureEvent对象在RotationGesture中的回调属性:

名称 说明
angle 旋转角度
fingerList FingerInfo[],包含触发事件的所有触点信息

回调比较简单, 只需要在onActionUpdate中获取旋转角度用于计算就可以了。
接下来看一下控制组件旋转的rotate参数,只有angle是必填

名称 说明
x 旋转轴向量x坐标
y 旋转轴向量y坐标
z 旋转轴向量z坐标
angle 旋转角度
centerX 变换中心点x轴坐标
centerY 变换中心点y轴坐标
centerZ z轴锚点,即3D旋转中心点的z轴分量,px
perspective 相机放置的z轴坐标,px

以上参数中,x,y,z 三个参数共同决定组件的旋转方向,例如
x:1, y:0, z:0:围绕 X 轴旋转(类似 “前后翻转”)
x:0, y:1, z:0:围绕 Y 轴旋转(类似 “左右翻转”)
x:0, y:0, z:1:围绕 Z 轴旋转(类似 “平面旋转”)
2D平面旋转中,centerX,centerY决定组件的旋转中心点,即以哪个点为中心旋转。
看一下旋转效果演示
鸿蒙Next手势详解系列:旋转手势-鸿蒙开发者社区
注意:
1.旋转过程中不会重新回调图片的onAreaChange,所以旋转中心点是基于图片初始状态的
2.centerZ测试不支持动态修改,感兴趣的可以自己赋值看一下效果
演示源码:

import { getScreenHeight, getScreenWidth } from '../utils/DisplayUtil'
@Extend(Radio)
function myRadioStyle() {
  .checked(false)
  .radioStyle({
    checkedBackgroundColor: Color.Green,  //开启状态底板颜色
    uncheckedBorderColor:Color.Red, //关闭状态描边颜色
    indicatorColor:Color.Yellow  //开启状态内部圆饼颜色
  })
  .height(20)
  .width(20)
}
@Entry
@ComponentV2
struct RotationGestureTest{
  @Local imageWidth: number = getScreenWidth()
  @Local imageHeight: number = getScreenHeight()
  @Local imageTopleftX:number = 0 //图片左上角相对于父元素的坐标
  @Local imageTopleftY:number = 0
  @Local rotateValue :number=0  //旋转角度
  @Local currentAngle :number=0  //记录当前旋转角度 用于旋转计算累加
  @Local fingerList: FingerInfo[] = []
  @Local rotateX:number = 0
  @Local rotateY:number = 0
  @Local rotateZ:number = 1
  @Local rotateCenterX:number = 0 //旋转中心点 坐标
  @Local rotateCenterY:number = 0
  @Local centerX:number=50
  @Local centerY:number=50
  build() {
    Column(){
      Stack(){
        Image($r('app.media.imagetest'))
          .width(this.imageWidth)
          .height(this.imageHeight)
          .draggable(false)
          .onComplete((event)=>{
            const imageTrueWidth=event?.width??0
            const imageTrueHeight=event?.height??0
            // 宽度铺满
            this.imageHeight = imageTrueHeight*this.imageWidth / imageTrueWidth;
          })
          .onAreaChange((oldValue: Area, newValue: Area)=>{
            this.imageTopleftX = newValue.position.x as  number
            this.imageTopleftY = newValue.position.y as  number
            this.rotateCenterX = this.imageWidth/2
            this.rotateCenterY = this.imageTopleftY+this.imageHeight/2
          })
          .rotate({
            x:this.rotateX,y:this.rotateY,z:this.rotateZ,
            centerX:this.centerX+'%',
            centerY:this.centerY+'%',
            angle:this.rotateValue})
          .gesture(
            RotationGesture({fingers:2,angle:5})
              .onActionStart((event: GestureEvent) => {
              })
              .onActionUpdate((event: GestureEvent) => {
                this.rotateValue = this.currentAngle + event.angle
              })
              .onActionEnd((event: GestureEvent) => {
                this.currentAngle = this.rotateValue
              })
          )
        Circle({width:20,height:20}).fill(Color.Red)
          .position({
            x:this.rotateCenterX,
            y:this.rotateCenterY})
      }.width('100%').height('50%').border({
        width:2,
        color:Color.Black
      })
      Column({space:20}){
        Text('旋转角度:'+this.rotateValue).height(60).fontSize(24)
        Button('恢复默认').onClick(()=>{
          this.rotateValue=0
          this.currentAngle=0
          this.rotateX = 0
          this.rotateY = 0
          this.rotateZ = 1
          this.rotateCenterX = this.imageWidth/2
          this.rotateCenterY = this.imageTopleftY+this.imageHeight/2
          this.centerX = 50
          this.centerY = 50
        })
        Row(){
          Radio({ value: 'x', group: 'radioGroup' })
            .myRadioStyle()
            .onChange((isChecked: boolean) => {
              if(isChecked){
                this.rotateX = 1
                this.rotateY = 0
                this.rotateZ = 0
              }
            })
          Text('X轴方向旋转 ')
          Radio({ value: 'y', group: 'radioGroup' })
            .myRadioStyle()
            .onChange((isChecked: boolean) => {
              if(isChecked){
                this.rotateX = 0
                this.rotateY = 1
                this.rotateZ = 0
              }
            })
          Text('Y轴方向旋转 ')
          Radio({ value: 'z', group: 'radioGroup' })
            .myRadioStyle()
            .checked(this.rotateZ==1)
            .onChange((isChecked: boolean) => {
              if(isChecked){
                this.rotateX = 0
                this.rotateY = 0
                this.rotateZ = 1
              }
            })
          Text('Z轴方向旋转 ')
        }
        Column(){
          Row({ space: 10 }) {
            Text('旋转中心点X偏移量' + this.centerX)
            Slider({
              value: this.centerX,
              min: 0,
              max: 100,
              style: SliderStyle.OutSet
            }).width('50%')
              .onChange((value: number) => {
                this.centerX = value;
                this.rotateCenterX = this.imageWidth*this.centerX/100
              })
          }
          Row({ space: 10 }) {
            Text('旋转中心点Y偏移量' + this.centerY)
            Slider({
              value: this.centerY,
              min: 0,
              max: 100,
              style: SliderStyle.OutSet
            }).width('50%')
              .onChange((value: number) => {
                this.centerY = value;
                this.rotateCenterY = this.imageHeight*this.centerY/100+this.imageTopleftY
              })
          }

          Text('图片左上角坐标X'+this.imageTopleftX)
          Text('图片左上角坐标Y'+this.imageTopleftY)
        }
        .alignItems(HorizontalAlign.Start)
        .backgroundColor(Color.Gray)

      }.layoutWeight(1).width('100%')
    }
  }
}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
收藏
回复
举报
回复
    相关推荐