HarmonyOS 鸿蒙Next头像裁剪圆形遮罩效果实现demo

发布于 1周前 作者 htzhanglong 来自 鸿蒙OS

HarmonyOS 鸿蒙Next头像裁剪圆形遮罩效果实现demo

【起因】

需要实现头像剪裁的圆形遮罩。

看到一个第三方库oh-crop是方形遮罩的

https://ohpm.openharmony.cn/#/cn/detail/@xinyansoft%2Foh-crop

【经过】

查看源码,修改.onReady改为圆形遮罩

【完整示例】

import { image } from ‘@kit.ImageKit’;
import { picker } from ‘@kit.CoreFileKit’;
import Matrix4 from ‘@ohos.matrix4’
import fs from ‘@ohos.file.fs’;
import { hilog } from ‘@kit.PerformanceAnalysisKit’;

@Component export struct CropView {

@State private model: CropModel = new CropModel();

private settings: RenderingContextSettings = new RenderingContextSettings(true); private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

@State private matrix: object = Matrix4.identity() .translate({ x: 0, y: 0 }) .scale({ x: this.model.scale, y: this.model.scale});

/** 临时变量,无需手动赋值 / private tempScale = 1; / 临时变量,无需手动赋值 / private startOffsetX: number = 0; / 临时变量,无需手动赋值 **/ private startOffsetY: number = 0;

build() { Stack() { Image(this.model.src) .width(‘100%’) .height(‘100%’) .alt(this.model.previewSource) .objectFit(ImageFit.Contain) .transform(this.matrix) .onComplete((msg) => { if (msg) { // 图片加载成功 this.model.imageWidth = msg.width; this.model.imageHeight = msg.height; this.model.componentWidth = msg.componentWidth; this.model.componentHeight = msg.componentHeight; this.checkImageAdapt();

        <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageLoadEventListener != <span class="hljs-literal"><span class="hljs-literal">null</span></span> &amp;&amp; msg.loadingStatus == <span class="hljs-number"><span class="hljs-number">1</span></span>) {
          <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageLoadEventListener.onImageLoaded(msg);
        }
      }
    })
    .onError((error) =&gt; {
      <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageLoadEventListener != <span class="hljs-literal"><span class="hljs-literal">null</span></span>) {
        <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageLoadEventListener.onImageLoadError(error);
      }
    })
  Canvas(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context)
    .width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
    .height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
    .backgroundColor(Color.Transparent)
    .onReady(() =&gt; {
      <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context == <span class="hljs-literal"><span class="hljs-literal">null</span></span>) {
        <span class="hljs-keyword"><span class="hljs-keyword">return</span></span>
      }

      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> height = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.height
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> width = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.width
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.fillStyle= <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.maskColor;
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.fillRect(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-number"><span class="hljs-number">0</span></span>, width, height)

      <span class="hljs-comment"><span class="hljs-comment">// 计算圆形的中心点和半径</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> centerX = width / <span class="hljs-number"><span class="hljs-number">2</span></span>;
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> centerY = height / <span class="hljs-number"><span class="hljs-number">2</span></span>;
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> minDimension = <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.min(width, height);
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameRadiusInVp = (minDimension - px2vp(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth)) / <span class="hljs-number"><span class="hljs-number">2</span></span>; <span class="hljs-comment"><span class="hljs-comment">// 减去边框宽度</span></span>

      <span class="hljs-comment"><span class="hljs-comment">// 把中间的取景框透出来</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.globalCompositeOperation = <span class="hljs-string"><span class="hljs-string">'destination-out'</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.fillStyle = <span class="hljs-string"><span class="hljs-string">'white'</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameWidthInVp = px2vp(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth);
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameHeightInVp = px2vp(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight());
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> x = (width - px2vp(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth)) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> y = (height - px2vp(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight())) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
      <span class="hljs-comment"><span class="hljs-comment">// this.context.fillRect(x, y, frameWidthInVp, frameHeightInVp)</span></span>
      console.info(`width:${width}`)
      console.info(`height:${height}`)
      console.info(`x:${x}`)
      console.info(`y:${y}`)
      console.info(`<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth:${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth}`)
      console.info(`<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight():${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight()}`)
      console.info(`frameWidthInVp:${frameWidthInVp}`)
      console.info(`frameHeightInVp:${frameHeightInVp}`)
      console.info(`frameRadiusInVp:${frameRadiusInVp}`)
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.beginPath();
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.arc(centerX, centerY, px2vp(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth/<span class="hljs-number"><span class="hljs-number">2</span></span>), <span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-number"><span class="hljs-number">2</span></span> * <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.PI);
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.fill();

      <span class="hljs-comment"><span class="hljs-comment">// 设置综合操作模式为源覆盖,以便在现有图形上添加新的图形</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.globalCompositeOperation = <span class="hljs-string"><span class="hljs-string">'source-over'</span></span>;

      <span class="hljs-comment"><span class="hljs-comment">// 设置描边颜色</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.strokeStyle = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.strokeColor;


      <span class="hljs-comment"><span class="hljs-comment">// 计算圆形的半径,这里我们取正方形边框的较短边的一半作为半径</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> radius = <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.min(frameWidthInVp, frameHeightInVp) / <span class="hljs-number"><span class="hljs-number">2</span></span>;

      <span class="hljs-comment"><span class="hljs-comment">// 开始绘制路径</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.beginPath();

      <span class="hljs-comment"><span class="hljs-comment">// 使用 arc 方法绘制圆形</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.arc(centerX, centerY, radius, <span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-number"><span class="hljs-number">2</span></span> * <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.PI);

      <span class="hljs-comment"><span class="hljs-comment">// 关闭路径</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.closePath();

      <span class="hljs-comment"><span class="hljs-comment">// 描绘圆形边框</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.lineWidth = <span class="hljs-number"><span class="hljs-number">1</span></span>; <span class="hljs-comment"><span class="hljs-comment">// 边框宽度</span></span>
      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.context.stroke();

    })
    .enabled(<span class="hljs-literal"><span class="hljs-literal">false</span></span>)
}
.clip(<span class="hljs-literal"><span class="hljs-literal">true</span></span>)
.width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.backgroundColor(<span class="hljs-string"><span class="hljs-string">"#00000080"</span></span>)
.priorityGesture(
  TapGesture({ count: <span class="hljs-number"><span class="hljs-number">2</span></span>, fingers: <span class="hljs-number"><span class="hljs-number">1</span></span> })
    .onAction((event:GestureEvent) =&gt; {
      <span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(!event){
        <span class="hljs-keyword"><span class="hljs-keyword">return</span></span>
      }
      <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.zoomEnabled) {
        <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale != <span class="hljs-number"><span class="hljs-number">1</span></span>) {
          <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale = <span class="hljs-number"><span class="hljs-number">1</span></span>;
          <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.reset();
          <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.updateMatrix();
        } <span class="hljs-keyword"><span class="hljs-keyword">else</span></span> {
          <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.zoomTo(<span class="hljs-number"><span class="hljs-number">2</span></span>);
        }
      }

      <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.checkImageAdapt();
    })
)
.gesture(
  GestureGroup(GestureMode.Parallel,
    <span class="hljs-comment"><span class="hljs-comment">// 拖动手势</span></span>
    PanGesture({})
      .onActionStart(() =&gt; {
        hilog.info(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-string"><span class="hljs-string">"CropView"</span></span>, <span class="hljs-string"><span class="hljs-string">"Pan gesture start"</span></span>);
        <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.startOffsetX = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.offsetX;
        <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.startOffsetY = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.offsetY;
      })
      .onActionUpdate((event:GestureEvent) =&gt; {
        hilog.info(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-string"><span class="hljs-string">"CropView"</span></span>, `Pan gesture update: ${<span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(event)}`);
        <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (event) {
          <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.panEnabled) {
            <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> distanceX: number = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.startOffsetX + vp2px(event.offsetX) / <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale;
            <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> distanceY: number = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.startOffsetY + vp2px(event.offsetY) / <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale;
            <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.offsetX = distanceX;
            <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.offsetY = distanceY;
            <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.updateMatrix()
          }
        }
      })
      .onActionEnd(() =&gt; {
        hilog.info(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-string"><span class="hljs-string">"CropView"</span></span>, <span class="hljs-string"><span class="hljs-string">"Pan gesture end"</span></span>);
        <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.checkImageAdapt();
      }),

    <span class="hljs-comment"><span class="hljs-comment">// 缩放手势处理</span></span>
    PinchGesture({ fingers: <span class="hljs-number"><span class="hljs-number">2</span></span> })
      .onActionStart(() =&gt; {
        <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tempScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale
      })
      .onActionUpdate((event) =&gt; {
        <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (event) {
          <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (!<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.zoomEnabled) <span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
          <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.zoomTo(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tempScale * event.scale);
        }
      })
      .onActionEnd(() =&gt; {
        <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.checkImageAdapt();
      })
  )
)

}

/**

  • 检查手势操作后,图片是否填满取景框,没填满则进行调整 */ private checkImageAdapt() { let offsetX = this.model.offsetX; let offsetY = this.model.offsetY; let scale = this.model.scale; hilog.info(0, “CropView”, offsetX: ${offsetX}, offsetY: ${offsetY}, scale: ${scale})
<span class="hljs-comment"><span class="hljs-comment">// 图片适配控件的时候也进行了缩放,计算出这个缩放比例</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> widthScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentWidth / <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageWidth;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> heightScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentHeight / <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageHeight;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> adaptScale = <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.min(widthScale, heightScale);
hilog.info(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-string"><span class="hljs-string">"CropView"</span></span>, `Image scale ${adaptScale} <span class="hljs-keyword"><span class="hljs-keyword">while</span></span> attaching the component[${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentWidth}, ${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentHeight}]`)

<span class="hljs-comment"><span class="hljs-comment">// 经过两次缩放(适配控件、手势)后,图片的实际显示大小</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showWidth = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageWidth * adaptScale * <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showHeight = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.imageHeight * adaptScale * <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> imageX = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentWidth - showWidth) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> imageY = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentHeight - showHeight) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
hilog.info(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-string"><span class="hljs-string">"CropView"</span></span>, `Image left top is (${imageX}, ${imageY})`)

<span class="hljs-comment"><span class="hljs-comment">// 取景框的左上角坐标</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameX = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentWidth - <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameY = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.componentHeight - <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight()) / <span class="hljs-number"><span class="hljs-number">2</span></span>;

<span class="hljs-comment"><span class="hljs-comment">// 图片左上角坐标</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showX = imageX + offsetX * scale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showY = imageY + offsetY * scale;
hilog.info(<span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-string"><span class="hljs-string">"CropView"</span></span>, `Image show at (${showX}, ${showY})`)

<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth &gt; showWidth || <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight() &gt; showHeight) { <span class="hljs-comment"><span class="hljs-comment">// 图片缩放后,大小不足以填满取景框</span></span>
  <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> xScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth / showWidth;
  <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> yScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight() / showHeight;
  <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> newScale = <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.max(xScale, yScale);
  <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.scale * newScale;
  showX *= newScale;
  showY *= newScale;
}

<span class="hljs-comment"><span class="hljs-comment">// 调整x轴方向位置,使图像填满取景框</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(showX &gt; frameX) {
  showX = frameX;
} <span class="hljs-keyword"><span class="hljs-keyword">else</span></span> <span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(showX + showWidth &lt; frameX + <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth) {
  showX = frameX + <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.frameWidth - showWidth;
}
<span class="hljs-comment"><span class="hljs-comment">// 调整y轴方向位置,使图像填满取景框</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(showY &gt; frameY) {
  showY = frameY;
} <span class="hljs-keyword"><span class="hljs-keyword">else</span></span> <span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(showY + showHeight &lt; frameY + <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight()) {
  showY = frameY + <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.getFrameHeight() - showHeight;
}
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.offsetX = (showX - imageX) / scale;
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.offsetY = (showY - imageY) / scale;
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.updateMatrix();

}

public zoomTo(scale: number): void { this.model.scale = scale; this.updateMatrix(); }

public updateMatrix(): void { this.matrix = Matrix4.identity() .translate({ x: this.model.offsetX, y: this.model.offsetY }) .scale({ x: this.model.scale, y: this.model.scale }) } }

interface ImageLoadedEvent { width: number; height: number; componentWidth: number; componentHeight: number; loadingStatus: number; contentWidth: number; contentHeight: number; contentOffsetX: number; contentOffsetY: number; }

export interface ImageLoadEventListener {

onImageLoaded(msg: ImageLoadedEvent): void;

onImageLoadError(error: ImageError): void; }

export class CropModel { /**

  • 图片uri
  • 类型判断太麻烦了,先只支持string,其他形式的需要先转换成路径 / src: string = ‘’; /*
  • 图片预览 / previewSource: string | Resource = ‘’; /*
  • 是否可以拖动 / panEnabled: boolean = true; /*
  • 是否可以缩放 / zoomEnabled: boolean = true; /*
  • 取景框宽度 / frameWidth = 1000; /*
  • 取景框宽高比 / frameRatio = 1; /*
  • 遮罩颜色 / maskColor: string = ‘#AA000000’; /*
  • 取景框边框颜色 / strokeColor: string = ‘#FFFFFF’; /*
  • 图片加载监听 */ imageLoadEventListener: ImageLoadEventListener | null = null;

/// 以下变量不要手动赋值 /// /**

  • 图片宽度,加载完成之后才会赋值 / imageWidth: number = 0; /*
  • 图片高度,加载完成之后才会赋值 / imageHeight: number = 0; /*
  • 控件宽度 / componentWidth: number = 0; /*
  • 控件高度 / componentHeight: number = 0; /*
  • 手势缩放比例
  • 图片经过了两重缩放,一是适配控件的时候进行了缩放,二是手势操作的时候进行了缩放 / scale: number = 1; /*
  • x轴方向偏移量 / offsetX: number = 0; /*
  • y轴方向偏移量 */ offsetY: number = 0;

/////////////////////////////////////////////

public setImage(src: string, previewSource?: string | Resource): CropModel { this.src = src; if (!!previewSource) { this.previewSource = previewSource; } return this; }

public setScale(scale: number): CropModel { this.scale = scale; return this; }

public isPanEnabled(): boolean { return this.panEnabled; }

public setPanEnabled(panEnabled: boolean): CropModel { this.panEnabled = panEnabled; return this; }

public setZoomEnabled(zoomEnabled: boolean): CropModel { this.zoomEnabled = zoomEnabled; return this; }

public setFrameWidth(frameWidth: number) : CropModel { this.frameWidth = frameWidth; return this; }

public setFrameRatio(frameRatio: number) : CropModel { this.frameRatio = frameRatio; return this; }

public setMaskColor(color: string) : CropModel { this.maskColor = color; return this; }

public setStrokeColor(color: string) : CropModel { this.strokeColor = color; return this; }

public setImageLoadEventListener(listener: ImageLoadEventListener) : CropModel { this.imageLoadEventListener = listener; return this; }

public getScale(): number { return this.scale; }

public isZoomEnabled(): boolean { return this.zoomEnabled; }

public getImageWidth(): number { return this.imageWidth; }

public getImageHeight(): number { return this.imageHeight; }

public getFrameHeight() { return this.frameWidth / this.frameRatio; }

public reset(): void { this.scale = 1; this.offsetX = 0; this.offsetY = 0; }

public async crop(format: image.PixelMapFormat) : Promise<image.PixelMap> {

<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(!<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.src || <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.src == <span class="hljs-string"><span class="hljs-string">''</span></span>) {
  <span class="hljs-keyword"><span class="hljs-keyword">throw</span></span> <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> <span class="hljs-built_in"><span class="hljs-built_in">Error</span></span>(<span class="hljs-string"><span class="hljs-string">'Please set src first'</span></span>);
}
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.imageWidth == <span class="hljs-number"><span class="hljs-number">0</span></span> || <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.imageHeight == <span class="hljs-number"><span class="hljs-number">0</span></span>) {
  <span class="hljs-keyword"><span class="hljs-keyword">throw</span></span> <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> <span class="hljs-built_in"><span class="hljs-built_in">Error</span></span>(<span class="hljs-string"><span class="hljs-string">'The image is not loaded'</span></span>);
}

<span class="hljs-comment"><span class="hljs-comment">// 图片适配控件的时候也进行了缩放,计算出这个缩放比例</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> widthScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.componentWidth / <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.imageWidth;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> heightScale = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.componentHeight / <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.imageHeight;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> adaptScale = <span class="hljs-built_in"><span class="hljs-built_in">Math</span></span>.min(widthScale, heightScale);

<span class="hljs-comment"><span class="hljs-comment">// 经过两次缩放(适配控件、手势)后,图片的实际显示大小</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> totalScale = adaptScale * <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.scale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showWidth = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.imageWidth * totalScale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showHeight = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.imageHeight * totalScale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> imageX = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.componentWidth - showWidth) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> imageY = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.componentHeight - showHeight) / <span class="hljs-number"><span class="hljs-number">2</span></span>;

<span class="hljs-comment"><span class="hljs-comment">// 取景框的左上角坐标</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameX = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.componentWidth - <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.frameWidth) / <span class="hljs-number"><span class="hljs-number">2</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> frameY = (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.componentHeight - <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.getFrameHeight()) / <span class="hljs-number"><span class="hljs-number">2</span></span>;

<span class="hljs-comment"><span class="hljs-comment">// 图片左上角坐标</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showX = imageX + <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.offsetX * <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.scale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> showY = imageY + <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.offsetY * <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.scale;

<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> x = (frameX - showX) / totalScale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> y = (frameY - showY) / totalScale;
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> file = fs.openSync(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.src, fs.OpenMode.READ_ONLY)
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> imageSource : image.ImageSource = image.createImageSource(file.fd);
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> decodingOptions : image.DecodingOptions = {
  editable: <span class="hljs-literal"><span class="hljs-literal">true</span></span>,
  desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
}

<span class="hljs-comment"><span class="hljs-comment">// 创建pixelMap</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> pm = await imageSource.createPixelMap(decodingOptions);
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> cp = await <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.copyPixelMap(pm);
pm.release();
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> region: image.Region = { x: x, y: y, size: { width: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.frameWidth / totalScale, height: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.getFrameHeight() / totalScale } };
cp.cropSync(region);
<span class="hljs-keyword"><span class="hljs-keyword">return</span></span> cp;

}

async copyPixelMap(pm: PixelMap): Promise<PixelMap> { const imageInfo: image.ImageInfo = await pm.getImageInfo(); const buffer: ArrayBuffer = new ArrayBuffer(pm.getPixelBytesNumber()); // TODO 知识点:通过readPixelsToBuffer实现PixelMap的深拷贝,其中readPixelsToBuffer输出为BGRA_8888 await pm.readPixelsToBuffer(buffer); // TODO 知识点:readPixelsToBuffer输出为BGRA_8888,此处createPixelMap需转为RGBA_8888 const opts: image.InitializationOptions = { editable: true, pixelFormat: image.PixelMapFormat.RGBA_8888, size: { height: imageInfo.size.height, width: imageInfo.size.width } }; return image.createPixelMap(buffer, opts); } }

interface MyEvent { result: FileSelectorResult, fileSelector: FileSelectorParam } @Entry @Component struct Page23 { @State pm: PixelMap | undefined = undefined; @State private model: CropModel = new CropModel();

build() { Column() { CropView({ model: this.model, }) .layoutWeight(1) .width(‘100%’) Button(‘打开相册’).onClick(()=>{ this.openPicker() }) Button(‘测试剪裁’).onClick(async () => { try { this.pm = await this.model.crop(image.PixelMapFormat.RGBA_8888); } catch (e) { console.info(e:${<span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(e)}) } }) Text(‘剪裁结果’) Image(this.pm).width(‘300lpx’).height(‘300lpx’).borderRadius(‘150lpx’) } .height(‘100%’) .width(‘100%’) } // 弹出图片选择器方法 async openPicker() { try { // 设置图片选择器选项 const photoSelectOptions = new picker.PhotoSelectOptions(); // 限制只能选择一张图片 photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; photoSelectOptions.maxSelectNumber = 1; // 创建并实例化图片选择器 const photoViewPicker = new picker.PhotoViewPicker(); // 选择图片并获取图片URI let uris: picker.PhotoSelectResult = await photoViewPicker.select(photoSelectOptions); if (!uris || uris.photoUris.length === 0) return; // 获取选中图片的第一张URI let uri: string = uris.photoUris[0]; this.model.setImage(uri) .setFrameWidth(1000) .setFrameRatio(1); } catch (e) { console.error(‘openPicker’, JSON.stringify(e)); } }

handleFileSelection(event: MyEvent) { const PhotoSelectOptions = new picker.PhotoSelectOptions(); PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; PhotoSelectOptions.maxSelectNumber = 1;

<span class="hljs-keyword"><span class="hljs-keyword">const</span></span> photoPicker = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> picker.PhotoViewPicker();

photoPicker.select(PhotoSelectOptions)
  .then((PhotoSelectResult) =&gt; {
    <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (PhotoSelectResult.photoUris.length === <span class="hljs-number"><span class="hljs-number">0</span></span>) {
      console.warn(<span class="hljs-string"><span class="hljs-string">'No image selected.'</span></span>);
      <span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
    }

    <span class="hljs-keyword"><span class="hljs-keyword">const</span></span> srcUri = PhotoSelectResult.photoUris[<span class="hljs-number"><span class="hljs-number">0</span></span>];
    <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.model.setImage(srcUri)
      .setFrameWidth(<span class="hljs-number"><span class="hljs-number">1000</span></span>)
      .setFrameRatio(<span class="hljs-number"><span class="hljs-number">1</span></span>);
  })
  .catch((selectError: object) =&gt; {
    console.error(<span class="hljs-string"><span class="hljs-string">'Failed to invoke photo picker:'</span></span>, <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(selectError));
  });

<span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-literal"><span class="hljs-literal">true</span></span>;

} }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>



关于HarmonyOS 鸿蒙Next头像裁剪圆形遮罩效果实现demo的问题,您也可以访问:https://www.itying.com/category-93-b0.html 联系官网客服。

回到顶部