微信小程序图片(头像)裁剪工具we-cropper含2d版-完整版
本教程基于 we-cropper v1.4.0,非本人同意禁止任何形的转载
效果图
一、非2d版
1.组件文件we-cropper.wxml
<template name="we-cropper">
<canvas
class="cropper"
disable-scroll="true"
bindtouchstart="touchStart"
bindtouchmove="touchMove"
bindtouchend="touchEnd"
style="width:{{width}}px;height:{{height}}px;"
canvas-id="{{id}}">
</canvas>
<canvas
class="cropper"
disable-scroll="true"
style="position: fixed; top: -{{width * pixelRatio}}px; left: -{{height * pixelRatio}}px; width:{{width * pixelRatio}}px;height:{{height * pixelRatio}}px;"
canvas-id="{{targetId}}">
</canvas>
</template>
2.调用页面imgcropper.wxml(2b版一样)
<import src="/components/we-cropper/we-cropper.wxml" />
<view class="page imgDisposeBlock">
<view class='titleBar'>
<text>头像裁剪工具</text>
<text class='backBtn' decode='true' bindtap="cancleCropper"> < 返回 </text>
</view>
<view class='imgDisposeArea'>
<template is="we-cropper" data="{{...cropperOpt}}" />
</view>
<view class='imgDisposeControlLine'>
<button class='editBtn reelectBtn' bindtap="chooseMedia">选择图片</button>
<!-- <button class='editBtn reelectBtn' open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">选择图片</button> -->
<view class='editBtn uploadBtn editPerfectBtn' bindtap="getCropperImage">上传</view>
</view>
</view>
3.css文件imgcropper.wxss(2d版一样)
/* pages/user/edit/imgcropper.wxss */
page {
width: 100%;
height: 100%;
}
.titleBar {
width: 100%;
height: 128rpx;
/* position: fixed; */
padding-left: 15rpx;
text-align: left;
line-height: 128rpx;
font-size: 36rpx;
color: #333;
background: linear-gradient(to right, #EEE 0%, #FFF 100%);
}
.imgDisposeBlock {
position: absolute;
z-index: 99;
left: 0;
top: 0;
width: 100%;
height: 100%;
/* background: rgb(0, 0, 0); */
background: rgb(255, 255, 255);
overflow: hidden;
}
.imgDisposeArea {
width: 100%;
overflow: hidden;
background: #FFF;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAflBMVEXDw8PFxcX6+vrIyMi/v7/09PTNzM3ExMTCwsLv7+/ExMT+/v76+vq7u7v+/v76+frDw8PFxcW+vr7BwcH4+Pj8/PzHx8fKysrx8fHv7+/Ly8vAwMC9vb36+vr29va7u7vOzs7z8/PQ0NC4uLi5ubnr6+vq6upKc1GSAAAAEnRSTlPg4ODg4ODg4PDq6uDM8PDgzMzoyyPwAAABkklEQVQ4y82Va5OCIBSG8VZ7aW8eDoIFKJq1//8PLiAkW81un5qeST+888xxpPeM5POZzLxmWZ7n9laVBQkUZWUTy8uGvNWBHQCl1N6w7WLWCKTgeSLkNjG/t9jRW8XrE6mgDpSJqKgnFbeIzGPaOsLBzJnKSNF1HbfUW4YMwD+64U3TNfbqBQsTS1JS4Whh6APNVoLwgByt7+hXpEKPmkQd6XBCpRCZYTxmxE6cQXkKR4ZzxmgTs4Lk4EnFniH4g1IPIpbXxcuXydjF/9GjYeBA6GJGFlEuImB4ipvIHXbiahv4bkXbSosYxpjtBiFn1kspej1pdEywlEJNqLX9mSppD4QCKLk0vHU1A0v5S4TQHn62Cow9jKjAo5N19TtzJo4KmXIVPgheR6hxC2OvdLm0VujFZLmYO0eNqCuy3s8M++MucNwPM3I4Za/k42C0BSdaRxrjMmP0hDwtha9U+q4jZcvOcMtcs9BHk4jxoGJx/xXx7mJ/VXwKoW7rvya+k81L5qnWJLLKqvAhyYsQPX/9AIo9ekfsnrQOAAAAAElFTkSuQmCC');
}
.imgDisposeControlLine {
width: 100%;
height: 240rpx;
position: relative;
background: #fff;
}
.editBtn {
position: absolute;
top: 70rpx;
transform: translateY(-50%);
width: 260rpx;
height: 80rpx;
background: #EEE;
font-size: 26rpx;
text-align: center;
line-height: 80rpx;
color: #333;
border-radius: 6rpx;
}
.uploadBtn {
background: #79958b;
color: #FFF;
}
.backBtn {
position: absolute;
transform: translateY(-50%);
/* width:260rpx; */
padding: 0 25rpx 0 25rpx;
height: 60rpx;
background: #DDD;
font-size: 26rpx;
text-align: center;
line-height: 60rpx;
color: #333;
border-radius: 6rpx;
top: 62rpx;
right: 25rpx;
}
.editBtn.reelectBtn {
left: 10%;
}
.editBtn.editPerfectBtn {
right: 10%;
}
.finalCanvasClass {
position: absolute;
top: -600%;
left: 0;
z-index: 15;
transform-origin: left top;
transform: scale(0.25);
}
.letterCanvasClass {
position: absolute;
top: -9999rpx;
left: 0;
transform-origin: left top;
transform: scale(0.25);
z-index: -1;
}
.letterSrcClass {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* 截图canvas放大 真机上不行*/
/* .cropper{
transform-origin: left top;
transform: scale(0.25);
} */
.cropper {
/* background-color: red; */
/* background-color: rgba(0, 0, 0, 0.2); */
}
.shadowBlock {
width: 100%;
height: 100%;
background: #f7f7f7;
position: absolute;
top: 0;
left: 0;
z-index: 99;
}
4.js文件imgcropper.js
// pages/user/edit/imgcropper.js
import WeCropper from '../../../components/we-cropper/we-cropper.min.js';
const device = wx.getSystemInfoSync(); // 获取设备信息
const width = device.windowWidth; // 示例为一个与屏幕等宽的正方形裁剪框
const devicePixelRatio = device.pixelRatio;
const height = device.windowHeight - Math.round(368 / devicePixelRatio) - 70; //70是手机底部圆角区
const fs = width / 750 * 2;
const imgPix = 250;
Page({
/**
* 页面的初始数据
*/
data: {
showCropper: !1,
imgSrc: '',//确定裁剪后的图片
currentImage: '',
cropperOpt: {
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: devicePixelRatio,
width: width, // 画布宽度
height: height, // 画布高度
scale: 2.5, // 最大缩放倍数
zoom: 8, // 缩放系数
cut: {
x: (width - imgPix) / 2, // 裁剪框x轴起点(width * fs * 0.128) / 2
y: (height * 0.5 - imgPix * 0.5), // 裁剪框y轴期起点
width: imgPix, // 裁剪框宽度
height: imgPix// 裁剪框高度
}
},
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const that = this;
const avatarUrl = options.avatarUrl;
const nickName = options.nickName;
this.setData({
avatarUrl: avatarUrl,
nickName: nickName
});
//剪裁组件初始化
const { cropperOpt } = this.data;
this.myCropper = new WeCropper(cropperOpt)
.on('ready', (ctx) => {
console.log('wecropper is ready for work!');
})
.on('beforeImageLoad', (ctx) => {
wx.showToast({
title: '加载中',
icon: 'loading',
duration: 20000
})
})
.on('imageLoad', (ctx) => {
wx.hideToast();
});
//刷新画面
this.myCropper.updateCanvas();
//剪裁组件初始化end
// 加载原有的图片
if (avatarUrl) {
wx.getImageInfo({
src: avatarUrl,
success(res) {
// console.log(res);
that.myCropper.pushOrign(res.path);
}
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
},
onChooseAvatar(e) {
let that = this;
const { avatarUrl } = e.detail
// console.log(avatarUrl);
that.setData({
currentImage: avatarUrl
});
that.myCropper.pushOrign(avatarUrl);
},
chooseMedia(e) {
let that = this;
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
maxDuration: 30,
camera: 'back',
success(res) {
// console.log(res.tempFiles[0],e);
let { tempFilePath, fileType } = res.tempFiles[0];
that.setData({
currentImage: tempFilePath
});
if (fileType == 'image') {
that.myCropper.pushOrign(tempFilePath);
}
else {
getApp().core.showModal({
title: '提示',
content: "只能选择图片",
showCancel: false
});
}
},
fail(res) {
// console.log(res);
let { errMsg } = res;
if (errMsg != "chooseMedia:fail cancel") {
getApp().core.showModal({
title: '选择图片时出错',
content: res.errMsg,
showCancel: false
});
}
}
});
},
touchStart(e) {
this.myCropper.touchStart(e)
},
touchMove(e) {
this.myCropper.touchMove(e)
},
touchEnd(e) {
this.myCropper.touchEnd(e)
},
getCropperImage() {
let that = this;
if (!that.data.currentImage) {
getApp().core.showModal({
title: '提示',
content: '请先选择图片',
showCancel: false,
success: res => { }
});
return false;
}
wx.showToast({
title: '上传中',
icon: 'loading',
duration: 20000
});
// 如果有需要两层画布处理模糊,实际画的是放大的那个画布
this.myCropper.getCropperImage((src) => {
if (src) {
that.setData({
imgSrc: src
});
console.log(src);
// wx.previewImage({
// current: '', // 当前显示图片的http链接
// urls: [src] // 需要预览的图片http链接列表
// })
wx.uploadFile({
filePath: src,
name: 'image',
url: getApp().api.default.upload_avatar,
success(res) {
if (res.statusCode == 200) {
const avatar = JSON.parse(res.data);
if (avatar.code == 0) {
const img = avatar.data.url;
console.log(img);
that.setData({
avatarUrl: img,
// currentImage: '',
});
getApp().core.redirectTo({
url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
});
} else {
console.log("上传头像失败:", avatar.msg);
getApp().core.showToast({
title: avatar.msg
});
}
} else {
console.log("上传头像失败:", res.errMsg);
getApp().core.showToast({
title: res.errMsg
});
}
},
fail(err) {
console.log("上传头像失败:", err);
getApp().core.showToast({
title: '上传头像失败'
});
},
complete(res) {
wx.hideToast()
}
});
that.setData({
showCropper: !that.data.showCropper
});
} else {
console.log('获取图片地址失败,请稍后重试')
}
})
},
/**
* 回退
*/
cancleCropper() {
let that = this;
getApp().core.redirectTo({
url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
});
},
})
二、2d版
1.组件文件we-cropper.wxml
<template name="we-cropper">
<canvas
class="cropper"
type="2d"
disable-scroll="true"
bindtouchstart="touchStart"
bindtouchmove="touchMove"
bindtouchend="touchEnd"
style="width:{{width}}px;height:{{height}}px;"
id="{{id}}">
</canvas>
<canvas
class="cropper"
type="2d"
disable-scroll="true"
style="position: fixed; top: -{{width * pixelRatio}}px; left: -{{height * pixelRatio}}px; width:{{width * pixelRatio}}px;height:{{height * pixelRatio}}px;"
id="{{targetId}}">
</canvas>
</template>
2.调用页面imgcropper.wxml(同上)
3.css文件imgcropper.wxss(同上)
4.js文件imgcropper.js
// pages/user/edit/imgcropper.js
import WeCropper from '../../../components/we-cropper/we-cropper.min.js';
const device = wx.getSystemInfoSync(); // 获取设备信息
const width = device.windowWidth; // 示例为一个与屏幕等宽的正方形裁剪框
const devicePixelRatio = device.pixelRatio;
const height = device.windowHeight - Math.round(368 / devicePixelRatio) - 70; //70是手机底部圆角区
const fs = width / 750 * 2;
const imgPix = 250;
Page({
/**
* 页面的初始数据
*/
data: {
showCropper: !1,
imgSrc: '',//确定裁剪后的图片
currentImage: '',
cropperOpt: {
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: devicePixelRatio,
width: width, // 画布宽度
height: height, // 画布高度
scale: 2.5, // 最大缩放倍数
zoom: 8, // 缩放系数
cut: {
x: (width - imgPix) / 2, // 裁剪框x轴起点(width * fs * 0.128) / 2
y: (height * 0.5 - imgPix * 0.5), // 裁剪框y轴期起点
width: imgPix, // 裁剪框宽度
height: imgPix// 裁剪框高度
}
},
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const that = this;
const avatarUrl = options.avatarUrl;
const nickName = options.nickName;
this.setData({
avatarUrl: avatarUrl,
nickName: nickName
});
//剪裁组件初始化
const { cropperOpt } = this.data;
this.createSelectorQuery().select('#' + cropperOpt.id).fields({ node: true, size: true }).exec((res) => {
// console.log(res);
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
cropperOpt.canvas = canvas;
cropperOpt.ctx = ctx;
this.myCropper = new WeCropper(cropperOpt)
.on('ready', (ctx) => {
console.log('wecropper is ready for work!');
})
.on('beforeImageLoad', (ctx) => {
wx.showToast({
title: '加载中',
icon: 'loading',
duration: 20000
})
})
.on('imageLoad', (ctx) => {
wx.hideToast();
})
//刷新画面
this.myCropper.updateCanvas();
// 加载原有的图片
if (avatarUrl) {
wx.getImageInfo({
src: avatarUrl,
success(res) {
// console.log(res);
that.myCropper.pushOrign(res.path);
}
});
}
})
//剪裁组件初始化end
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
},
onChooseAvatar(e) {
let that = this;
const { avatarUrl } = e.detail
// console.log(avatarUrl);
that.setData({
currentImage: avatarUrl
});
that.myCropper.pushOrign(avatarUrl);
},
chooseMedia(e) {
let that = this;
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
maxDuration: 30,
camera: 'back',
success(res) {
// console.log(res.tempFiles[0],e);
let { tempFilePath, fileType } = res.tempFiles[0];
that.setData({
currentImage: tempFilePath
});
if (fileType == 'image') {
that.myCropper.pushOrign(tempFilePath);
}
else {
getApp().core.showModal({
title: '提示',
content: "只能选择图片",
showCancel: false
});
}
},
fail(res) {
// console.log(res);
let { errMsg } = res;
if (errMsg != "chooseMedia:fail cancel") {
getApp().core.showModal({
title: '选择图片时出错',
content: res.errMsg,
showCancel: false
});
}
}
});
},
touchStart(e) {
this.myCropper.touchStart(e)
},
touchMove(e) {
this.myCropper.touchMove(e)
},
touchEnd(e) {
this.myCropper.touchEnd(e)
},
getCropperImage() {
let that = this;
if (!that.data.currentImage) {
getApp().core.showModal({
title: '提示',
content: '请先选择图片',
showCancel: false,
success: res => { }
});
return false;
}
wx.showToast({
title: '上传中',
icon: 'loading',
duration: 20000
});
// 如果有需要两层画布处理模糊,实际画的是放大的那个画布
this.myCropper.getCropperImage((src) => {
if (src) {
that.setData({
imgSrc: src
});
console.log(src);
// wx.previewImage({
// current: '', // 当前显示图片的http链接
// urls: [src] // 需要预览的图片http链接列表
// })
wx.uploadFile({
filePath: src,
name: 'image',
url: getApp().api.default.upload_avatar,
success(res) {
if (res.statusCode == 200) {
const avatar = JSON.parse(res.data);
if (avatar.code == 0) {
const img = avatar.data.url;
console.log(img);
that.setData({
avatarUrl: img,
// currentImage: '',
});
getApp().core.redirectTo({
url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
});
} else {
console.log("上传头像失败:", avatar.msg);
getApp().core.showToast({
title: avatar.msg
});
}
} else {
console.log("上传头像失败:", res.errMsg);
getApp().core.showToast({
title: res.errMsg
});
}
},
fail(err) {
console.log("上传头像失败:", err);
getApp().core.showToast({
title: '上传头像失败'
});
},
complete(res) {
wx.hideToast()
}
});
that.setData({
showCropper: !that.data.showCropper
});
} else {
console.log('获取图片地址失败,请稍后重试')
}
})
},
/**
* 回退
*/
cancleCropper() {
let that = this;
getApp().core.redirectTo({
url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
});
},
})