【HarmonyOS】模仿个人中心头像图片,调用系统相机拍照,从系统相册选择图片和圆形裁剪显示 (二)

【HarmonyOS】模仿个人中心头像图片,调用系统相机拍照,从系统相册选择图片和圆形裁剪显示 (二)

Demo效果展示:

方案思路:

1.修改调用相机的方式,使用cameraKit进行相机的调用,拍照后返回图片url进行处理。

2.裁剪View,使用画布进行取景框的效果展示

手势拖动和放大缩小图片,裁剪计算在第三章进行讲解。

Demo示例代码:

UI主界面

dart 复制代码
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { image } from '@kit.ImageKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { router } from '@kit.ArkUI';
import { cameraPicker as picker } from '@kit.CameraKit';
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { CropView } from './CropView';

@Entry
@Component
struct Index {
  private TAG: string = "imageTest";

  @State mUserPixel: image.PixelMap | undefined = undefined;
  @State mTargetPixel: image.PixelMap | undefined = undefined;

  /**
   * 拍照获取图片
   */
  private async getPictureFromCamera(){
    try {
      let pickerProfile: picker.PickerProfile = {
        // 相机的位置。
        cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
      };
      let pickerResult: picker.PickerResult = await picker.pick(
        getContext(),
        [picker.PickerMediaType.PHOTO],
        pickerProfile
      );
      console.log(this.TAG, "the pick pickerResult is:" + JSON.stringify(pickerResult));
      // 成功才处理
      if(pickerResult && pickerResult.resultCode == 0){
        await this.getImageByPath(pickerResult.resultUri);
      }
    } catch (error) {
      let err = error as BusinessError;
      console.error(this.TAG, `the pick call failed. error code: ${err.code}`);
    }
  }

  /**
   * 相册选择图片
   */
  private async getPictureFromAlbum() {
    let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    PhotoSelectOptions.maxSelectNumber = 1;

    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
    let albumPath = photoSelectResult.photoUris[0];
    console.info(this.TAG, 'getPictureFromAlbum albumPath= ' + albumPath);
    await this.getImageByPath(albumPath);
  }

  /**
   * 获取图片pixelMap
   * @param path
   */
  private async getImageByPath(path: string) {
    console.info(this.TAG, 'getImageByPath path: ' + path);
    try {
      // 读取图片为buffer
      const file = fs.openSync(path, fs.OpenMode.READ_ONLY);
      let photoSize = fs.statSync(file.fd).size;
      console.info(this.TAG, 'Photo Size: ' + photoSize);
      let buffer = new ArrayBuffer(photoSize);
      fs.readSync(file.fd, buffer);
      fs.closeSync(file);
      // 解码成PixelMap
      const imageSource = image.createImageSource(buffer);
      console.log(this.TAG, 'imageSource: ' + JSON.stringify(imageSource));
      this.mUserPixel = await imageSource.createPixelMap({});
    } catch (e) {
      console.info(this.TAG, 'getImage e: ' + JSON.stringify(e));
    }
  }

  build() {
    Scroll(){
      Column() {
        Text("点击拍照")
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            this.getPictureFromCamera();
          })

        Text("相册选择")
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            this.getPictureFromAlbum();
          })

        Image(this.mUserPixel)
          .objectFit(ImageFit.Fill)
          .width('100%')
          .aspectRatio(1)

        Text("图片裁剪")
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            this.cropImage();
            // router.pushUrl({
            //   url: "pages/crop"
            // })
          })

        CropView({ mImg: $mUserPixel })
          .width('100%')
          .aspectRatio(1)

        Text("裁剪效果")
          .fontSize(50)
          .fontWeight(FontWeight.Bold)

        Image(this.mTargetPixel)
            .width('100%')
            .aspectRatio(1)
            .borderRadius(200)

      }
      .height(3000)
      .width('100%')
    }
    .height('100%')
    .width('100%')
  }

  private async cropImage(){
    if(!this.mUserPixel){
      return;
    }
    let cp = await this.copyPixelMap(this.mUserPixel);
    let region: image.Region = { x: 0, y: 0, size: { width: 400, height: 400 } };
    cp.cropSync(region);
  }

  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);
  }

}

CropView 裁剪View

dart 复制代码
interface LoadResult {
  width: number;
  height: number;
  componentWidth: number;
  componentHeight: number;
  loadingStatus: number;
  contentWidth: number;
  contentHeight: number;
  contentOffsetX: number;
  contentOffsetY: number;
}

@Component
export struct CropView {
  private TAG: string = "CropView";

  private mRenderingContextSettings: RenderingContextSettings = new RenderingContextSettings(true);
  private mCanvasRenderingContext2D: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.mRenderingContextSettings);

  @Link mImg: PixelMap;


  private onLoadImgComplete = (msg: LoadResult) => {

  }

  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 - 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() {
    Stack() {
      // 黑色底图
      Row().width("100%").height("100%").backgroundColor(Color.Black)

      // 用户图
      Image(this.mImg)
        .objectFit(ImageFit.Fill)
        .width('100%')
        .aspectRatio(1)
        .onComplete(this.onLoadImgComplete)

      // 取景框
      Canvas(this.mCanvasRenderingContext2D)
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Transparent)
        .onReady(this.onCanvasReady)
        .clip(true)
        .backgroundColor("#00000080")

    }.width("100%").height("100%")
  }
}
相关推荐
zhanshuo2 小时前
在鸿蒙里优雅地处理网络错误:从 Demo 到实战案例
harmonyos
zhanshuo2 小时前
在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
harmonyos
whysqwhw7 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw8 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw10 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw11 小时前
鸿蒙音频编码
harmonyos
whysqwhw11 小时前
鸿蒙音频解码
harmonyos
whysqwhw11 小时前
鸿蒙视频解码
harmonyos
whysqwhw11 小时前
鸿蒙视频编码
harmonyos
ajassi200011 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos