这篇文章主要介绍“JavaScript如何实现小程序图片裁剪功能”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JavaScript如何实现小程序图片裁剪功能”文章能帮助大家解决问题。
要做图片裁剪,首先要解决的是图片上传的事情,这块在微信环境下,比较好处理,微信小程序提供了多个文件或图片的操作接口,让我们可以很方便的完成这个步骤。
小程序中图片上传比较简单,直接使用已提供的接口 chooseMedia
,用于选择媒体文件(图片或视频):
wx.chooseMedia({
count,
mediaType: ['image'],
sizeType: [ 'original' ],
sourceType: [ 'album' ],
success: (imgRes) => {
callBack && callBack(imgRes)
},
fail: () => {}
})
除此外,还可以有其他方式,如 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" >
<image src="{{imagePath}}" class="pos-absolute" />
<view class="mask-bg" ></view>
<view class="clip-path" >
<image src="{{imagePath}}" class="pos-absolute" />
</view>
<view class="clip-box" bind:touchmove="touchmoveM" bind:touchstart="touchstartM" >
<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
外层增加图片面板,图片资源直接使用 <image>
组件加载,与外层面板宽高保持一致;
.mask-bg
增加一个遮罩层,作为非裁剪区域的蒙层,以黑灰色透明度实现效果;
.clip-path
于图片面板内部,设置图片的裁剪区域,这里使用的是CSS中的 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 id="main" canvasId="main" class="main-canvas" ></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),在小程序也是常见的功能。
图片保存到系统相册功能完成后,我们就实现了在小程序中进行图片剪裁的完整功能,包含加载图片、图片适配和裁剪框绘制、裁剪框拖动与缩放事件、图片导出和保存的过程。
关于“JavaScript如何实现小程序图片裁剪功能”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://juejin.cn/post/7216133455297855547