HarmonyOS 鸿蒙Next手势旋转

HarmonyOS 鸿蒙Next手势旋转 大佬们,求个简单的手势旋转的Demo

4 回复

【背景知识】

【解决方案】

这边旋转的场景是什么,可以参考下这个demo,通过手势对组件进行旋转这样:

import { matrix4 } from '@kit.ArkUI'

@Entry
@ComponentV2
struct ImageMovePage {
  // 声明平移矩阵
  @Local matrix: matrix4.Matrix4Transit = matrix4.identity().copy();
  // 声明旋转矩阵
  @Local translateMatrix: matrix4.Matrix4Transit = matrix4.identity().copy();
  // 声明手势前的矩阵
  private rotateAndScaleMatrix: matrix4.Matrix4Transit = matrix4.identity().copy();
  // 声明初始位置坐标
  private initStackX: number = 0;
  private initStackY: number = 0;
  // 声明初始宽高
  private initStackWidth: number = 0;
  private initStackHeight: number = 0;
  // 声明中心点坐标
  private initCenterX: number = 0;
  private initCenterY: number = 0;
  // 声明初始半径
  private initR: number = 0;
  // 声明初始角度
  private initAngle: number = 0;
  // 声明手势开始时的坐标
  private startStackX: number = 0;
  private startStackY: number = 0;
  // 声明当前位置
  @Local currentX: number = 0;
  @Local currentY: number = 0;
  // 声明当前中心点坐标
  @Local currentCenterX: number = 0;
  @Local currentCenterY: number = 0;
  // 声明缩放比例
  @Local stackScale: number = 1;
  private lastScale: number = 1;
  // 声明当前旋转角度
  @Local stackRotate: number = 0;
  private lastRotate: number = 0;
  // 声明移动时手指的初始坐标
  private initFingerX: number = -1;
  private initFingerY: number = -1;
  private lastFingerX: number = 0;
  private lastFingerY: number = 0;

  // 在即将出现时初始化数据
  aboutToAppear(): void {
    this.init();
  }

  // 初始化方法:获取初始位置
  init() {
    let observer = this.getUIContext().getUIInspector().createComponentObserver('target');
    observer.on('layout', () => {
      let node = this.getUIContext().getAttachedFrameNodeById('target');
      if (node) {
        let position = node.getPositionToParent();
        this.initStackX = position.x;
        this.initStackY = position.y;
        let size = node.getMeasuredSize();
        this.initStackWidth = this.getUIContext().px2vp(size.width);
        this.initStackHeight = this.getUIContext().px2vp(size.height);
        this.initCenterX = this.initStackX + (this.initStackWidth >> 1);
        this.initCenterY = this.initStackY + (this.initStackHeight >> 1);
        this.initR = Math.sqrt((this.initStackWidth >> 1) * (this.initStackWidth >> 1) +
          (this.initStackHeight >> 1) * (this.initStackHeight >> 1));
        this.initAngle = Math.atan2((this.initStackHeight), (this.initStackWidth)) * 180 / Math.PI;
      }
      console.info('on layout:',
        `x:${this.initStackX}, y:${this.initStackY}, width: ${this.initStackWidth}, height: ${this.initStackHeight}, centerX: ${this.initCenterX}, centerY: ${this.initCenterY}
          initR: ${this.initR}, angle: ${this.initAngle}
      `)
      observer.off('layout');
    })
  }

  build() {
    NavDestination() {
      Stack() {
        Stack() {
          Stack({ alignContent: Alignment.BottomEnd }) {
            Text('harmonyos')
              .width(180)
              .height(180)
              .borderWidth(1)
              .borderColor('#f00')
              .fontColor('#fff')
            Text()
              .width(60)
              .height(60)
              .backgroundColor('#0f0')
              .gesture(
                PanGesture()
                  .onActionStart(event => {
                    // 记录上一次的矩阵
                    this.rotateAndScaleMatrix = this.matrix.copy();
                    // 获取手指位置
                    this.initFingerX = (this.initStackWidth >> 1);
                    this.initFingerY = (this.initStackHeight >> 1);
                  })
                  .onActionUpdate(event => {
                    // 获取当前坐标手指
                    let currentFingerX: number = this.initFingerX + event.offsetX;
                    let currentFingerY: number = this.initFingerY + event.offsetY;
                    // 映射手指坐标
                    let point = this.rotateAndScaleMatrix.copy().transformPoint([currentFingerX, currentFingerY]);
                    currentFingerX = point[0];
                    currentFingerY = point[1];
                    console.info('event.offsetX:', currentFingerX, 'event.offsetY', currentFingerY);
                    // 获取半径
                    let r: number = Math.sqrt(currentFingerX * currentFingerX + currentFingerY * currentFingerY);
                    console.info('半径r:', r, '初始半径:', this.initR, '比率:', r / this.initR);
                    // 获取cos和sin,判断象限
                    this.stackScale = (r / this.initR);
                    // 声明角度
                    let theta: number = Math.atan2(currentFingerY, currentFingerX) * 180 / Math.PI - this.initAngle;
                    // 判断象限
                    this.stackRotate = theta;
                    this.matrix = matrix4.identity().copy()
                      // .scale({ x: r / this.initR, y: r / this.initR })
                      .rotate({ z: 1, angle: theta })
                  })
                  .onActionEnd(event => {
                    // this.lastFingerX = this.initFingerX + event.offsetX;
                    // this.lastFingerY = this.initFingerY + event.offsetY;
                    console.info('旋转角度:', this.stackRotate);
                  })
              )
          }
          .transform(this.matrix).gesture(
            PanGesture()
              .onActionStart(() => {
                this.startStackX = this.currentX;
                this.startStackY = this.currentY;
              })
              .onActionUpdate(event => {
                // 获取手指位置
                let point = this.matrix.copy().transformPoint([event.offsetX, event.offsetY]);
                this.currentX = this.startStackX + point[0];
                this.currentY = this.startStackY + point[1];
              })
              .onActionEnd(() => {
                console.info('currentX:', this.currentX, 'currentY:', this.currentY);
              })
          )
        }
        .id('target')
      }
      .height('100%')
      .backgroundColor('#000')
      .width('100%')
    }
  }
}

如果是需要双指旋转手势,可以参考官网示例,可以根据实际需要进行具体开发。

更多关于HarmonyOS 鸿蒙Next手势旋转的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


HarmonyOS Next的手势旋转功能基于ArkTS/ArkUI框架实现。通过RotationGesture组件的onAction回调可获取旋转角度与速度,使用rotate方法直接操作组件旋转属性。系统自动处理多指触摸数据,识别旋转手势并触发响应。开发者需在布局中声明RotationGesture识别器,绑定旋转事件处理逻辑。该功能依赖鸿蒙的分布式手势识别能力,支持跨设备同步旋转状态。

基于HarmonyOS Next的手势旋转简单Demo实现

使用ArkTS编写,适用于旋转图片或组件:

import { Gesture, GestureGroup, GestureMask, PanGesture, RotationGesture } from '@kit.ArkUI';

@Entry
@Component
struct RotationDemo {
  @State angle: number = 0; // 旋转角度
  private initialAngle: number = 0; // 初始角度记录

  build() {
    Column() {
      // 可旋转的图片组件
      Image($r('app.media.rotate_img'))
        .width(200)
        .height(200)
        .rotate({ angle: this.angle })
        // 组合手势:旋转+平移(可选)
        .gesture(
          GestureGroup(
            GestureMode.Exclusive,
            RotationGesture()
              .onActionStart((event: GestureEvent) => {
                this.initialAngle = this.angle; // 记录开始角度
              })
              .onActionUpdate((event: RotationGestureEvent) => {
                // 更新旋转角度(弧度转角度)
                this.angle = this.initialAngle + event.angle * 180 / Math.PI;
              }),
            PanGesture({ distance: 1 }) // 可同时支持平移
          )
        )
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

关键说明

  1. 使用RotationGesture监听旋转手势
  2. 通过rotate修饰符实时更新组件旋转角度
  3. GestureMode.Exclusive保证多手势互斥识别
  4. 角度计算:手势返回弧度值,需转换为角度(1弧度≈57.3度)

扩展建议

  • 可搭配PinchGesture实现缩放组合手势
  • 通过onActionEnd添加旋转惯性动画
  • 使用GestureMask.Normal控制手势竞争关系

此Demo实现了基础旋转功能,实际使用时需替换图片资源路径并调整手势参数。

回到顶部