项目需求是需要做一个上传图片按1:1,16:9 ,3:4一个图片截取,网上查了好久,找到一个cropper截取组件 ,现在把接入以及修改步骤记录一下
截取组件原地址找不到了,贴一下代码吧(谢谢大佬分享了!!!)
wxml
<view class="wx-cropper-info" wx:if="{{isShowCropper}}">
<view class='cropper-content'>
<view class="wx-corpper" style="width:{{cropperInitW}}rpx;height:{{cropperInitH}}rpx;background:#000">
<view class="wx-corpper-content" style="width:{{cropperW}}rpx;height:{{cropperH}}rpx;left:{{cropperL}}rpx;top:{{cropperT}}rpx">
<image class="img" src="{{imageSrc}}" style="width:{{cropperW}}rpx;height:{{cropperH}}rpx"></image>
<view class="wx-corpper-crop-box" bindtouchstart="contentStartMove" bindtouchmove="contentMoveing"
bindtouchend="contentTouchEnd" style="left:{{cutL}}rpx;top:{{cutT}}rpx;right:{{cutR}}rpx;bottom:{{cutB}}rpx">
<view class="wx-cropper-view-box">
<view class="wx-cropper-dashed-h"></view>
<view class="wx-cropper-dashed-v"></view>
<!-- catchtouchmove="dragMove" -->
<view class="wx-cropper-line-t" data-drag="top" catchtouchstart="dragStart" catchtouchmove="dragMove"></view>
<view class="wx-cropper-line-r" data-drag="right" catchtouchstart="dragStart"
catchtouchmove="dragMove"></view>
<view class="wx-cropper-line-b" data-drag="bottom" catchtouchstart="dragStart"
catchtouchmove="dragMove"></view>
<view class="wx-cropper-line-l" data-drag="left" catchtouchstart="dragStart" catchtouchmove="dragMove"></view>
<view class="wx-cropper-point point-t" data-drag="top" catchtouchstart="dragStart"
catchtouchmove="dragMove"></view>
<view class="wx-cropper-point point-tr" data-drag="topTight"></view>
<view class="wx-cropper-point point-r" data-drag="right" catchtouchstart="dragStart"
catchtouchmove="dragMove"></view>
<view class="wx-cropper-point point-rb" data-drag="rightBottom" catchtouchstart="dragStart"
catchtouchmove="dragMove"></view>
<view class="wx-cropper-point point-b" data-drag="bottom" catchtouchstart="dragStart"
catchtouchmove="dragMove" catchtouchend="dragEnd"></view>
<view class="wx-cropper-point point-bl" data-drag="bottomLeft"></view>
<view class="wx-cropper-point point-l" data-drag="left" catchtouchstart="dragStart"
catchtouchmove="dragMove"></view>
<view class="wx-cropper-point point-lt" data-drag="leftTop"></view>
</view>
</view>
</view>
</view>
</view>
<view class="cropper-configs" wx:if="{{!isDefault}}">
<button type="primary reverse" class="btn" bindtap="changeSome" data-index="1" style='margin-top: 30rpx;'> 1:1 </button>
<button type="primary reverse" class="btn" bindtap="changeSome" data-index="0.75"style='margin-top: 30rpx;'> 3:4 </button>
<button type="primary reverse" class="btn" bindtap="changeSome" data-index="0.5625" style='margin-top: 30rpx;'> 9:16 </button>
</view>
<view class='cropper-config'>
<button type="primary reverse" class="btn" bindtap="cancelCropper" style='margin-top: 30rpx;'> 取消 </button>
<button type="primary" class="cropper-confirm btn" bindtap="confirmCropper" style='margin-top: 30rpx;'> 确认 </button>
</view>
<canvas wx:if="{{!isCircleCrop}}" canvas-id="cropper" style="position:absolute;border: 1px solid red; width:{{qualityWidth}}px;height:{{qualityWidth/innerAspectRadio}}px;top:-9999px;left:-9999px;"></canvas>
<canvas wx:else canvas-id="cropper" style="position:absolute;border: 1px solid red; width:{{canvasW}}px;height:{{canvasH}}px;top:-9999px;left:-9999px;"></canvas>
</view>
js
const defaultData = {
isShowCropper:false,
// 初始化的宽高
cropperInitW: 750,
cropperInitH: 750,
// 动态的宽高
cropperW: 750,
cropperH: 750,
// 动态的left top值
cropperL: 0,
cropperT: 0,
transL: 0,
transT: 0,
// 图片缩放值
scaleP: 0,
imageW: 0,
imageH: 0,
// 裁剪框 宽高
cutL: 0,
cutT: 0,
cutB: 0,
cutR: 0,
qualityWidth: '',
innerAspectRadio: 750 / wx.getSystemInfoSync().windowWidth,
C_CONSTANTS:{
SCREEN_WIDTH : 750,
PAGE_X:0, // 手按下的x位置
PAGE_Y:0, // 手按下y的位置
PR : wx.getSystemInfoSync().pixelRatio, // dpi
T_PAGE_X:{}, // 手移动的时候x的位置
T_PAGE_Y:{}, // 手移动的时候Y的位置
CUT_L:0, // 初始化拖拽元素的left值
CUT_T:0, // 初始化拖拽元素的top值
CUT_R:0, // 初始化拖拽元素的
CUT_B:0, // 初始化拖拽元素的
CUT_W:0, // 初始化拖拽元素的宽度
CUT_H:0, // 初始化拖拽元素的高度
IMG_RATIO:0, // 图片比例
IMG_REAL_W:0, // 图片实际的宽度
IMG_REAL_H:0, // 图片实际的高度
IMG_TYPE:'',//图片的格式
DRAFG_MOVE_RATIO : 750 / wx.getSystemInfoSync().windowWidth //移动时候的比例
}
};
let data = {};
try{
data = JSON.parse(JSON.stringify(defaultData));
}catch(e){console.log(e)};
Component({
properties: {
imageSrc:{
type:String,
value:'',
observer(newVal, oldVal) {
if(newVal !== oldVal){
this.setData({
isShowCropper:true
},() => {
this.loadImage();
})
}
}
},
isCircleCrop:{
type:Boolean,
value:false
},
isDefault: {
type: Boolean,
value: false
},
enableScale:{
type:Boolean,
value:false
},
ratio:{
type:Number,
value:1,
}
},
observers:{
ratio(val) {
let that = this;
console.log(val,'1111')
that.loadImage()
}
},
data,
ready(){
if(this.data.isCircleCrop){
//圆形裁剪 强制比例为1
this.setData({
ratio:1
})
}
},
methods: {
// 修改裁剪比例
changeSome(e){
let that = this;
// console.log(e.currentTarget.dataset.index)
that.triggerEvent('changeScale', {
'scale': e.currentTarget.dataset.index
})
// that.setData({
// ratio:e.currentTarget.dataset.index,
// })
},
loadImage() {
let {ratio,imageSrc} = this.data;
let {IMG_REAL_W,IMG_REAL_H,IMG_RATIO,SCREEN_WIDTH,IMG_TYPE} = this.data.C_CONSTANTS;
wx.getImageInfo({
src: imageSrc,
success: res => {
IMG_REAL_W = res.width;
IMG_REAL_H = res.height;
IMG_RATIO = IMG_REAL_W / IMG_REAL_H;
IMG_TYPE = res.type === 'png' ? 'png' : 'jpg';
// 根据图片的宽高显示不同的效果 保证图片可以正常显示
let temp = {};
let cropperData = {};
if (IMG_RATIO >= 1) {
cropperData = {
cropperW: SCREEN_WIDTH,
cropperH: SCREEN_WIDTH / IMG_RATIO,
// 初始化left right
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2)
}
if(ratio > 1){
temp = {
cutL: (SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2,
cutT: (SCREEN_WIDTH / IMG_RATIO - SCREEN_WIDTH / IMG_RATIO / ratio ) / 2,
cutR: SCREEN_WIDTH - (SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2 - SCREEN_WIDTH / IMG_RATIO,
cutB: SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - SCREEN_WIDTH / IMG_RATIO / ratio ) / 2 - SCREEN_WIDTH / IMG_RATIO / ratio
}
}else{
temp = {
cutT:0,
cutB:0,
cutL:(SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO * ratio) / 2,
cutR: SCREEN_WIDTH - (SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO * ratio) / 2 - SCREEN_WIDTH / IMG_RATIO * ratio
}
}
} else {
cropperData = {
cropperW: SCREEN_WIDTH * IMG_RATIO,
cropperH: SCREEN_WIDTH,
// 初始化left right
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2)
}
if(ratio > 1){
temp = {
cutL:0,
cutR:0,
cutT: (SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO / ratio) / 2,
cutB: SCREEN_WIDTH - (SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO / ratio) / 2 - SCREEN_WIDTH * IMG_RATIO / ratio
}
}else{
temp = {
cutL: (SCREEN_WIDTH * IMG_RATIO - SCREEN_WIDTH * IMG_RATIO * ratio) / 2,
cutR: SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO - SCREEN_WIDTH * IMG_RATIO * ratio) / 2 - SCREEN_WIDTH * IMG_RATIO * ratio,
cutT: (SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2,
cutB: SCREEN_WIDTH - (SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2 - SCREEN_WIDTH * IMG_RATIO
}
}
}
this.setData({
C_CONSTANTS:Object.assign({},this.data.C_CONSTANTS,{
IMG_REAL_W,
IMG_REAL_H,
IMG_RATIO,
IMG_TYPE
}),
isShowCropper: true,
// 图片缩放值
scaleP: IMG_REAL_W / SCREEN_WIDTH,
qualityWidth: IMG_REAL_W,
innerAspectRadio: IMG_RATIO,
...temp,
...cropperData
})
}
});
},
contentStartMove(e) {
this.setData({
'C_CONSTANTS.PAGE_X':e.touches[0].pageX,
'C_CONSTANTS.PAGE_Y':e.touches[0].pageY
})
},
// 拖动时候触发的touchMove事件
contentMoveing(e) {
let {PAGE_X,PAGE_Y,DRAFG_MOVE_RATIO} = this.data.C_CONSTANTS;
let {cutL,cutR,cutT,cutB} = this.data;
let dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
let dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
// 左移
if (dragLengthX > 0) {
if (cutL - dragLengthX < 0) dragLengthX = cutL
} else {
if (cutR + dragLengthX < 0) dragLengthX = -cutR
}
if (dragLengthY > 0) {
if (cutT - dragLengthY < 0) dragLengthY = cutT
} else {
if (cutB + dragLengthY < 0) dragLengthY = -cutB
}
this.setData({
cutL: cutL - dragLengthX,
cutT: cutT - dragLengthY,
cutR: cutR + dragLengthX,
cutB: cutB + dragLengthY,
'C_CONSTANTS.PAGE_X':e.touches[0].pageX,
'C_CONSTANTS.PAGE_Y':e.touches[0].pageY
});
},
// 设置大小的时候触发的touchStart事件
dragStart(e) {
let {cutL,cutR,cutT,cutB} = this.data;
this.setData({
C_CONSTANTS:Object.assign({},this.data.C_CONSTANTS,{
T_PAGE_X : e.touches[0].pageX,
T_PAGE_Y : e.touches[0].pageY,
CUT_L : cutL,
CUT_R : cutR,
CUT_B : cutB,
CUT_T : cutT
})
})
},
// 设置大小的时候触发的touchMove事件
dragMove(e) {
let dragType = e.target.dataset.drag
let {ratio,cropperW,cropperH,cutL,cutT,cutR,cutB,enableScale} = this.data;
let {CUT_R,CUT_L,CUT_T,CUT_B,T_PAGE_X,T_PAGE_Y,DRAFG_MOVE_RATIO} = this.data.C_CONSTANTS;
let dragLength;
switch (dragType) {
case 'right':
dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
if (CUT_R + dragLength < 0) dragLength = -CUT_R
cutR = CUT_R + dragLength;
if(enableScale){
cutT = CUT_T + dragLength / ratio;
}
if(cutR < 0 || cutT < 0 || cutT > cropperH || cutR > cropperW) return;
break;
case 'left':
dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
if (CUT_L - dragLength < 0) dragLength = CUT_L
if ((CUT_L - dragLength) > (this.data.cropperW - this.data.cutR)) dragLength = CUT_L - (this.data.cropperW - this.data.cutR)
cutL = CUT_L - dragLength;
if(enableScale){
cutT = CUT_T - dragLength / ratio;
}
if(cutL < 0 || cutT < 0 || cutT > cropperH || cutL > cropperW) return;
break;
case 'top':
dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
if (CUT_T - dragLength < 0) dragLength = CUT_T
if ((CUT_T - dragLength) > (this.data.cropperH - this.data.cutB)) dragLength = CUT_T - (this.data.cropperH - this.data.cutB)
cutT = CUT_T - dragLength;
if(enableScale){
cutR = CUT_R - dragLength * ratio;
}
break;
case 'bottom':
dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
if (CUT_B + dragLength < 0) dragLength = -CUT_B
cutB = CUT_B + dragLength;
if(enableScale){
cutR = CUT_R + dragLength * ratio;
}
if(cutR < 0 || cutT < 0 || cutT > cropperH || cutR > cropperW) return;
break;
default:'';
}
this.setData({
cutL,
cutT,
cutR,
cutB
});
},
contentTouchEnd(){},
// 获取图片
confirmCropper() {
const isCircleCrop = this.data.isCircleCrop;
if(isCircleCrop){
this.circleCrop()
}else{
this.normalCropper();
}
},
normalCropper(){
let {imageSrc,cropperW,cropperH,cutL,cutT,cutR,cutB} = this.data;
let {IMG_REAL_W,IMG_REAL_H,IMG_TYPE} = this.data.C_CONSTANTS;
// 将图片写入画布
const ctx = wx.createCanvasContext('cropper',this)
ctx.drawImage(imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
ctx.draw(true, () => {
// 获取画布要裁剪的位置和宽度 均为百分比 * 画布中图片的宽度 保证了在微信小程序中裁剪的图片模糊 位置不对的问题 canvasT = (_this.data.cutT / _this.data.cropperH) * (_this.data.imageH / pixelRatio)
let canvasW = ((cropperW - cutL - cutR) / cropperW) * IMG_REAL_W
let canvasH = ((cropperH - cutT - cutB) / cropperH) * IMG_REAL_H
let canvasL = (cutL / cropperW) * IMG_REAL_W
let canvasT = (cutT / cropperH) * IMG_REAL_H
wx.canvasToTempFilePath({
x: canvasL,
y: canvasT,
width: canvasW,
height: canvasH,
destWidth: canvasW,
destHeight: canvasH,
fileType:IMG_TYPE || 'jpg',
canvasId: 'cropper',
success: (res) => {
//图片裁剪成功
this.cancelCropper();
this.triggerEvent('cropperDone', {
src:res.tempFilePath,
cropperData:{
x: canvasL,
y: canvasT,
width: canvasW,
height: canvasH
}
})
},
fail:err =>{
this.triggerEvent('cropperFail',err)
}
},this);
})
},
circleCrop(){
console.log('圆角切割')
let {imageSrc,cropperW,cropperH,cutL,cutT,cutR,cutB} = this.data;
let {IMG_REAL_W,IMG_REAL_H,IMG_TYPE} = this.data.C_CONSTANTS;
// 将图片写入画布
const ctx = wx.createCanvasContext('cropper',this)
let canvasW = ((cropperW - cutL - cutR) / cropperW) * IMG_REAL_W
let canvasL = (cutL / cropperW) * IMG_REAL_W
let canvasT = (cutT / cropperH) * IMG_REAL_H
this.setData({
canvasW:canvasW,
canvasH:canvasW
},() => {
ctx.arc(canvasW / 2,canvasW / 2,canvasW / 2,0,2 * Math.PI);
ctx.clip();
ctx.drawImage(imageSrc, canvasL, canvasT, canvasW, canvasW,0,0,canvasW,canvasW);
ctx.draw(true, () => {
wx.canvasToTempFilePath({
fileType:IMG_TYPE || 'jpg',
canvasId: 'cropper',
success: (res) => {
//图片裁剪成功
this.cancelCropper();
this.triggerEvent('cropperDone', {
src:res.tempFilePath,
cropperData:{
x: canvasL,
y: canvasT,
width: canvasW,
height: canvasW
}
})
},
fail:err =>{
this.triggerEvent('cropperFail',err)
}
},this);
})
})
},
cancelCropper(){
let originData = {}
try{
originData = JSON.parse(JSON.stringify(defaultData))
}catch(e){};
this.setData({
...originData
});
this.triggerEvent('cropperCancel')
}
}
})
1.引入组件
//.json文件
"usingComponents": {
"cropper":"../../components/cropper"
}
//.wxml文件
<!-- 图片截取 -->
<view class="btn" bindtap="choiceImageCropper">冲冲冲</view>
<cropper imageSrc="{{imageSrc}}" enableScale="{{true}}" isDefault="{{}}" isCircleCrop="{{false}}"
ratio="{{proportion}}" bind:changeScale='changeScale' bind:cropperDone="cropperDone"
bind:cropperCancel="cropperCancel" bind:cropperFail="cropperFail"></cropper>
<!--
imageSrc:需要截取的图片地址
enableScale:是否限制比例,true为用户不能随意调整比例
isDefault:是否限定默认尺寸为1:1且不显示其他比例选项 默认flase
isCircleCrop:是否显示圆角切割 (用于切割头像)
ratio:为传入截取比例尺(组件默认为1)
changeScale:组件传递比例事件
cropperDone:用户确认截取图片事件
cropperCancel:用户取消截取
cropperFail:截取报错
-->
//
2.js事件
//组件触发取消截取方法
cropperCancel(e){
console.log('用户截取取消了')
},
// 组件触发截取确认方法
cropperDone(e) {
console.log(e);
let that = this;
that.setData({
src:e.detail.src
})
},
// 修改比例
changeScale(e) {
let that = this;
that.setData({
proportion: e.detail.scale,
})
},
// 上传需要裁剪图片
choiceImageCropper(){
let that = this;
wx.chooseImage({
count: 1,
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success(res) {
console.log(res);
that.setData({
imageSrc:res.tempFilePaths[0]
})
},
fail(err) {}
})
},
以上代码贴下来可以直接使用,也可根据自身需求去添加功能及修改,组件有一点问题就是切圆形头像的时候截取框还是矩形不是圆形,但是截取出来的还是圆形的图片的,因为项目不需要截取圆形图片就没做修改,代码也添加了项目需求的一部分功能,总的来说这个组件还是挺好的。
希望能帮到大家,也便于自己记录(如果有帮助到大家可以点下赞哦~)!!!
如有疑问或者不对的地方可以下方评论留言讨论哦~会积极回复大家的!!!