赞
踩
<template>
<view class="content">
<view class="edgeInsetTop"></view>
<wanl-image-cutter @ok="getCropperImage" @cancel="oncancle" :url="url" :fixed="false" :blob="false"
:maxWidth="500" :maxHeight="500" />
<view>
<view @tap="chooseImage" class="cu-avatar avatar" v-if="path"
:style="{ backgroundImage: 'url(' + path + ')' }"></view>
<view class=" text-sm" @tap="chooseImage">点击修改头像</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
url: '',
path: 'https://cn.vuejs.org/images/dcloud2.png',
};
},
computed: {},
methods: {
// 点击选择图片
chooseImage() {
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
// 设置url的值,显示控件
this.url = res.tempFilePaths[0];
}
});
},
// 上传图片
getCropperImage(e) {
// 这里走接口替换的值
this.path = e.path;
this.url = '';
},
// 点击返回清空组件值
oncancle() {
this.url = '';
}
}
};
</script>
<style>
.content {
display: flex;
justify-content: center;
align-items: center;
}
.avatar {
width: 180rpx;
height: 180rpx;
margin: 20rpx auto;
background-color: #e5e5e5;
border-radius: 50%;
background-position: center;
background-size: 100%;
}
</style>
下面文件引入公用文件夹
wanl-image-cutter.vue
<template>
<view v-show="url" class="wanl-image-cutter">
<canvas :style="{width: target.width + 'px', height: target.height + 'px'}" canvas-id="target"></canvas>
<view class="body">
<image v-if="url" class="image" @load="imageLoad"
:style="{left: image.left + 'px', top: image.top + 'px', width: image.width + 'px', height: image.height + 'px'}"
:src="url"></image>
<view v-if="mask.show" class="mask"></view>
<view @touchstart="touchStart($event, 'plank')" @touchmove="touchMove" @touchend="touchEnd"
@touchcancel="touchCancel" class="plank">
<view class="frame" @touchstart="touchStart($event, 'frame')" @touchstart.stop.prevent="touchHandle"
:style="{left: frame.left + 'px', top: frame.top + 'px', width: frame.width + 'px', height: frame.height + 'px'}">
<canvas v-if="mask.show" class="canvas"
:style="{width: frame.width + 'px', height: frame.height + 'px'}" canvas-id="canvas"></canvas>
<view class="rect"></view>
<view class="line-one"></view>
<view class="line-two"></view>
<view class="line-three"></view>
<view class="line-four"></view>
<view @touchstart="touchStart($event, 'left')" @touchstart.stop.prevent="touchHandle"
class="frame-left"></view>
<view @touchstart="touchStart($event, 'right')" @touchstart.stop.prevent="touchHandle"
class="frame-right"></view>
<view @touchstart="touchStart($event, 'top')" @touchstart.stop.prevent="touchHandle"
class="frame-top"></view>
<view @touchstart="touchStart($event, 'bottom')" @touchstart.stop.prevent="touchHandle"
class="frame-bottom"></view>
<view @touchstart="touchStart($event, 'left-top')" @touchstart.stop.prevent="touchHandle"
class="frame-left-top"></view>
<view @touchstart="touchStart($event, 'left-bottom')" @touchstart.stop.prevent="touchHandle"
class="frame-left-bottom"></view>
<view @touchstart="touchStart($event, 'right-top')" @touchstart.stop.prevent="touchHandle"
class="frame-right-top"></view>
<view @touchstart="touchStart($event, 'right-bottom')" @touchstart.stop.prevent="touchHandle"
class="frame-right-bottom"></view>
</view>
</view>
</view>
<view class="toolbar">
<button @tap="oncancle" class="btn-cancel">返回</button>
<button @tap="onok" class="btn-ok">确定</button>
</view>
</view>
</template>
<script>
export default {
props: {
url: {
type: String,
default: ""
},
fixed: {
type: Boolean,
default: false
},
width: {
type: Number,
default: 200
},
height: {
type: Number,
default: 200
},
maxWidth: {
type: Number,
default: 1024
},
maxHeight: {
type: Number,
default: 1024
},
blob: {
type: Boolean,
default: true
}
},
data() {
return {
mask: {
show: false
},
frame: {
left: 50,
top: 50,
width: this.width,
height: this.height
},
image: {
left: 20,
top: 20,
width: 300,
height: 400
},
real: {
width: 100,
height: 100
},
target: {
width: this.width,
height: this.height
},
touches: [],
type: "",
start: {
frame: {
left: 0,
top: 0,
width: 0,
height: 0
},
image: {
left: 0,
top: 0,
width: 0,
height: 0
},
},
timeoutId: -1,
context: null
};
},
mounted() {
//#ifdef H5
this.$el.addEventListener("touchmove", (ev) => {
ev.preventDefault();
});
// #endif
this.context = uni.createCanvasContext("canvas", this);
this.targetContext = uni.createCanvasContext("target", this);
},
methods: {
imageLoad(ev) {
this.mask.show = true;
this.real.width = ev.detail.width;
this.real.height = ev.detail.height;
this.image.width = ev.detail.width;
this.image.height = ev.detail.height;
this.frame.width = this.width;
this.frame.height = this.height;
if (!this.fixed) {
this.frame.width = this.image.width;
this.frame.height = this.image.height;
}
var query = uni.createSelectorQuery().in(this);
query.select(".body").boundingClientRect((data) => {
var bw = data.width;
var bh = data.height;
var fw = this.frame.width;
var fh = this.frame.height;
var tw = bw * 0.8;
var th = bh * 0.8;
var sx = tw / fw;
var sy = th / fh;
var scale = sx;
if (sx < sy) {
scale = sy;
}
tw = fw * scale;
th = fh * scale;
var tx = (bw - tw) / 2;
var ty = (bh - th) / 2;
this.frame.width = tw;
this.frame.height = th;
this.frame.left = tx;
this.frame.top = ty;
var iw = this.image.width;
var ih = this.image.height;
sx = tw / iw;
sy = th / ih;
scale = sx;
if (sx < sy) {
scale = sy;
}
this.image.width = iw * scale;
this.image.height = ih * scale;
this.image.left = (bw - this.image.width) / 2;
this.image.top = (bh - this.image.height) / 2;
setTimeout(() => {
this.trimImage();
}, 100);
}).exec();
},
touchHandle() {},
touchStart(ev, type) {
this.stopTime();
this.mask.show = false;
if (this.touches.length == 0) {
this.type = type;
this.start.frame.left = this.frame.left;
this.start.frame.top = this.frame.top;
this.start.frame.width = this.frame.width;
this.start.frame.height = this.frame.height;
this.start.image.left = this.image.left;
this.start.image.top = this.image.top;
this.start.image.width = this.image.width;
this.start.image.height = this.image.height;
}
var touches = ev.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// this.touches[touch.identifier] = touch;
this.touches.push(touch);
}
},
touchMove(ev) {
this.stopTime();
ev.preventDefault();
var touches = ev.touches;
if (this.touches.length == 1) {
if (this.type == "plank" || this.type == "frame" || this.fixed) {
this.moveImage(this.touches[0], touches[0]);
} else {
this.scaleFrame(this.touches[0], touches[0], this.type);
}
} else if (this.touches.length == 2 && touches.length == 2) {
var ta = this.touches[0];
var tb = this.touches[1];
var tc = touches[0];
var td = touches[1];
if (ta.identifier != tc.identifier) {
var temp = tc;
tc = td;
td = temp;
}
this.scaleImage(ta, tb, tc, td);
}
},
touchEnd(ev) {
this.type = "";
this.touches = [];
this.startTime();
},
touchCancel(ev) {
this.type = "";
this.touches = [];
this.startTime();
},
startTime() {
this.stopTime();
this.timeoutId = setTimeout(() => {
this.trimImage();
}, 800);
},
stopTime() {
if (this.timeoutId >= 0) {
clearTimeout(this.timeoutId);
this.timeoutId = -1;
}
},
trimImage() {
this.mask.show = true;
var query = uni.createSelectorQuery().in(this);
query.select(".body").boundingClientRect((data) => {
var bw = data.width;
var bh = data.height;
var fw = this.frame.width;
var fh = this.frame.height;
var tw = bw * 0.8;
var th = bh * 0.8;
var sx = tw / fw;
var sy = th / fh;
var scale = sx;
if (sx > sy) {
scale = sy;
}
tw = fw * scale;
th = fh * scale;
var tx = (bw - tw) / 2;
var ty = (bh - th) / 2;
var ax = tx - this.frame.left + (this.frame.left - this.image.left) * (1 - scale);
var ay = ty - this.frame.top + (this.frame.top - this.image.top) * (1 - scale);
this.frame.width = tw;
this.frame.height = th;
this.frame.left = tx;
this.frame.top = ty;
this.image.width *= scale;
this.image.height *= scale;
this.image.left += ax;
this.image.top += ay;
}).exec();
setTimeout(() => {
var scale = this.image.width / this.real.width;
var x = (this.frame.left - this.image.left) / scale;
var y = (this.frame.top - this.image.top) / scale;
var width = this.frame.width / scale;
var height = this.frame.height / scale;
this.context.drawImage(this.url, x, y, width, height, 0, 0, this.frame.width, this.frame
.height);
this.context.draw(false);
}, 100);
},
moveImage(ta, tb) {
var ax = tb.clientX - ta.clientX;
var ay = tb.clientY - ta.clientY;
this.image.left = this.start.image.left + ax;
this.image.top = this.start.image.top + ay;
if (this.image.left > this.frame.left) {
this.image.left = this.frame.left;
}
if (this.image.top > this.frame.top) {
this.image.top = this.frame.top;
}
if (this.image.left + this.image.width < this.frame.left + this.frame.width) {
this.image.left = this.frame.left + this.frame.width - this.image.width;
}
if (this.image.top + this.image.height < this.frame.top + this.frame.height) {
this.image.top = this.frame.top + this.frame.height - this.image.height;
}
},
scaleImage(ta, tb, tc, td) {
var x1 = ta.clientX;
var y1 = ta.clientY;
var x2 = tb.clientX;
var y2 = tb.clientY;
var x3 = tc.clientX;
var y3 = tc.clientY;
var x4 = td.clientX;
var y4 = td.clientY;
var ol = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
var el = Math.sqrt((x3 - x4) * (x3 - x4) + (y3 - y4) * (y3 - y4));
var ocx = (x1 + x2) / 2;
var ocy = (y1 + y2) / 2;
var ecx = (x3 + x4) / 2;
var ecy = (y3 + y4) / 2;
var ax = ecx - ocx;
var ay = ecy - ocy;
var scale = el / ol;
if (this.start.image.width * scale < this.frame.width) {
scale = this.frame.width / this.start.image.width;
}
if (this.start.image.height * scale < this.frame.height) {
scale = this.frame.height / this.start.image.height;
}
if (this.start.image.width * scale < this.frame.width) {
scale = this.frame.width / this.start.image.width;
}
this.image.left = this.start.image.left + ax - (ocx - this.start.image.left) * (scale - 1);
this.image.top = this.start.image.top + ay - (ocy - this.start.image.top) * (scale - 1);
this.image.width = this.start.image.width * scale;
this.image.height = this.start.image.height * scale;
if (this.image.left > this.frame.left) {
this.image.left = this.frame.left;
}
if (this.image.top > this.frame.top) {
this.image.top = this.frame.top;
}
if (this.image.left + this.image.width < this.frame.left + this.frame.width) {
this.image.left = this.frame.left + this.frame.width - this.image.width;
}
if (this.image.top + this.image.height < this.frame.top + this.frame.height) {
this.image.top = this.frame.top + this.frame.height - this.image.height;
}
},
scaleFrame(ta, tb, type) {
var ax = tb.clientX - ta.clientX;
var ay = tb.clientY - ta.clientY;
var x1 = this.start.frame.left;
var y1 = this.start.frame.top;
var x2 = this.start.frame.left + this.start.frame.width;
var y2 = this.start.frame.top + this.start.frame.height;
if (type == "left") {
x1 += ax;
} else if (type == "right") {
x2 += ax;
} else if (type == "top") {
y1 += ay;
} else if (type == "bottom") {
y2 += ay;
} else if (type == "left-top") {
x1 += ax;
y1 += ay;
} else if (type == "left-bottom") {
x1 += ax;
y2 += ay;
} else if (type == "right-top") {
x2 += ax;
y1 += ay;
} else if (type == "right-bottom") {
x2 += ax;
y2 += ay;
}
if (x1 < this.image.left) {
x1 = this.image.left;
}
if (y1 < this.image.top) {
y1 = this.image.top;
}
if (x2 > this.image.left + this.image.width) {
x2 = this.image.left + this.image.width;
}
if (y2 > this.image.top + this.image.height) {
y2 = this.image.top + this.image.height;
}
this.frame.left = x1;
this.frame.top = y1;
this.frame.width = x2 - x1;
this.frame.height = y2 - y1;
},
parseBlob(base64) {
var arr = base64.split(',');
var mime = arr[0].match(/:(.*?);/)[1];
var bstr = atob(arr[1]);
var n = bstr.length;
var u8arr = new Uint8Array(n);
for (var i = 0; i < n; i++) {
u8arr[i] = bstr.charCodeAt(i);
}
var url = URL || webkitURL;
return url.createObjectURL(new Blob([u8arr], {
type: mime
}));
},
onok() {
var scale = this.image.width / this.real.width;
var x = (this.frame.left - this.image.left) / scale;
var y = (this.frame.top - this.image.top) / scale;
var width = this.frame.width / scale;
var height = this.frame.height / scale;
var tw = width;
var th = height;
if (this.fixed) {
tw = this.width / 2;
th = this.height / 2;
} else {
if (tw > this.maxWidth / 2) {
var sc = this.maxWidth / 2 / tw;
tw = tw * sc;
th = th * sc;
}
if (th > this.maxHeight / 2) {
var sc = this.maxHeight / 2 / th;
th = th * sc;
tw = tw * sc;
}
}
this.target.width = tw;
this.target.height = th;
uni.showLoading({
title: "正在裁剪"
});
setTimeout(() => {
this.targetContext.drawImage(this.url, x, y, width, height, 0, 0, tw, th);
this.targetContext.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: "target",
success: (res) => {
var path = res.tempFilePath;
// #ifdef H5
if (this.blob) {
path = this.parseBlob(path);
}
// #endif
this.$emit("ok", {
path: path
});
},
fail: (ev) => {
console.log(ev);
},
complete: () => {
uni.hideLoading();
}
}, this);
});
}, 100);
},
oncancle() {
this.$emit("cancel");
}
}
}
</script>
<style scoped>
.wanl-image-cutter {
position: absolute;
left: 0;
width: 100%;
height: 100%;
top: 0;
bottom: 0;
z-index: 1000;
}
.toolbar {
position: absolute;
width: 100%;
height: 100upx;
left: 0upx;
bottom: 0upx;
box-sizing: border-box;
border-bottom: 1px solid #C0C0C0;
background: #F8F8F8;
}
.btn-cancel {
position: absolute;
left: 100upx;
top: 12upx;
font-size: 30upx;
line-height: 30upx;
padding: 20upx;
color: #333333;
}
.btn-ok {
position: absolute;
right: 100upx;
top: 12upx;
font-size: 30upx;
line-height: 30upx;
padding: 20upx;
color: #333333;
}
.body {
position: absolute;
left: 0upx;
right: 0upx;
top: 0upx;
bottom: 100upx;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC);
overflow: hidden;
}
uni-button:after {
border: 0;
}
.mask {
position: absolute;
left: 0upx;
right: 0upx;
top: 0upx;
bottom: 0upx;
background: black;
opacity: 0.6;
}
.plank {
position: absolute;
left: 0upx;
right: 0upx;
top: 0upx;
bottom: 0upx;
}
.image {
position: absolute;
}
.frame {
position: absolute;
}
.canvas {
position: absolute;
display: block;
left: 0px;
top: 0px;
}
.rect {
position: absolute;
left: -2px;
top: -2px;
width: 100%;
height: 100%;
border: 2px solid white;
}
.line-one {
position: absolute;
width: 100%;
height: 1px;
background: white;
left: 0;
top: 33.3%;
}
.line-two {
position: absolute;
width: 100%;
height: 1px;
background: white;
left: 0;
top: 66.7%;
}
.line-three {
position: absolute;
width: 1px;
height: 100%;
background: white;
top: 0;
left: 33.3%;
}
.line-four {
position: absolute;
width: 1px;
height: 100%;
background: white;
top: 0;
left: 66.7%;
}
.frame-left {
position: absolute;
height: 100%;
width: 8px;
left: -4px;
top: 0;
}
.frame-right {
position: absolute;
height: 100%;
width: 8px;
right: -4px;
top: 0;
}
.frame-top {
position: absolute;
width: 100%;
height: 8px;
top: -4px;
left: 0;
}
.frame-bottom {
position: absolute;
width: 100%;
height: 8px;
bottom: -4px;
left: 0;
}
.frame-left-top {
position: absolute;
width: 20px;
height: 20px;
left: -6px;
top: -6px;
border-left: 4px solid red;
border-top: 4px solid red;
}
.frame-left-bottom {
position: absolute;
width: 20px;
height: 20px;
left: -6px;
bottom: -6px;
border-left: 4px solid red;
border-bottom: 4px solid red;
}
.frame-right-top {
position: absolute;
width: 20px;
height: 20px;
right: -6px;
top: -6px;
border-right: 4px solid red;
border-top: 4px solid red;
}
.frame-right-bottom {
position: absolute;
width: 20px;
height: 20px;
right: -6px;
bottom: -6px;
border-right: 4px solid red;
border-bottom: 4px solid red;
}
</style>