【入门到精通】鸿蒙next开发:基于Canvas、Image组件等实现对个人头像处理以及群头像生成实现实战

往期鸿蒙5.0全套实战文章必看:(文中附带全栈鸿蒙5.0学习资料)


基于Canvas、Image组件等实现对个人头像处理以及群头像生成实现

场景一:调用系统相机拍照,从系统相册选择图片,根据圆形遮罩,裁剪生成个人头像。

场景一实现方案:

1.创建图片选择器photoViewPicker,设置选择模式为图片,数量最大为1,调用photoViewPicker,将选择的图片以uri形式,存储到cropModel里面,并设置取景框宽度和宽高比

2.以画布为中心,使用canvas中API的this.context.arc绘制圆形取景框

3.添加拖动手势.gesture(gesturegroup),panGesture(onActionStart,onActionUpdate,onActionEnd)

4.对取景框对图片校准,若拖动缩放图片过程中,拖拽动作停止时,缩放图片太小;未占满取景框时,则将图片缩放到和取景框一 样宽度,调整图片和取景框在一个中心点上

5.根据cropModel存储的uri路径,获取图片源,根据生成的XY坐标和size,使用api image.Region获取图片对应的PixelMap

各步骤代码实现:

创建photoViewPicker,选择图片,并以uri形式,存储到cropModel。

// 弹出图片选择器方法 
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; 
  const photoPicker = new picker.PhotoViewPicker(); 
  photoPicker.select(PhotoSelectOptions) 
    .then((PhotoSelectResult) => { 
      if (PhotoSelectResult.photoUris.length === 0) { 
        console.warn('No image selected.'); 
        return; 
      } 
      const srcUri = PhotoSelectResult.photoUris[0]; 
      this.model.setImage(srcUri) 
        .setFrameWidth(1000) 
        .setFrameRatio(1); 
    }) 
    .catch((selectError: object) => { 
      console.error('Failed to invoke photo picker:', JSON.stringify(selectError)); 
    }); 
  return true; 
} 
}

使用画布进行圆形取景框的绘制。

Canvas(this.context).width('100%').height('100%') 
  .backgroundColor(Color.Transparent) 
  .onReady(() => { 
    if (this.context == null) { 
      return 
    } 
    let height = this.context.height 
    let width = this.context.width 
    this.context.fillStyle= this.model.maskColor; 
    this.context.fillRect(0, 0, width, height) 
    // 计算圆形的中心点和半径 
    let centerX = width / 2; 
    let centerY = height / 2; 
    let minDimension = Math.min(width, height); 
    let frameRadiusInVp = (minDimension - px2vp(this.model.frameWidth)) / 2; // 减去边框宽度 
    // 把中间的取景框透出来 
    this.context.globalCompositeOperation = 'destination-out' 
    this.context.fillStyle = 'white' 
    let frameWidthInVp = px2vp(this.model.frameWidth); 
    let frameHeightInVp = px2vp(this.model.getFrameHeight()); 
    let x = (width - px2vp(this.model.frameWidth)) / 2; 
    let y = (height - px2vp(this.model.getFrameHeight())) / 2; 
    this.context.beginPath(); 
    this.context.arc(centerX, centerY, px2vp(this.model.frameWidth/2), 0, 2 * Math.PI); 
    this.context.fill(); 
    // 设置综合操作模式为源覆盖,以便在现有图形上添加新的图形 
    this.context.globalCompositeOperation = 'source-over'; 
    // 设置描边颜色 
    this.context.strokeStyle = this.model.strokeColor; 
    // 计算圆形的半径,这里我们取正方形边框的较短边的一半作为半径 
    let radius = Math.min(frameWidthInVp, frameHeightInVp) / 2; 
    // 开始绘制路径 
    this.context.beginPath(); 
    // 使用 arc 方法绘制圆形 
    this.context.arc(centerX, centerY, radius, 0, 2 * Math.PI); 
    // 关闭路径 
    this.context.closePath(); 
    // 描绘圆形边框 
    this.context.lineWidth = 1; // 边框宽度 
    this.context.stroke(); 
  })

添加拖拽手势,调整获取图片的内容。

.gesture( 
  GestureGroup(GestureMode.Parallel, 
    // 拖动手势 
    PanGesture({}) 
      .onActionStart(() => { 
        hilog.info(0, "CropView", "Pan gesture start"); 
        this.startOffsetX = this.model.offsetX; 
        this.startOffsetY = this.model.offsetY; 
      }) 
      .onActionUpdate((event:GestureEvent) => { 
        hilog.info(0, "CropView", `Pan gesture update: ${JSON.stringify(event)}`); 
        if (event) { 
          if (this.model.panEnabled) { 
            let distanceX: number = this.startOffsetX + vp2px(event.offsetX) / this.model.scale; 
            let distanceY: number = this.startOffsetY + vp2px(event.offsetY) / this.model.scale; 
            this.model.offsetX = distanceX; 
            this.model.offsetY = distanceY; 
            this.updateMatrix() 
          } 
        } 
      }) 
      .onActionEnd(() => { 
        hilog.info(0, "CropView", "Pan gesture end"); 
        this.checkImageAdapt(); 
      }), 
 
    // 缩放手势处理 
    PinchGesture({ fingers: 2 }) 
      .onActionStart(() => { 
        this.tempScale = this.model.scale 
      }) 
      .onActionUpdate((event) => { 
        if (event) { 
          if (!this.model.zoomEnabled) return; 
          this.zoomTo(this.tempScale * event.scale); 
        } 
      }) 
      .onActionEnd(() => { 
        this.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}`) 
 
  // 图片适配控件的时候也进行了缩放,计算出这个缩放比例 
  let widthScale = this.model.componentWidth / this.model.imageWidth; 
  let heightScale = this.model.componentHeight / this.model.imageHeight; 
  let adaptScale = Math.min(widthScale, heightScale); 
  hilog.info(0, "CropView", `Image scale ${adaptScale} while attaching the component[${this.model.componentWidth}, ${this.model.componentHeight}]`) 
 
  // 经过两次缩放(适配控件、手势)后,图片的实际显示大小 
  let showWidth = this.model.imageWidth * adaptScale * this.model.scale; 
  let showHeight = this.model.imageHeight * adaptScale * this.model.scale; 
  let imageX = (this.model.componentWidth - showWidth) / 2; 
  let imageY = (this.model.componentHeight - showHeight) / 2; 
  hilog.info(0, "CropView", `Image left top is (${imageX}, ${imageY})`) 
 
  // 取景框的左上角坐标 
  let frameX = (this.model.componentWidth - this.model.frameWidth) / 2; 
  let frameY = (this.model.componentHeight - this.model.getFrameHeight()) / 2; 
 
  // 图片左上角坐标 
  let showX = imageX + offsetX * scale; 
  let showY = imageY + offsetY * scale; 
  hilog.info(0, "CropView", `Image show at (${showX}, ${showY})`) 
 
  if(this.model.frameWidth > showWidth || this.model.getFrameHeight() > showHeight) { // 图片缩放后,大小不足以填满取景框 
    let xScale = this.model.frameWidth / showWidth; 
    let yScale = this.model.getFrameHeight() / showHeight; 
    let newScale = Math.max(xScale, yScale); 
    this.model.scale = this.model.scale * newScale; 
    showX *= newScale; 
    showY *= newScale; 
  } 
 
  // 调整x轴方向位置,使图像填满取景框 
  if(showX > frameX) { 
    showX = frameX; 
  } else if(showX + showWidth < frameX + this.model.frameWidth) { 
    showX = frameX + this.model.frameWidth - showWidth; 
  } 
  // 调整y轴方向位置,使图像填满取景框 
  if(showY > frameY) { 
    showY = frameY; 
  } else if(showY + showHeight < frameY + this.model.getFrameHeight()) { 
    showY = frameY + this.model.getFrameHeight() - showHeight; 
  } 
  this.model.offsetX = (showX - imageX) / scale; 
  this.model.offsetY = (showY - imageY) / scale; 
  this.updateMatrix(); 
}

根据取景框的内缩放图片的位置大小,生成新pixelMap数据。

public async crop(format: image.PixelMapFormat) : Promise<image.PixelMap> { 
  if(!this.src || this.src == '') { 
  throw new Error('Please set src first'); 
} 
if(this.imageWidth == 0 || this.imageHeight == 0) { 
  throw new Error('The image is not loaded'); 
} 
// 图片适配控件的时候也进行了缩放,计算出这个缩放比例 
let widthScale = this.componentWidth / this.imageWidth; 
let heightScale = this.componentHeight / this.imageHeight; 
let adaptScale = Math.min(widthScale, heightScale); 
// 经过两次缩放(适配控件、手势)后,图片的实际显示大小 
let totalScale = adaptScale * this.scale; 
let showWidth = this.imageWidth * totalScale; 
let showHeight = this.imageHeight * totalScale; 
let imageX = (this.componentWidth - showWidth) / 2; 
let imageY = (this.componentHeight - showHeight) / 2; 
// 取景框的左上角坐标 
let frameX = (this.componentWidth - this.frameWidth) / 2; 
let frameY = (this.componentHeight - this.getFrameHeight()) / 2; 
// 图片左上角坐标 
let showX = imageX + this.offsetX * this.scale; 
let showY = imageY + this.offsetY * this.scale; 
let x = (frameX - showX) / totalScale; 
let y = (frameY - showY) / totalScale; 
let file = fs.openSync(this.src, fs.OpenMode.READ_ONLY) 
let imageSource : image.ImageSource = image.createImageSource(file.fd); 
let decodingOptions : image.DecodingOptions = { 
  editable: true, 
  desiredPixelFormat: image.PixelMapFormat.BGRA_8888, 
} 
// 创建pixelMap 
let pm = await imageSource.createPixelMap(decodingOptions); 
let cp = await this.copyPixelMap(pm); 
pm.release(); 
let region: image.Region = { x: x, y: y, size: { width: this.frameWidth / totalScale, height: this.getFrameHeight() / totalScale } }; 
cp.cropSync(region); 
return 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); 
} 
}

场景二:对头像添加VIP标识或其他标签,添加lottie动画,以及直播头像

1.头像添加身份标识。

实现:主要以头像图片和标签图片叠加实现此效果

Row() { 
  Image($r('app.media.imageHead')) 
    .width(100).height(100) 
    .borderRadius(50) 
    .borderWidth(2) 
    .borderColor(Color.Orange) 
  Image($r('app.media.app_icon')) 
    .height(16).width(16) 
    .borderRadius(8) 
    .position({ x: 75, y: 75 }) 
} 
 .width(100) 
.height(100)

2.直播类应用头像添加lottie动画。

实现:在头像上叠加一个对应的lottie动画文件,来实现此效果。

1)lottie是一个适用于HarmonyOS的动画库,它可以解析json格式的动画,并在移动设备上进行本地渲染。

2)下载安裝:ohpm install @ohos/lottie。

3)相对路径加载,项目内entry/src/main/ets文件夹下创建和pages同级的目录common,将需要播放的json文件放在目录common下。

13.png

4)path路径加载只支持文件夹下的相对路径,不能使用./或者../的相对路径,会导致动画加载不出来,正确格式:path: 'common/lottie/grunt.json'。

Button('播放') 
  .onClick(() => { 
    if (this.animateItem === null) { 
      this.animateItem = lottie.loadAnimation({ 
        container: this.mainCanvasRenderingContext, 
        renderer: 'canvas', // canvas 渲染模式 
        loop: 10, 
        autoplay: true, 
        name: this.animateName, 
        contentMode: 'Contain', 
        path: 'common/lottie/grunt.json', //路径加载动画只支持entry/src/main/ets 文件夹下的相对路径 
      }) 
    } 
  }

3.头像水波纹动画实现:

1)实现水波纹动画,设置2个长度为3数组,sacleList主要控制缩放,每次加0.1的倍数,opacityList控制透明度渐变效果,每次递减0.1倍。

2)使用显式动画 (animateTo)在onAppear中,执行2组数组中设置的缩放和透明度的动画系数,来达到水波纹的缩放和渐变效果。

.scale({ x: this.scaleList[index], y: this.scaleList[index] }) 
  .onAppear(() => { 
    animateTo({ duration: 1000, iterations: -1 }, () => { 
      //iterations: -1 
      //每个圆缩放系数+0.1 
      this.scaleList[index] = this.cloneScaleList[index] + this.scaleRatio 
      //每个圆透明度-0.1 
      this.opacityList[index] = this.cloneOpacityList[index] -  0.1 
    }) 
  })

4.头像缩放动画实现

1)头像缩放动画,使用关键帧动画 (keyframeAnimateTo)来实现。

2)第一帧将头像从1.0缩小到0.9,第二帧将头像从0.9放大到1.0,动画播放次数iterations设置为-1,无限次播放。

Image($r('app.media.app_icon')).width(100).borderRadius(50) 
  .scale({ x: this.scaleN, y: this.scaleN}) 
  .onAppear(()=>{ 
    this.scaleN = 1.0; 
    this?.uiContext?.keyframeAnimateTo({ iterations: -1 }, [ 
      { 
        // 第一段关键帧动画时长为1000ms,scale属性做从1到0.9的动画 
        duration: 1000, 
        event: () => { 
          this.scaleN = 0.9; 
        } 
      }, 
      { 
        // 第二段关键帧动画时长为1000ms,scale属性做从0.9到1的动画 
        duration: 1000, 
        event: () => { 
          this.scaleN = 1; 
        } 
      } 
    ]) 
  }) 
}

3)整体效果代码如下:

build() { 
  Column({ space: 5 }) { 
    Stack() { 
      ForEach(this.scaleList, (item: number, index: number) => { 
        Column() { 
        } 
        .width(100) 
        .height(100) 
        .borderRadius(50) 
        .backgroundColor(Color.Red) 
        .opacity(this.opacityList[index]) 
        .scale({ x: this.scaleList[index], y: this.scaleList[index] }) 
        .onAppear(() => { 
          animateTo({ duration: 1000, iterations: -1 }, () => { 
            // iterations: -1 
            // 每个圆缩放系数+0.1 
            this.scaleList[index] = this.cloneScaleList[index] + this.scaleRatio 
            // 每个圆透明度-0.1 
            this.opacityList[index] = this.cloneOpacityList[index] -  0.1 
          }) 
        }) 
      }, (item: number, index: number) => index.toString()) 
 
      Row(){ 
        Image($r('app.media.app_icon')).width(100).borderRadius(50) 
          .scale({ x: this.scaleN, y: this.scaleN}) 
          .onAppear(()=>{ 
            this.scaleN = 1.0; 
            this?.uiContext?.keyframeAnimateTo({ iterations: -1 }, [ 
              { 
                // 第一段关键帧动画时长为1000ms,scale属性做从1到0.9的动画 
                duration: 1000, 
                event: () => { 
                  this.scaleN = 0.9; 
                } 
              }, 
              { 
                // 第二段关键帧动画时长为1000ms,scale属性做从0.9到1的动画 
                duration: 1000, 
                event: () => { 
                  this.scaleN = 1; 
                } 
              } 
            ]) 
          }) 
      } 
      .height(100).width(100).borderRadius(50) 
      .backgroundColor(Color.Blue) 
    } 
  }.height('100%').width('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center) 
}

场景三:使用canvas实现群头像功能

1.使获取单个头像图片,转换成ImageBitmap。

private img:ImageBitmap = new ImageBitmap("common/images/imageHeadtwo.png")

2.通过CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象直接调用drawImage进行绘制

其中的四个参数,表示以画布左上角坐标起点,前2个表示x,y坐标位置,后2个参数表示在画布上绘制的宽高。

3.头像个数3-9张,并且根据图片的数量,计算每种情况下图片所在画布位置,自动调整布局,确保每个图片都是正方形,且有统一的边框和间距。

4.根据API:getPixelMap,根据画布起始点位置,和自己想要获取画布内容大小,来获取对应的pixelMap数据。

5.拿到pixelMap数据后,可以用image图片来展示。

群成员数量不同,画布上对应的群成员头像所在的坐标不同,这里以3-9张为例代码如下:

属性介绍

//群成员数量  
private imgCount:number = 9;  
//画布宽度/长度  
private canvasW:number = 200;  
//头像间距  
private itemGap:number = 10;

多情况头像布局

if (this.imgCount == 3) { 
  let imageW = (this.canvasW - this.itemGap) / 2; 
  this.context.drawImage(this.img, (this.canvasW - imageW) / 2, 0, imageW, imageW) 
  this.context.drawImage(this.img, 0, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, imageW + this.itemGap, imageW, imageW) 
}else  if (this.imgCount == 4){ 
  let imageW = (this.canvasW - this.itemGap) / 2; 
  this.context.drawImage(this.img, 0, 0, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, 0, imageW, imageW) 
  this.context.drawImage(this.img, 0, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, imageW + this.itemGap, imageW, imageW) 
}else  if (this.imgCount == 5){ 
  let imageW = (this.canvasW - this.itemGap * 2) / 3; 
  let startX = (this.canvasW - imageW * 2 - this.itemGap) / 2; 
  this.context.drawImage(this.img, startX, startX, imageW, imageW) 
  this.context.drawImage(this.img, startX + imageW + this.itemGap, startX, imageW, imageW) 
  this.context.drawImage(this.img, 0, startX + imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, startX + imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 2 * (imageW + this.itemGap), startX + imageW + this.itemGap, imageW, imageW) 
}else  if (this.imgCount == 6){ 
  let imageW = (this.canvasW - this.itemGap * 2) / 3; 
  let startX = (this.canvasW - imageW * 2 - this.itemGap) / 2; 
  this.context.drawImage(this.img, 0, startX, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, startX, imageW, imageW) 
  this.context.drawImage(this.img, 2 * (imageW + this.itemGap), startX, imageW, imageW) 
  this.context.drawImage(this.img, 0, startX + imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, startX + imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 2 * (imageW + this.itemGap), startX + imageW + this.itemGap, imageW, imageW) 
}else  if (this.imgCount == 7){ 
  let imageW = (this.canvasW - this.itemGap * 2) / 3; 
  let startX = (this.canvasW - imageW) / 2; 
  this.context.drawImage(this.img, startX, 0, imageW, imageW) 
  this.context.drawImage(this.img, 0, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 0, 2 * ( imageW + this.itemGap), imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, 2 * ( imageW + this.itemGap), imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), 2 * ( imageW + this.itemGap), imageW, imageW) 
}else  if (this.imgCount == 8){ 
  let imageW = (this.canvasW - this.itemGap * 2) / 3; 
  let startX = (this.canvasW - imageW * 2 - this.itemGap) / 2; 
  this.context.drawImage(this.img, startX, 0, imageW, imageW) 
  this.context.drawImage(this.img, startX + imageW + this.itemGap, 0, imageW, imageW) 
  this.context.drawImage(this.img, 0, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 0, 2 * ( imageW + this.itemGap), imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, 2 * ( imageW + this.itemGap), imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), 2 * ( imageW + this.itemGap), imageW, imageW) 
}else  if (this.imgCount == 9){ 
  let imageW = (this.canvasW - this.itemGap * 2) / 3; 
  this.context.drawImage(this.img, 0, 0, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, 0, imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), 0, imageW, imageW) 
  this.context.drawImage(this.img, 0, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), imageW + this.itemGap, imageW, imageW) 
  this.context.drawImage(this.img, 0, 2 * ( imageW + this.itemGap), imageW, imageW) 
  this.context.drawImage(this.img, imageW + this.itemGap, 2 * ( imageW + this.itemGap), imageW, imageW) 
  this.context.drawImage(this.img, 2 * ( imageW + this.itemGap), 2 * ( imageW + this.itemGap), imageW, imageW) 
}

实现:以9张头像为例,代码实现如下

Canvas(this.context) 
  .width('200') 
  .height('200') 
  .backgroundColor('#F5DC62') 
  .onReady(() => { 
    this.context.drawImage(this.img, 0, 0, 60, 60) 
    this.context.drawImage(this.img, 70, 0, 60, 60) 
    this.context.drawImage(this.img, 140, 0, 60, 60) 
    this.context.drawImage(this.img, 0, 70, 60, 60) 
    this.context.drawImage(this.img, 70, 70, 60, 60) 
    this.context.drawImage(this.img, 140, 70, 60, 60) 
    this.context.drawImage(this.img, 0, 140, 60, 60) 
    this.context.drawImage(this.img, 70, 140, 60, 60) 
    this.context.drawImage(this.img, 140, 140, 60, 60) 
    let pixelmap = this.context.getPixelMap(0, 0, 200, 200) 
    this.headImg = pixelmap; 
    this.context.setPixelMap(pixelmap) 
  }) 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值