支持功能:
1.自定义裁剪尺寸
2.等比例缩放
3.自由拖动
5.裁剪生成新图片
6.本地选择图片
实现代码:
<!--
图片编辑器,支持自定义尺寸、缩放、拖动、裁剪
@version 1.0.2
@updatedAt 2021/01/06
-->
<template>
<view class="upload-image">
<view class="upload-image-body">
<canvas canvas-id="picCanvas" :style="'position: absolute; width: ' + picSizeW + 'px; height: ' + picSizeH + 'px; left: -' + picSizeW + 'px;'"></canvas>
<view class="pic-preview" @touchstart="touchstart" @touchmove="touchmove">
<scroll-view class='pic-area' @scroll='scroll' scroll-x scroll-y>
<view :style="{ height: `calc(50% - ${areaH * 0.5}vw)` }"></view>
<image :src="picSrc || picUrl" :style="styleImg"></image>
<view :style="{ height: `calc(50% - ${areaH * 0.5}vw)` }"></view>
</scroll-view>
<view class="outside-mask-block" :style="styleV"></view>
<view class="outside-mask-block" :style="styleV" style="bottom: 0;"></view>
<view class="outside-mask-block" :style="styleH" style="left: 0;"></view>
<view class="outside-mask-block" :style="styleH" style="right: 0;"></view>
</view>
<view class="bottom-bar safe-area-inset-bottom">
<block v-if="picSrc != ''">
<view class="rechoose" @click="chooseImage">重选</view>
<button class="button" size="mini" @click="uploadClick">确定</button>
</block>
<view v-else class="choose-btn" @click="chooseImage">选择图片</view>
</view>
</view>
</view>
</template>
<script>
const AREA_SIZE = 75; // 裁剪框占屏幕尺寸百分比
const IMG_SIZE = 300; // 裁剪图片默认尺寸
export default {
data() {
return {
// bobyHeight: this.getBobyHeight(),
picSrc: '',
picUrl: '',
dataKey: '',
areaW: AREA_SIZE,
areaH: AREA_SIZE,
width: this.areaW,
height: this.areaH,
old_width: 0,
old_height: 0,
picSizeW: IMG_SIZE,
picSizeH: IMG_SIZE,
x: 0,
y: 0,
distance: 0,
scale: 1,
disable: false
}
},
onLoad(options) {
this.picSizeW = Number(options.w) || IMG_SIZE;
this.picSizeH = Number(options.h) || IMG_SIZE;
this.picUrl = ''; // 初始化图片
this.initAreaSize();
},
computed: {
styleImg() {
return `padding: 0 ${50 - this.areaW * 0.5}%;width: ${this.width}%;height: ${this.height}vw;`;
},
styleV() {
// (屏幕高度 - 图片高度) / 2
return `height: calc(50% - ${this.areaH * 0.5}vw);left: ${50 - this.areaW * 0.5}%;right: ${50 - this.areaW * 0.5}%;`;
},
styleH() {
// (屏幕宽度 - 图片宽度) / 2
return `top: 0;bottom: 0;width: ${50 - this.areaW * 0.5}%;`;
},
},
methods: {
initAreaSize() {
if (this.picSizeW > this.picSizeH) {
this.areaH = AREA_SIZE * this.picSizeH / this.picSizeW;
} else if (this.picSizeW < this.picSizeH) {
this.areaW = AREA_SIZE * this.picSizeW / this.picSizeH;
}
this.width = this.areaW;
this.height = this.areaH;
},
chooseImage() {
uni.chooseImage({
count: 1,
success: (res) => {
this.resetData();
this.initImage(res.tempFiles[0].path);
}
});
},
resetData() {
this.picSrc = '';
this.distance = 0;
this.old_width = 0;
this.old_height = 0;
this.x = 0;
this.y = 0;
this.scale = 1;
this.disable = false;
this.initAreaSize();
},
initImage(url) {
uni.getImageInfo({
src: url,
success: (res) => {
// #ifdef APP-PLUS || MP
if (['png', 'jpeg', 'jpg'].indexOf(res.type) == -1) {
uni.showModal({
title: '',
content: '仅支持上传png和jpg格式图片',
showCancel: true,
cancelText: '取消',
confirmText: '重选',
success: (res) => {
if (res.confirm) {
this.chooseImage();
}
},
});
return;
}
// #endif
let scale = res.width / res.height;
let areaScale = this.areaW / this.areaH;
this.picSrc = url;
this.scale = scale;
if (scale > 1) { // 横向图片
if (scale >= areaScale) { // 图片宽不小于目标宽,则高固定,宽自适应
this.width = (this.height / res.height) * this.width * (res.width / this.width);
} else { // 否则宽固定、高自适应
this.height = res.height * this.width / res.width;
}
} else { // 纵向图片
if (scale <= areaScale) { // 图片高不小于目标高,宽固定,高自适应
this.height = (this.width / res.width) * this.height / (this.height / res.height);
} else { // 否则高固定,宽自适应
this.width = res.width * this.height / res.height;
}
}
// 记录原始宽高,为缩放比列做限制
this.old_width = this.width;
this.old_height = this.height;
},
});
},
touchstart(e) {
if (this.picSrc && e.touches.length == 2) {
let _x = e.touches[1].pageX - e.touches[0].pageX,
_y = e.touches[1].pageY - e.touches[0].pageY,
distance = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2));
this.distance = distance;
}
},
touchmove(e) {
if (this.picSrc && e.touches.length == 2) {
let _x = e.touches[1].pageX - e.touches[0].pageX,
_y = e.touches[1].pageY - e.touches[0].pageY,
old_width = this.old_width,
old_height = this.old_height,
newdistance = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2)),
distance = this.distance,
end_distance = newdistance - distance,
pic_scale = 1 + end_distance * 0.001,
width = this.width * pic_scale,
height = this.height * pic_scale;
let max = width / old_width;
if (max > 2) {
width = old_width * 2;
height = old_height * 2;
} else if (max < 1) {
width = old_width;
height = old_height;
}
this.width = width;
this.height = height;
}
},
scroll(e) {
if(this.picSrc) {
let x = e.detail.scrollLeft,
y = e.detail.scrollTop;
this.x = x;
this.y = y;
}
},
uploadClick(e) {
uni.showModal({
content: '确定要截取当前可视区域图片并上传吗?',
success: modalRes => {
if (modalRes.confirm) {
uni.showLoading({
title: '上传中...',
mask: true
});
const systemInfo = uni.getSystemInfoSync();
let whScale = systemInfo.screenWidth * 0.01, // 图片宽高vw与px比
// 生成图片的实际尺寸与截取区域比
xScale = this.picSizeW / (systemInfo.screenWidth * this.areaW * 0.01),
yScale = this.picSizeH / (systemInfo.screenWidth * this.areaH * 0.01);
const canvas = uni.createCanvasContext('picCanvas');
// 注意:无法直接绘制网络图片,需要先下载到本地
canvas.drawImage(this.picSrc, -this.x * xScale, -this.y * yScale, this.width * whScale * xScale, this.height *
whScale * xScale);
canvas.draw(setTimeout(() => {
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: this.picSizeW,
height: this.picSizeH,
destWidth: this.picSizeW, // 必要,保证生成图片宽度不受设备分辨率影响
destHeight: this.picSizeH, // 必要,保证生成图片高度不受设备分辨率影响
canvasId: 'picCanvas',
success: (fileRes) => {
console.log(fileRes)
this.uploadImage(fileRes.tempFilePath);
},
fail: function(err) {
console.log(err);
uni.showToast({
title: '上传失败:图片生成过程中遇到错误',
icon: 'none'
});
}
}, this);
}, 1000));
}
}
});
},
uploadImage(tempFilePath){
// 在H5平台下,tempFilePath 为 base64
}
}
}
</script>
<style lang="scss">
.upload-image {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
.upload-image-body {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
.pic-preview {
width: 100%;
flex: 1;
position: relative;
.pic-area {
left: 0;
right: 0;
top: 0;
bottom: 0;
font-size: 0;
z-index: 1;
background-color: $uni-bg-color-grey;
position: absolute;
display: flex;
flex-direction: column;
}
.outside-mask-block {
background-color: rgba(51, 51, 51, 0.9);
z-index: 2;
position: absolute;
}
}
.bottom-bar {
display: flex;
flex-direction: row;
position: relative;
background-color: $uni-bg-color-grey;
.rechoose {
color: $uni-color-primary;
padding: 0 $uni-spacing-row-lg;
line-height: 100rpx;
}
.choose-btn {
color: $uni-color-primary;
text-align: center;
line-height: 100rpx;
flex: 1;
}
.button {
margin: auto $uni-spacing-row-lg auto auto;
}
}
.safe-area-inset-bottom {
padding-bottom: 0;
padding-bottom: constant(safe-area-inset-bottom); // 兼容 IOS<11.2
padding-bottom: env(safe-area-inset-bottom); // 兼容 IOS>=11.2
}
}
}
</style>
使用说明:
1.自定义裁剪尺寸和默认展示图片,修改 onLoad 中得代码即可
onLoad(options) {
this.picSizeW = Number(options.w) || IMG_SIZE;
this.picSizeH = Number(options.h) || IMG_SIZE;
this.picUrl = ''; // 初始化图片
this.initAreaSize();
},
2.现已发布了图片裁剪插件2.0(建议使用),可直接使用HBuilder导入项目,代码开源注释全、裁剪功能更丰富、体验更佳、性能也大幅度提升。
uniapp微信小程序图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式_Homilier的博客-CSDN博客