首页IT科技小程序 截图(微信小程序图片裁剪功能的实现)

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

时间2025-08-04 02:03:05分类IT科技浏览9491
导读:在之前的博文中,已经介绍了如何使用在前端开发中,实现较方便自由的图片裁剪功能,可见博文:...

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

如何一步步实现图片裁剪功能                。

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

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

图片上传与处理

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

本博之前关于小程序的图片审核的文章:小程序图片上传与内容安全审核                         ,已介绍了使用 wx.chooseMedia 接口来选择图片文件        。

除此外                          ,还可以有其他方式,如 wx.chooseMessageFile 接口可以从聊天记录里选择图片                ,我们可以综合处理如下: function selectPhoto (callBack) { let itemList = [ 拍照, 从手机相册选择, 从聊天记录选择 ] wx.showActionSheet({ itemList, success: (action) => { if (action.tapIndex === 0) { // 从拍照选择图片                          ,使用 chooseMedia wx.chooseMedia({ sourceType: [ camera ] }) } else if (action.tapIndex === 1) { // 从手机相册选择图片         ,使用 chooseMedia wx.chooseMedia({ sourceType: [ album ] }) } else if (action.tapIndex === 2) { // 从聊天记录选择图片                ,使用 chooseMessageFile wx.chooseMessageFile({}) } } }) }

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

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

图片尺寸适配

由于要在前端对图片进行裁剪                 ,使用canvas绘制图片也是不可少的,在绘制之前                         ,我们需要根据图片尺寸进行适配处理                 。

首先需要做的就是读取图片的尺寸大小                          ,使用小程序的 wx.getImageInfo 接口即可,它能够返回图片原始的宽高属性        。

接着                ,根据图片的宽高属性        、系统屏幕的宽高                          ,一起在小程序页面可见区域内设置图片的缩放显示: // 根据窗口大小和图片大小         ,设置图片显示尺寸以及缩放 // imgHeight 和 imgWidth 表示当前图片的宽高 // 设置图片显示面板的宽高 let panelW = sys.windowWidth - 20 let panelH = sys.windowHeight - 100 if (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 代码:

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

以上代码中        ,

.crop-content

外层增加图片面板                         ,图片资源直接使用 组件加载                 ,与外层面板宽高保持一致; .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: return 0 } }, getClipY (clipHeight) { switch (activeCorner) { case leftTop: case rightTop: return clipBoxBeforeScaleClipY + (clipBoxBeforeScaleHeight - clipHeight) case leftBottom: case rightBottom: return clipBoxBeforeScaleClipY default: return 0 } }, getScaleXWidthOffset (offsetW) { switch (activeCorner) { case leftTop: case leftBottom: return -offsetW case rightTop: case rightBottom: return offsetW default: return 0 } }, getScaleYHeightOffset (offsetH) { switch (activeCorner) { case rightBottom: case leftBottom: return offsetH case rightTop: case leftTop: return -offsetH default: return 0 } }, 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) // 裁剪框最小宽度36 let 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) // 裁剪框最小高度36 let 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元素:

<canvas id="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浏览器上        ,反之亦然                 。

区别可能只在于图片的加载和保存上                         ,可以使用小程序提供的多种内置接口方法                 ,能较方便的完成        。

上文也附上了大量的核心代码,根据这些代码已经基本可以还原裁剪功能                         ,如果需要完整的小程序图片裁剪代码                          ,可以访问下载:小程序图片裁剪源码下载
声明:本站所有文章,如无特殊说明或标注                ,均为本站原创发布                         。任何个人或组织                          ,在未征得本站同意时         ,禁止复制、盗用                 、采集                         、发布本站内容到任何网站        、书籍等各类媒体平台                 。如若本站内容侵犯了原著者的合法权益                ,可联系我们进行处理。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
stream流foreach添加到另一个list(总算给女盆友讲明白了,如何使用stream流的filter()操作) vue常用知识点(Vue的基础知识(二))