【HarmonyOS】头像裁剪之手势放大缩小,平移,双击缩放控制(三)
 作者:GeorgeGcs
- 2025-06-24  上海
- 本文字数:4377 字 - 阅读完需:约 14 分钟 
一、DEMO 效果图:
 
  
 二、开发思路:使用矩阵变换控制图片的放大缩小和平移形态。
通过监听点击手势 TapGesture,缩放手势 PinchGesture,拖动手势 PanGesture 进行手势操作的功能实现。
通过对矩阵变换参数 mMatrix 的赋值,将矩阵变换参数赋值给 image 控件。实现手势操作和图片操作的同步。
 
 该参数拥有四维坐标,只需要通过手势操作调整四个参数即可实现。通过.transform(this.mMatrix)赋值给 image 控件。
通过 image 的.onComplete(this.onLoadImgComplete)函数回调,获取图片控件的宽高和内容宽高等参数。以此为基准,手势操作调整的都是这些值。
三、DEMO 示例代码:
import router from '@ohos.router';import { CropMgr, ImageInfo } from '../manager/CropMgr';import { image } from '@kit.ImageKit';import Matrix4 from '@ohos.matrix4';import FS from '@ohos.file.fs';
export class LoadResult {  width: number = 0;  height: number = 0;  componentWidth: number = 0;  componentHeight: number = 0;  loadingStatus: number = 0;  contentWidth: number = 0;  contentHeight: number = 0;  contentOffsetX: number = 0;  contentOffsetY: number = 0;}
@Entry@Componentexport struct CropPage {  private TAG: string = "CropPage";
  private mRenderingContextSettings: RenderingContextSettings = new RenderingContextSettings(true);  private mCanvasRenderingContext2D: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.mRenderingContextSettings);
  // 加载图片  @State mImg: PixelMap | undefined = undefined;  // 图片矩阵变换参数  @State mMatrix: object = Matrix4.identity()    .translate({ x: 0, y: 0 })    .scale({ x: 1, y: 1});
  @State mImageInfo: ImageInfo = new ImageInfo();
  private tempScale = 1;  private startOffsetX: number = 0;  private startOffsetY: number = 0;
  aboutToAppear(): void {    console.log(this.TAG, "aboutToAppear start");    let temp = CropMgr.Ins().mSourceImg;    console.log(this.TAG, "aboutToAppear temp: " + JSON.stringify(temp));    this.mImg = temp;    console.log(this.TAG, "aboutToAppear end");  }
  private getImgInfo(){    return this.mImageInfo;  }
  onClickCancel = ()=>{    router.back();  }
  onClickConfirm = async ()=>{    if(!this.mImg){      console.error(this.TAG, " onClickConfirm mImg error null !");      return;    }
    // 存当前裁剪的图  // ...    router.back();  }
  /**   * 复制图片   * @param pixel   * @returns   */  async copyPixelMap(pixel: PixelMap): Promise<PixelMap> {    const info: image.ImageInfo = await pixel.getImageInfo();    const buffer: ArrayBuffer = new ArrayBuffer(pixel.getPixelBytesNumber());    await pixel.readPixelsToBuffer(buffer);    const opts: image.InitializationOptions = {      editable: true,      pixelFormat: image.PixelMapFormat.RGBA_8888,      size: { height: info.size.height, width: info.size.width }    };    return image.createPixelMap(buffer, opts);  }
  /**   * 图片加载回调   */  private onLoadImgComplete = (msg: LoadResult) => {    this.getImgInfo().loadResult = msg;    this.checkImageScale();  }
  /**   * 绘制画布中的取景框   */  private onCanvasReady = ()=>{    if(!this.mCanvasRenderingContext2D){      console.error(this.TAG, "onCanvasReady error mCanvasRenderingContext2D null !");      return;    }    let cr = this.mCanvasRenderingContext2D;    // 画布颜色    cr.fillStyle = '#AA000000';    let height = cr.height;    let width = cr.width;    cr.fillRect(0, 0, width, height);
    // 圆形的中心点    let centerX = width / 2;    let centerY = height / 2;    // 圆形半径    let radius = Math.min(width, height) / 2 - px2vp(100);    cr.globalCompositeOperation = 'destination-out'    cr.fillStyle = 'white'    cr.beginPath();    cr.arc(centerX, centerY, radius, 0, 2 * Math.PI);    cr.fill();
    cr.globalCompositeOperation = 'source-over';    cr.strokeStyle = '#FFFFFF';    cr.beginPath();    cr.arc(centerX, centerY, radius, 0, 2 * Math.PI);    cr.closePath();
    cr.lineWidth = 1;    cr.stroke();  }
  build() {    RelativeContainer() {      // 黑色底图      Row().width("100%").height("100%").backgroundColor(Color.Black)
      // 用户图      Image(this.mImg)        .objectFit(ImageFit.Contain)        .width('100%')        .height('100%')        .transform(this.mMatrix)        .alignRules({          center: { anchor: '__container__', align: VerticalAlign.Center },          middle: { anchor: '__container__', align: HorizontalAlign.Center }        })        .onComplete(this.onLoadImgComplete)
      // 取景框      Canvas(this.mCanvasRenderingContext2D)        .width('100%')        .height('100%')        .alignRules({          center: { anchor: '__container__', align: VerticalAlign.Center },          middle: { anchor: '__container__', align: HorizontalAlign.Center }        })        .backgroundColor(Color.Transparent)        .onReady(this.onCanvasReady)        .clip(true)        .backgroundColor("#00000080")
      Row(){        Button("取消")          .size({            width: px2vp(450),            height: px2vp(200)          })          .onClick(this.onClickCancel)
        Blank()
        Button("确定")          .size({            width: px2vp(450),            height: px2vp(200)          })          .onClick(this.onClickConfirm)      }      .width("100%")      .height(px2vp(200))      .margin({ bottom: px2vp(500) })      .alignRules({        center: { anchor: '__container__', align: VerticalAlign.Bottom },        middle: { anchor: '__container__', align: HorizontalAlign.Center }      })      .justifyContent(FlexAlign.Center)
    }    .width("100%").height("100%")    .priorityGesture(      // 点击手势      TapGesture({        // 点击次数        count: 2,        // 一个手指        fingers: 1      }).onAction((event: GestureEvent)=>{        console.log(this.TAG, "TapGesture onAction start");        if(!event){          return;        }        if(this.getImgInfo().scale != 1){          this.getImgInfo().scale = 1;          this.getImgInfo().offsetX = 0;          this.getImgInfo().offsetY = 0;          this.mMatrix = Matrix4.identity()            .translate({              x: this.getImgInfo().offsetX,              y: this.getImgInfo().offsetY            })            .scale({              x: this.getImgInfo().scale,              y: this.getImgInfo().scale            })        }else{          this.getImgInfo().scale = 2;          this.mMatrix = Matrix4.identity()            .translate({              x: this.getImgInfo().offsetX,              y: this.getImgInfo().offsetY            })            .scale({              x: this.getImgInfo().scale,              y: this.getImgInfo().scale            })        }
        console.log(this.TAG, "TapGesture onAction end");      })    )    .gesture(GestureGroup(      GestureMode.Parallel,      // 缩放手势      PinchGesture({        // 两指缩放        fingers: 2      })        .onActionStart(()=>{          console.log(this.TAG, "PinchGesture onActionStart");          this.tempScale = this.getImgInfo().scale;        })        .onActionUpdate((event)=>{          console.log(this.TAG, "PinchGesture onActionUpdate" + JSON.stringify(event));          if(event){            this.getImgInfo().scale = this.tempScale * event.scale;            this.mMatrix = Matrix4.identity()              .translate({                x: this.getImgInfo().offsetX,                y: this.getImgInfo().offsetY              })              .scale({                x: this.getImgInfo().scale,                y: this.getImgInfo().scale              })          }        })        .onActionEnd(()=>{          console.log(this.TAG, "PinchGesture onActionEnd");         })      ,      // 拖动手势      PanGesture()        .onActionStart(()=>{          console.log(this.TAG, "PanGesture onActionStart");          this.startOffsetX = this.getImgInfo().offsetX;          this.startOffsetY = this.getImgInfo().offsetY;      })        .onActionUpdate((event)=>{          console.log(this.TAG, "PanGesture onActionUpdate" + JSON.stringify(event));          if(event){            let distanceX: number = this.startOffsetX + vp2px(event.offsetX) / this.getImgInfo().scale;            let distanceY: number = this.startOffsetY + vp2px(event.offsetY) / this.getImgInfo().scale;            this.getImgInfo().offsetX = distanceX;            this.getImgInfo().offsetY = distanceY;            this.mMatrix = Matrix4.identity()              .translate({                x: this.getImgInfo().offsetX,                y: this.getImgInfo().offsetY              })              .scale({                x: this.getImgInfo().scale,                y: this.getImgInfo().scale              })          }        })        .onActionEnd(()=>{          console.log(this.TAG, "PanGesture onActionEnd");        })    ))  }}
复制代码
 划线
评论
复制
发布于: 刚刚阅读数: 5

GeorgeGcs
关注
路漫漫其修远兮,吾将上下而求索。 2024-12-24 加入
鸿蒙创作先锋,华为HDE专家,鸿蒙讲师,作者。 目前任职鸿蒙应用架构师。历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。







 
    
 
				 
				 
			


评论