0


微信小程序图片裁剪功能的实现

文章目录

在之前的博文中,已经介绍了如何使用在前端开发中,实现较方便自由的图片裁剪功能,可见博文:
如何一步步实现图片裁剪功能。

本文将进一步讲述在微信小程序中,我们实现图片裁剪功能需要如何处理,文章末尾将附上小程序端图片裁剪完整源码的下载链接。

在小程序中进行图片裁剪的效果如下图所示:

在这里插入图片描述

图片上传与处理

要做图片裁剪,首先要解决的是图片上传的事情,这块在微信环境下,比较好处理,微信小程序提供了多个文件或图片的操作接口,让我们可以很方便的完成这个步骤。

本博之前关于小程序的图片审核的文章:小程序图片上传与内容安全审核,已介绍了使用

wx.chooseMedia

接口来选择图片文件。
除此外,还可以有其他方式,如

wx.chooseMessageFile

接口可以从聊天记录里选择图片,我们可以综合处理如下:

functionselectPhoto(callBack){let itemList =['拍照','从手机相册选择','从聊天记录选择']
  wx.showActionSheet({
    itemList,success:(action)=>{if(action.tapIndex ===0){// 从拍照选择图片,使用 chooseMedia
        wx.chooseMedia({sourceType:['camera']})}elseif(action.tapIndex ===1){// 从手机相册选择图片,使用 chooseMedia
        wx.chooseMedia({sourceType:['album']})}elseif(action.tapIndex ===2){// 从聊天记录选择图片,使用 chooseMessageFile
        wx.chooseMessageFile({})}}})}

其中,

showActionSheet

可以显示操作菜单,这个功能也比较常见,可以从拍照、相册、聊天记录里选择文件进行加载:

在这里插入图片描述
当前,我们获取到的是图片文件的临时路径,得到图片路径以后,接下来要做的事情,就是如何正确适配的显示出来。

图片尺寸适配

由于要在前端对图片进行裁剪,使用canvas绘制图片也是不可少的,在绘制之前,我们需要根据图片尺寸进行适配处理。
首先需要做的就是读取图片的尺寸大小,使用小程序的

wx.getImageInfo

接口即可,它能够返回图片原始的宽高属性。
接着,根据图片的宽高属性、系统屏幕的宽高,一起在小程序页面可见区域内设置图片的缩放显示:

// 根据窗口大小和图片大小,设置图片显示尺寸以及缩放// imgHeight 和 imgWidth 表示当前图片的宽高// 设置图片显示面板的宽高let panelW = sys.windowWidth -20let panelH = sys.windowHeight -100if(panelH / panelW >= imgHeight / imgWidth){
  panelH =parseInt(panelW * imgHeight / imgWidth)}else{
  panelW =parseInt(panelH * imgWidth / imgHeight)}// 图片在宽高上的缩放比,用于裁剪图片时计算图片实际尺寸
xScale = panelW / imgWidth
yScale = panelH / imgHeight
this.setData({
  imagePath,// 图片显示面板宽高panelWidth: panelW,panelHeight: panelH,// 裁剪框的宽高,初始时与图片面板一样clipWidth: panelW,clipHeight: panelH,// 裁剪的实际宽高croppingImageWidth: imgWidth,croppingImageHeight: imgHeight
})

通过以上代码和注释,我们知道了图片加载时,需要计算的几个宽高尺寸值,都是比较重要的。

图片显示与裁剪框

下面就可以在页面上显示图片,直接展示图片显示区域的

wxml

代码:

<viewwx:if="{{imagePath}}"class="crop-container"><viewclass="crop-content"style="width:{{panelWidth + 'px'}}; height:{{panelHeight + 'px'}};"><imagesrc="{{imagePath}}"class="pos-absolute"style="width:{{panelWidth + 'px'}}; height:{{panelHeight + 'px'}};left: 0;top:0;"/><viewclass="mask-bg"style="width:{{panelWidth + 'px'}}; height:{{panelHeight + 'px'}};left: 0;top: 0;"></view><viewclass="clip-path"style="width:{{clipWidth + 'px'}}; height:{{clipHeight + 'px'}}; left:{{clipX + 'px'}}; top:{{clipY + 'px'}};"><imagesrc="{{imagePath}}"class="pos-absolute"style="width:{{panelWidth + 'px'}}; height:{{panelHeight + 'px'}}; left:{{clipImgX + 'px'}}; top:{{clipImgY + 'px'}};"/></view><viewclass="clip-box"bind:touchmove="touchmoveM"bind:touchstart="touchstartM"style="width:{{clipWidth + 'px'}}; height:{{clipHeight + 'px'}}; left:{{clipX + 'px'}}; top:{{clipY + 'px'}};"><viewcapture-catch:touchmove="touchmove"capture-catch:touchstart="touchstart"data-id="leftTop"class="corner-area left-top"></view><viewcapture-catch:touchmove="touchmove"capture-catch:touchstart="touchstart"data-id="rightTop"class="corner-area right-top"></view><viewcapture-catch:touchmove="touchmove"capture-catch:touchstart="touchstart"data-id="rightBottom"class="corner-area right-bottom"></view><viewcapture-catch:touchmove="touchmove"capture-catch:touchstart="touchstart"data-id="leftBottom"class="corner-area left-bottom"></view></view></view></view>

以上代码中,

  • .crop-content 外层增加图片面板,图片资源直接使用 <image> 组件加载,与外层面板宽高保持一致;
  • .mask-bg 增加一个遮罩层,作为非裁剪区域的蒙层,以黑灰色透明度实现效果;
  • .clip-path 于图片面板内部,设置图片的裁剪区域,这里使用的是CSS中的 clip-path 属性,内部加载一张图片,作为裁剪区域的图片显示;clip-path 属性之前已有博文介绍,可以查看 clip-path属性
  • clip-box 设置裁剪框区域的四个corner角:左上、右上、右下和左下,里面四个元素对应这个四个角,可以对裁剪框进行缩放处理; 当然,我们也可以在各边的中间位置再加上操作区,那一共就是8个;
  • .clip-path 图片裁剪区与 clip-box 裁剪框需要设置位置信息,用于在移动的时候进行定位。

通过设置对应的图片元素、蒙层、裁剪框等等,我们就已经完成了图片裁剪的结构布局了,具体可见下图所示:

在这里插入图片描述
图片裁剪框设置完成后,我们需要处理的就是裁剪框的拖动与缩放事件处理了。

裁剪框的拖动与缩放

上面的

wxml

代码中,在裁剪框的元素部分,已经增加了

touchstart

touchmove

等事件,用于在处理拖动和缩放等操作。
当我们实现裁剪框的拖动,只需要如下两个事件:

touchstartM(event){const{ clipX, clipY }=this.data
  const{ pageX, pageY }= event.touches[0]// 获取鼠标点在裁剪框的内部位置信息
  clipBoxMoveInnerX = pageX - clipX
  clipBoxMoveInnerY = pageY - clipY
},touchmoveM(event){const{ pageX, pageY }= event.touches[0]const{ panelWidth, panelHeight, clipHeight, clipWidth }=this.data

  // 裁剪框不能脱离面板// X位置范围为 0 到 (面板宽度-裁剪框宽度)let clipX = pageX - clipBoxMoveInnerX
  clipX = Math.max(clipX,0)const panelX = panelWidth - clipWidth
  clipX = Math.min(clipX, panelX)// Y位置范围为 0 到 (面板高度-裁剪框高度)let clipY = pageY - clipBoxMoveInnerY
  clipY = Math.max(clipY,0)const panleY = panelHeight - clipHeight
  clipY = Math.min(clipY, panleY)// 裁剪框底图位置信息const clipImgX =0- clipX
  const clipImgY =0- clipY

  this.setData({
    clipX,
    clipY,
    clipImgX,
    clipImgY
  })}

以上代码,通过计算图片移动时的相对位移,重新计算裁剪框的新的位置信息,需要注意的是,移动时不要脱离外层的面板——即不能脱离图片区域,否则无效。

缩放的操作则相对复杂一些,需要计算位移移动的距离以及当前位置下的裁剪宽高数据,并且要对每个不同的corner角进行特殊处理,这块的完整代码和注释如下所示:

// 处理缩放动作中不同corner时的尺寸位置信息getClipX(clipWidth){switch(activeCorner){case'leftTop':case'leftBottom':return clipBoxBeforeScaleClipX +(clipBoxBeforeScaleWidth - clipWidth)case'rightTop':case'rightBottom':return clipBoxBeforeScaleClipX
    default:return0}},getClipY(clipHeight){switch(activeCorner){case'leftTop':case'rightTop':return clipBoxBeforeScaleClipY +(clipBoxBeforeScaleHeight - clipHeight)case'leftBottom':case'rightBottom':return clipBoxBeforeScaleClipY
    default:return0}},getScaleXWidthOffset(offsetW){switch(activeCorner){case'leftTop':case'leftBottom':return-offsetW
    case'rightTop':case'rightBottom':return offsetW
    default:return0}},getScaleYHeightOffset(offsetH){switch(activeCorner){case'rightBottom':case'leftBottom':return offsetH
    case'rightTop':case'leftTop':return-offsetH
    default:return0}},touchstart(event){const dragId = event.currentTarget.dataset.id
  const{ pageX, pageY }= event.touches[0]const{ clipX, clipY, clipHeight, clipWidth }=this.data

  // 设置缩放时临时变量初始化值
  activeCorner = dragId
  clipBoxBeforeScalePageX = pageX
  clipBoxBeforeScalePageY = pageY
  clipBoxBeforeScaleClipX = clipX
  clipBoxBeforeScaleClipY = clipY
  clipBoxBeforeScaleWidth = clipWidth
  clipBoxBeforeScaleHeight = clipHeight
},touchmove(event){const{ pageX, pageY }= event.touches[0]const{ panelWidth, panelHeight }=this.data

  // 缩放在X上的偏移const xWidthOffset =this.getScaleXWidthOffset(pageX - clipBoxBeforeScalePageX)// 裁剪框最小宽度36let clipWidth = Math.max(clipBoxBeforeScaleWidth + xWidthOffset,36)// 设置缩放最大宽度,放大时不能超过面板、缩小时不能超过初始裁剪框let tempPanelWidth = pageX > clipBoxBeforeScalePageX ? panelWidth - clipBoxBeforeScaleClipX : clipBoxBeforeScaleWidth + clipBoxBeforeScaleClipX
  // 设置裁剪框宽度
  clipWidth = Math.min(clipWidth, tempPanelWidth)// 缩放在Y上的偏移const yHeightOffset =this.getScaleYHeightOffset(pageY - clipBoxBeforeScalePageY)// 裁剪框最小高度36let clipHeight = Math.max(clipBoxBeforeScaleHeight + yHeightOffset,36)// 设置缩放最大高度,放大时不能超过面板、缩小时不能超过初始裁剪框let tempPanelHeight = pageY > clipBoxBeforeScalePageY ? panelHeight - clipBoxBeforeScaleClipY : clipBoxBeforeScaleHeight + clipBoxBeforeScaleClipY
  // 设置裁剪框高度
  clipHeight = Math.min(clipHeight, tempPanelHeight)// 裁剪框位置信息let clipX =this.getClipX(clipWidth)let clipY =this.getClipY(clipHeight)// 裁剪框底图位置信息let clipImgX =0- clipX
  let clipImgY =0- clipY

  this.setData({
    clipWidth,
    clipHeight,
    clipX,
    clipY,
    clipImgX,
    clipImgY,croppingImageWidth:parseInt(clipWidth / xScale),croppingImageHeight:parseInt(clipHeight / yScale)})}

至此,图片裁剪的功能基本完成了,能够加载图片、设置裁剪框、拖动和缩放裁剪框大小,计算裁剪图片的尺寸等等。
就剩下如何真正剪裁图片了。

增加canvas并裁剪图片

要剪裁图片,我们在小程序使用canvas画布组件来处理,在页面上增加一个canvas元素:

<canvasid="main"canvasId="main"class="main-canvas"style="width:{{croppingImageWidth + 'px'}}; height:{{croppingImageHeight + 'px'}}"></canvas>

由于这个canvas只是用来对图片进行裁剪操作,并不需要显示出来,我们可以把它定位到视觉窗口以外,不影响可操作的界面元素。
给画布设置图片裁剪所需要的宽高,通过在同等宽高下绘制图片,就能导出图片:

wx.showLoading({title:'正在裁剪...'})
preCtx.clearRect(0,0, imageWidth, imageHeight)const width = croppingImageWidth
const height = croppingImageHeight
const xPos = Math.abs(clipImgX / xScale)const yPos = Math.abs(clipImgY / yScale)// 绘制裁剪区内的图片到画布上
preCtx.drawImage(imagePath, xPos, yPos, width, height,0,0, width, height)
preCtx.save()
preCtx.restore()const that =this
preCtx.draw(false,function(){setTimeout(()=>{// 将画布导出为临时图片文件
    wx.canvasToTempFilePath({x:0,y:0,
      width,
      height,destWidth: width,destHeight: height,canvasId:'main',success:(canRes)=>{
        wx.hideLoading()
        that.initImage(width, height, canRes.tempFilePath)},fail(err){
        wx.hideLoading()
        console.log(err)}})},200)})

如上代码所示,通过裁剪图片的真实宽高以及相对位置信息,通过canvas的

drawImage

方法,把图片的裁剪区域的内容绘制到画布上,此时,该画布就对应一张裁剪后的图片,我们只需要把画布导出成图片文件即可。
导出画布,使用

wx.canvasToTempFilePath

方法,用于将画布导出为图片临时图片文件,这个图片文件可以重新加载或者进行导出。

保存图片到相册

以上过程,生成裁剪图片的临时文件后,就可以使用小程序提供的API,将图片文件保存到相册中。
只需要使用

wx.saveImageToPhotosAlbum

方法,专门用于将图片文件保存到系统相册,接收临时文件作为参数:

wx.saveImageToPhotosAlbum({filePath: imgSrc,success:()=>{
    wx.hideLoading()
    wx.vibrateShort()
    wx.showModal({content:'图片已保存到相册~',showCancel:false,confirmText:'好的',confirmColor:'#333'})}})

该方法简单方便,其中使用

wx.vibrateShort()

方法,作用是使手机发生较短时间的振动(15 ms),在小程序中也是常见的功能。
图片保存到系统相册功能完成后,我们就实现了在小程序中进行图片剪裁的完整功能,包含加载图片、图片适配和裁剪框绘制、裁剪框拖动与缩放事件、图片导出和保存的过程。

总结

本文详细讲述了在小程序中实现一个图片裁剪功能的过程,可以看出和在浏览器Web端的实现差别并不大。
在核心的图片适配、裁剪框绘制与缩放事件处理上,基本两边可以通用,在小程序中的实现逻辑可以完整在移到web浏览器上,反之亦然。
区别可能只在于图片的加载和保存上,可以使用小程序提供的多种内置接口方法,能较方便的完成。
上文也附上了大量的核心代码,根据这些代码已经基本可以还原裁剪功能,如果需要完整的小程序图片裁剪代码,可以访问下载:小程序图片裁剪源码下载


本文转载自: https://blog.csdn.net/jimojianghu/article/details/129408840
版权归原作者 前端江湖 所有, 如有侵权,请联系我们删除。

“微信小程序图片裁剪功能的实现”的评论:

还没有评论