一、背景
需求:需要实现用户上传图像并进行裁剪操作的功能。为了实现这一需求,使用了uniapp作为开发框架,并结合了we-cropper这一图像裁剪插件来实现图片裁剪的功能。
源码地址:https://github.com/we-plugin/we-cropper/
二、具体操作
2.1、在index.vue 页面点击头像上传获取临时路径 ,并跳转至cropper组件
<template>
<view>
<view class="upload box" @click="chooseImage">
<label>上传图片</label>
<view class="right">
<view class="upload-box">
<image :src="touxiang" mode="widthFix" :style="{width:imgSize,height:imgSize,'backgroundSize': 'cover','marginTop': imgMargin +'rpx'}" />
<text class="tips" v-show="isShow">点击上传</text>
</view>
</view>
</view>
<view style="position: fixed; top: 9999px;">
<canvas canvas-id="picCanvas" id='picCanvas' class="pic-canvas" :style="{width: imgWidth + 'px', height: imgHeight + 'px'}"></canvas>
</view>
</view>
</template>
<script>
export default {
data() {
return {
touxiang: '/static/upload.png',
isShow: true,
imgSize: '128rpx',
imgMargin: 30,
imgWidth: 0,
imgHeight: 0,
isActionBtn: false,
}
},
methods: {
chooseImage(){
uni.navigateTo({
url: "/pages/cropper/cropper?destWidth=200&rectWidth=200&fileType=jpg"
})
//接收cropper页面传递的图片路径
uni.$on('Cropper', path => {
if(path){
// 上传图片方法
this.update(path);
this.imgSize = '98%'
this.imgMargin = 0
this.touxiang = path
this.isShow = false
}
});
},
update(filePath) {
this.touxiang = filePath
this.isActionBtn = true
//根据其他业务需求,调取接口,保存头像
// ......
},
}
}
</script>
<style lang="scss" scoped>
.upload {
display: flex;
box-sizing: border-box;
padding: 20rpx 30rpx;
width: 710rpx;
height: 300rpx;
justify-content: center;
margin-top: 20rpx;
position: relative;
label {
width: 158rpx;
color: #333333;
font-size: 30spx;
}
.right {
display: flex;
width: 492rpx;
height: 250rpx;
.upload-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 205rpx;
height: 100%;
background-color: #F0F0F0;
border-radius: 10rpx;
image {
margin-left: auto;
margin-right: auto;
}
text {
margin-top: 10rpx;
width: 100%;
font-size: 20spx;
color: #999999;
text-align: center;
}
}
}
}
.pic-canvas {
width: 205px;
height: 250px;
}
</style>
2.2、cropper组件进行图片裁剪操作
<template>
<view class="content">
<view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }">
<canvas
class="cropper"
:disable-scroll="true"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
:style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }"
canvas-id="cropper"
id="cropper"
></canvas>
<canvas
class="cropper"
:disable-scroll="true"
:style="{
position: 'fixed',
top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
height: `${cropperOpt.height * cropperOpt.pixelRatio}`
}"
canvas-id="targetId"
id="targetId"
></canvas>
</view>
<view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px',color:cropperOpt.boundStyle.color}">
<view class="upload" @tap="uploadTap">选择图片</view>
<view class="upload btn" @tap="uploadTap">重新选取</view>
<view class="getCropperImage btn" :style="{ backgroundColor: cropperOpt.boundStyle.color }" @tap="getCropperImage(false)">完成</view>
</view>
</view>
</template>
<script>
import WeCropper from 'we-cropper';
export default {
props: {
boundStyle: {
type: Object,
default() {
return {
lineWidth: 4,
borderColor: 'rgb(245, 245, 245)',
mask: 'rgba(0, 0, 0, 0.35)'
};
}
}
},
data() {
return {
// 底部导航的高度
bottomNavHeight: 50,
originWidth: 200,
width: 0,
height: 0,
cropper: '',
cropperOpt: {
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: 1,
width: 0,
height: 0,
scale: 2.5,
zoom: 8,
cut: {
x: (this.width - this.originWidth) / 2,
y: (this.height - this.originWidth) / 2,
width: this.originWidth,
height: this.originWidth
},
boundStyle: {
color: '#04b00f',
mask: 'rgba(0,0,0,0.8)',
lineWidth: 1
}
},
// 裁剪框和输出图片的尺寸,高度默认等于宽度
// 输出图片宽度,单位px
destWidth: 200,
// 裁剪框宽度,单位px
rectWidth: 200,
// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
fileType: 'jpg',
src: '' // 选择的图片路径,用于在点击确定时,判断是否选择了图片
};
},
onLoad(option) {
let rectInfo = uni.getSystemInfoSync();
this.width = rectInfo.windowWidth;
this.height = rectInfo.windowHeight - this.bottomNavHeight;
this.cropperOpt.width = this.width;
this.cropperOpt.height = this.height;
this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
if (option.destWidth) this.destWidth = Number(option.destWidth);
if (option.rectWidth) {
let rectWidth = Number(option.rectWidth);
this.cropperOpt.cut = {
x: (this.width - rectWidth) / 2,
y: (this.height - rectWidth) / 2,
width: rectWidth,
height: rectWidth
};
}
this.rectWidth = Number(option.rectWidth);
if (option.fileType) this.fileType = option.fileType;
// 初始化
this.cropper = new WeCropper(this.cropperOpt)
.on('ready', ctx => {
// wecropper is ready for work!
})
.on('beforeImageLoad', ctx => {
// before picture loaded, i can do something
})
.on('imageLoad', ctx => {
// picture loaded
})
.on('beforeDraw', (ctx, instance) => {
// before canvas draw,i can do something
});
// 设置导航栏样式,以免用户在page.json中没有设置为黑色背景
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#000000'
});
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
this.src = res.tempFilePaths[0];
// 获取裁剪图片资源后,给data添加src属性及其值
this.cropper.pushOrign(this.src);
}
});
},
methods: {
touchStart(e) {
this.cropper.touchStart(e);
},
touchMove(e) {
this.cropper.touchMove(e);
},
touchEnd(e) {
this.cropper.touchEnd(e);
},
getCropperImage(isPre = false) {
if (!this.src) return this.$toast('请先选择图片再裁剪');
let cropper_opt = {
destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值
destWidth: Number(this.destWidth),
fileType: this.fileType
};
this.cropper.getCropperImage(cropper_opt, (path, err) => {
if (err) {
uni.showModal({
title: '温馨提示',
content: err.message
});
} else {
if (isPre) {
uni.previewImage({
current: '', // 当前显示图片的 http 链接
urls: [path] // 需要预览的图片 http 链接列表
});
} else {
uni.$emit('Cropper', path);
uni.navigateBack();
}
}
});
},
uploadTap() {
const self = this;
uni.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
self.src = res.tempFilePaths[0];
// 获取裁剪图片资源后,给data添加src属性及其值
self.cropper.pushOrign(this.src);
}
});
}
}
};
</script>
<style scoped lang="scss">
.content {
background: rgba(255, 255, 255, 1);
}
.cropper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 11;
}
.cropper-buttons {
background-color: #000000;
color: #eee;
}
.cropper-wrapper {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: #000;
}
.cropper-buttons {
width: 100vw;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
left: 0;
padding: 0 20rpx;
box-sizing: border-box;
font-size: 28rpx;
}
.cropper-buttons .upload, .cropper-buttons .getCropperImage{
text-align: center;
}
.btn{
height: 30px;
line-height: 30px;
padding: 0 24rpx;
border-radius: 2px;
color: #ffffff;
}
</style>
2.3、在cropper组件中需要引入we-cropper第三方插件,有两种引入方式
源码地址:https://github.com/we-plugin/we-cropper/
方式1:npm引入,然后在cropper组件中直接 import WeCropper from 'we-cropper';
方式2:在源码中找到dist文件夹下的we-cropper.js文件,创建utils文件夹,将we-cropper.js文件放入,cropper组件直接引入utils下的
2.4、效果呈现
最后:👏👏 😀😀😀 👍👍