新增服务,增加图片裁剪功能
This commit is contained in:
parent
e8e13a4154
commit
ff8b497c04
|
|
@ -73,7 +73,7 @@
|
|||
</view>
|
||||
</view>
|
||||
<!-- <view class="solid-top"> -->
|
||||
<!-- <view class="padding-lr flex align-center">
|
||||
<!-- <view class="padding-lr flex align-center">
|
||||
<view class="flex-twice flex justify-start align-center">
|
||||
<view style="width: 20%;">单位</view>
|
||||
<my-uni-combox :candidates="productUnits" placeholder="请选择" v-model="formData.productUnit">
|
||||
|
|
@ -83,20 +83,20 @@
|
|||
优惠券+
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- <view class="padding-lr flex align-center padding-tb-xs"> -->
|
||||
<!-- <view class="cu-form-group"> -->
|
||||
<!-- <checkbox style="transform:scale(1)" class="main-color margin-right-xs" :value="true"
|
||||
<!-- <view class="padding-lr flex align-center padding-tb-xs"> -->
|
||||
<!-- <view class="cu-form-group"> -->
|
||||
<!-- <checkbox style="transform:scale(1)" class="main-color margin-right-xs" :value="true"
|
||||
:v-model="formData.isGoldenServ" :checked="formData.isGoldenServ"></checkbox> -->
|
||||
<!-- <view class="title">金牌服务<text class="margin-left-xs text-red">*</text></view> -->
|
||||
<!-- <my-uni-combox :candidates="servHonorTitle" placeholder="请选择" v-model="formData.servHonorTitle"> -->
|
||||
<!-- </my-uni-combox> -->
|
||||
<!-- </view> -->
|
||||
<!-- <view class="flex-twice flex justify-start align-center">
|
||||
<!-- <view class="title">金牌服务<text class="margin-left-xs text-red">*</text></view> -->
|
||||
<!-- <my-uni-combox :candidates="servHonorTitle" placeholder="请选择" v-model="formData.servHonorTitle"> -->
|
||||
<!-- </my-uni-combox> -->
|
||||
<!-- </view> -->
|
||||
<!-- <view class="flex-twice flex justify-start align-center">
|
||||
<view style="width: 25%;">质保期</view>
|
||||
<my-uni-combox :candidates="productProtectTimes" placeholder="请选择"
|
||||
v-model="formData.productProtectTime"></my-uni-combox>
|
||||
</view> -->
|
||||
<!-- </view> -->
|
||||
<!-- </view> -->
|
||||
<!-- </view> -->
|
||||
</view>
|
||||
<view class="cu-form-group solid-top">
|
||||
|
|
@ -107,7 +107,8 @@
|
|||
<!-- <checkbox style="transform:scale(1)" class="main-color margin-right-xs" :value="true"
|
||||
:v-model="formData.isGoldenServ" :checked="formData.isGoldenServ"></checkbox> -->
|
||||
<view class="title">附加服务</view>
|
||||
<my-uni-combox class="form-val-area" :candidates="servHonorTitle" placeholder="请选择" v-model="formData.servHonorTitle">
|
||||
<my-uni-combox class="form-val-area" :candidates="servHonorTitle" placeholder="请选择"
|
||||
v-model="formData.servHonorTitle">
|
||||
</my-uni-combox>
|
||||
</view>
|
||||
<view class="solid-top">
|
||||
|
|
@ -134,8 +135,8 @@
|
|||
<view class="action text-black">区域描述</view>
|
||||
</view>
|
||||
<view class="padding-lr-sm padding-bottom-sm bg-white">
|
||||
<textarea name="areaDesc" style="width: 100%; height: 150rpx;" class="solid radius text-left padding-sm" v-model="formData.areaDesc"
|
||||
maxlength="-1" placeholder="如:XX区XX街道未覆盖或XX区仅服务XX街道"></textarea>
|
||||
<textarea name="areaDesc" style="width: 100%; height: 150rpx;" class="solid radius text-left padding-sm"
|
||||
v-model="formData.areaDesc" maxlength="-1" placeholder="如:XX区XX街道未覆盖或XX区仅服务XX街道"></textarea>
|
||||
</view>
|
||||
<!-- <view class="cu-form-group">
|
||||
<view class="title">上门费</view>
|
||||
|
|
@ -157,7 +158,7 @@
|
|||
<text class='cuIcon-close'></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="solids" @tap="chooseImgList($event, 'coverImgList')"
|
||||
<view class="solids" @tap="chooseImgList($event, 'coverImgList', 200, 200)"
|
||||
v-if="formData.coverImgList.length === 0">
|
||||
<text class='cuIcon-cameraadd'></text>
|
||||
</view>
|
||||
|
|
@ -203,6 +204,8 @@
|
|||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<ksp-cropper mode="fixed" :width="cropWidth" :height="cropHeight" :maxWidth="1024" :maxHeight="1024" :url="url2Crop"
|
||||
@cancel="oncancel" @ok="onok"></ksp-cropper>
|
||||
<view class="cu-bar bg-white solid-top">
|
||||
<view class="action text-black">
|
||||
视频上传
|
||||
|
|
@ -269,6 +272,10 @@
|
|||
coverImgList: [],
|
||||
videoList: []
|
||||
},
|
||||
url2Crop: '',
|
||||
cropWidth: 200,
|
||||
cropHeight: 300,
|
||||
curImgListField: '',
|
||||
categoryList: [],
|
||||
categoryMultiIndex: [0, 0],
|
||||
regionList: [],
|
||||
|
|
@ -332,15 +339,15 @@
|
|||
async checkBankAndCertify() {
|
||||
// 查询账户绑定信息
|
||||
// let bankCardRes = await this.$request.getBindBankCardByWorkerId({
|
||||
// workerId: this.curUserInfo.workerId
|
||||
// workerId: this.curUserInfo.workerId
|
||||
// });
|
||||
// this.bankCard = bankCardRes.data;
|
||||
// 查询实名信息
|
||||
let certifyInfoRes = await this.$request.getWorkerCertify();
|
||||
this.certifyInfo = certifyInfoRes.data;
|
||||
// if (!this.bankCard || !this.bankCard.bankNum) {
|
||||
// this.$refs.vertifyBankBind.showModal();
|
||||
// return false;
|
||||
// this.$refs.vertifyBankBind.showModal();
|
||||
// return false;
|
||||
if (!this.certifyInfo || !this.certifyInfo.workerCertificationId) {
|
||||
this.$refs.vertifyCertify.showModal();
|
||||
return false;
|
||||
|
|
@ -481,27 +488,37 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
chooseImgList(e, imgListField) {
|
||||
chooseImgList(e, imgListField, cropWidth, cropHeight) {
|
||||
this.cropWidth = cropWidth && cropWidth > 0 ? cropWidth : 200;
|
||||
this.cropHeight = cropHeight && cropHeight > 0 ? cropHeight : 300;
|
||||
uni.chooseImage({
|
||||
count: 9, //默认9
|
||||
count: 1, //默认9
|
||||
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album'], //从相册选择
|
||||
success: (res) => {
|
||||
uni.showLoading({
|
||||
title: '上传中',
|
||||
mask: true
|
||||
});
|
||||
res.tempFilePaths.forEach((tmpUrl, index) => {
|
||||
this.$request.uploadFile(tmpUrl).then((url) => {
|
||||
this.formData[imgListField].push(url);
|
||||
if (index === res.tempFilePaths.length - 1) {
|
||||
uni.hideLoading();
|
||||
}
|
||||
});
|
||||
this.url2Crop = tmpUrl;
|
||||
this.curImgListField = imgListField;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
onok(ev) {
|
||||
uni.showLoading({
|
||||
title: '上传中',
|
||||
mask: true
|
||||
});
|
||||
this.$request.uploadFile(ev.path).then((url) => {
|
||||
this.formData[this.curImgListField].push(url);
|
||||
uni.hideLoading();
|
||||
});
|
||||
// url设置为空,隐藏控件
|
||||
this.url2Crop = "";
|
||||
},
|
||||
oncancel() {
|
||||
// url设置为空,隐藏控件
|
||||
this.url2Crop = "";
|
||||
},
|
||||
viewImage(e, imgList) {
|
||||
uni.previewImage({
|
||||
urls: imgList,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
## 1.1.5(2022-06-14)
|
||||
填新版HBuilderX的坑,简单测试是没问题了。
|
||||
## 1.1.4(2022-02-15)
|
||||
修护ios下微信小程序第一次裁剪的bug
|
||||
## 1.1.3(2022-02-10)
|
||||
修护APP点击无效的bug
|
||||
## 1.1.2(2022-01-24)
|
||||
优化一些细节
|
||||
## 1.1.1(2022-01-19)
|
||||
更新示例项目
|
||||
## 1.1.0(2022-01-18)
|
||||
新增旋转功能
|
||||
## 1.0.2(2022-01-13)
|
||||
修护mode="fixed"模式无效的bug
|
||||
## 1.0.1(2021-12-20)
|
||||
修护IOS下,小程序点击没反应的bug
|
||||
## 1.0.0(2021-12-06)
|
||||
图片裁剪工具
|
||||
|
|
@ -0,0 +1,971 @@
|
|||
<template>
|
||||
<view v-show="url" :mode="modeValue" :change:mode="mwx.changeMode" :rotate="rotate" :change:rotate="mwx.changeRotate">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<canvas type="2d" class="canvas" :style="{width: target.width + 'px', height: target.height + 'px'}"></canvas>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<canvas :canvas-id="canvasId" :style="{width: target.width + 'px', height: target.height + 'px'}"></canvas>
|
||||
<!-- #endif -->
|
||||
<view class="panel">
|
||||
<view class="body" @touchstart="mwx.touchstart" @touchmove="mwx.touchmove" @touchend="mwx.touchend" @touchcancel="mwx.touchcancel">
|
||||
<view class="image-wrap" :class="{transit: transit}" :change:rect="mwx.changeImage" :rect="image">
|
||||
<image class="image" :class="{transit: transit}" :src="url" @load="imageLoad"/>
|
||||
</view>
|
||||
<view class="mask"></view>
|
||||
<view class="frame" :class="{transit: transit}" :change:rect="mwx.changeFrame" :rect="frame">
|
||||
<view class="rect">
|
||||
<view class="image-rect" :class="{transit: transit}">
|
||||
<image class="image" :class="{transit: transit}" :src="url"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-one"></view>
|
||||
<view class="line-two"></view>
|
||||
<view class="line-three"></view>
|
||||
<view class="line-four"></view>
|
||||
<view class="frame-left-top" @touchstart="mwx.touchstart"></view>
|
||||
<view class="frame-left-bottom" @touchstart="mwx.touchstart"></view>
|
||||
<view class="frame-right-top" @touchstart="mwx.touchstart"></view>
|
||||
<view class="frame-right-bottom" @touchstart="mwx.touchstart"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="toolbar">
|
||||
<view @tap="oncancle" class="btn-cancel">取消</view>
|
||||
<view @tap="rotateAngle" class="btn-rotate">旋转</view>
|
||||
<view @tap="onok" class="btn-ok">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
/**
|
||||
* @property {String} mode 模式
|
||||
* @value fixed 固定模式,裁剪出固定大小
|
||||
* @value ratio 等比模式,宽高等比缩放
|
||||
* @value free 自由模式,不限制宽高比
|
||||
* @property {String} url 图片路径
|
||||
* @property {Number} width 宽度
|
||||
* @property {Number} height 高度
|
||||
* @property {Number} maxWidth 最大宽带
|
||||
* @property {Number} minHeight 最大高度
|
||||
*/
|
||||
export default {
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
default: "free"
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
maxWidth: {
|
||||
type: Number,
|
||||
default: 1024
|
||||
},
|
||||
maxHeight: {
|
||||
type: Number,
|
||||
default: 1024
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
canvasId: Math.random().toString(36).slice(-6),
|
||||
real: {
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
target: {
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
body: {
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
frame: {
|
||||
left: 50,
|
||||
top: 50,
|
||||
width: 200,
|
||||
height: 300
|
||||
},
|
||||
image: {
|
||||
left: 20,
|
||||
top: 20,
|
||||
width: 300,
|
||||
height: 400
|
||||
},
|
||||
rotate: 0,
|
||||
transit: false,
|
||||
modeValue: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
imageLoad(event) {
|
||||
uni.getImageInfo({
|
||||
src: this.url,
|
||||
success: (rst) => {
|
||||
this.real.width = rst.width;
|
||||
this.real.height = rst.height;
|
||||
this.target = {};
|
||||
var query = uni.createSelectorQuery().in(this);
|
||||
query.select(".body").boundingClientRect((data) => {
|
||||
this.body.width = data.width;
|
||||
this.body.height = data.height;
|
||||
this.init();
|
||||
}).exec();
|
||||
}
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.modeValue = this.mode;
|
||||
this.rotate = 0;
|
||||
var rate = this.width / this.height;
|
||||
var width = this.body.width * 0.7;
|
||||
var height = this.body.height * 0.7;
|
||||
if (width / height > rate) {
|
||||
width = height * rate;
|
||||
} else {
|
||||
height = width / rate;
|
||||
}
|
||||
var left = (this.body.width - width) / 2;
|
||||
var top = (this.body.height - height) / 2;
|
||||
this.frame = {
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
rate = this.real.width / this.real.height;
|
||||
width = this.frame.width;
|
||||
height = this.frame.height;
|
||||
if (width / height > rate) {
|
||||
height = width / rate;
|
||||
} else {
|
||||
width = height * rate;
|
||||
}
|
||||
left = (this.frame.width - width) / 2 + this.frame.left;
|
||||
top = (this.frame.height - height) / 2 + this.frame.top;
|
||||
this.image = {
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
},
|
||||
async updateData(data) {
|
||||
this.frame = data.frame;
|
||||
this.image = data.image;
|
||||
await this.$nextTick();
|
||||
this.trimImage();
|
||||
},
|
||||
trimImage() {
|
||||
var rate = this.frame.width / this.frame.height;
|
||||
var width = this.body.width * 0.7;
|
||||
var height = this.body.height * 0.7;
|
||||
if (width / height > rate) {
|
||||
width = height * rate;
|
||||
} else {
|
||||
height = width / rate;
|
||||
}
|
||||
var left = (this.body.width - width) / 2;
|
||||
var top = (this.body.height - height) / 2;
|
||||
var mul = width / this.frame.width;
|
||||
var ox = this.frame.left - this.image.left;
|
||||
var oy = this.frame.top - this.image.top;
|
||||
this.frame = {
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
width = this.image.width * mul;
|
||||
height = this.image.height * mul;
|
||||
left = this.frame.left - ox * mul;
|
||||
top = this.frame.top - oy * mul;
|
||||
this.image = {
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
if (mul != 1) {
|
||||
this.transit = true;
|
||||
setTimeout(() => {
|
||||
this.transit = false;
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
rotateAngle() {
|
||||
this.rotate -= 90;
|
||||
var width = this.image.height;
|
||||
var height = this.image.width;
|
||||
var left = this.image.left;
|
||||
var top = this.image.top;
|
||||
var rate = width / height;
|
||||
if (width < this.frame.width) {
|
||||
width = this.frame.width;
|
||||
height = width / rate;
|
||||
}
|
||||
if (height < this.frame.height) {
|
||||
height = this.frame.height;
|
||||
width = height * rate;
|
||||
}
|
||||
if (left > this.frame.left) {
|
||||
left = this.frame.left;
|
||||
}
|
||||
if (top > this.frame.top) {
|
||||
top = this.frame.top;
|
||||
}
|
||||
if (left + width < this.frame.left + this.frame.width) {
|
||||
left = this.frame.left + this.frame.width - width;
|
||||
}
|
||||
if (top + height < this.frame.top + this.frame.height) {
|
||||
top = this.frame.top + this.frame.height - height;
|
||||
}
|
||||
this.image = {
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
this.transit = true;
|
||||
setTimeout(() => {
|
||||
this.transit = false;
|
||||
}, 300);
|
||||
},
|
||||
onok() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.cropWx();
|
||||
// #endif
|
||||
// #ifdef APP-PLUS || H5
|
||||
this.cropAppH5();
|
||||
// #endif
|
||||
},
|
||||
oncancle() {
|
||||
this.$emit("cancel");
|
||||
},
|
||||
async cropWx() {
|
||||
var mx = this.computeMatrix();
|
||||
this.target = {
|
||||
width: mx.tw,
|
||||
height: mx.th
|
||||
};
|
||||
var canvas = await new Promise((resolve) => {
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(".canvas")
|
||||
.fields({node: true})
|
||||
.exec((rst) => {
|
||||
var node = rst[0].node;
|
||||
resolve(node);
|
||||
});
|
||||
});
|
||||
canvas.width = mx.tw;
|
||||
canvas.height = mx.th;
|
||||
uni.showLoading({
|
||||
title: "处理中"
|
||||
});
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
var context = canvas.getContext("2d");
|
||||
var image = canvas.createImage();
|
||||
await new Promise((resolve, reject) => {
|
||||
image.onload = resolve;
|
||||
image.onerror = reject;
|
||||
image.src = this.url;
|
||||
});
|
||||
context.save();
|
||||
context.rotate(this.rotate * Math.PI / 180);
|
||||
context.drawImage(image, mx.sx, mx.sy, mx.sw, mx.sh, mx.dx, mx.dy, mx.dw, mx.dh);
|
||||
context.restore();
|
||||
wx.canvasToTempFilePath({
|
||||
canvas: canvas,
|
||||
destWidth: mx.tw,
|
||||
destHeight: mx.th,
|
||||
success: (rst) => {
|
||||
var path = rst.tempFilePath;
|
||||
this.$emit("ok", {
|
||||
path: path
|
||||
});
|
||||
},
|
||||
complete: () => {
|
||||
uni.hideLoading();
|
||||
}
|
||||
});
|
||||
},
|
||||
async cropAppH5() {
|
||||
var mx = this.computeMatrix();
|
||||
this.target = {
|
||||
width: mx.tw,
|
||||
height: mx.th
|
||||
};
|
||||
uni.showLoading({
|
||||
title: "处理中"
|
||||
});
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
var context = uni.createCanvasContext(this.canvasId, this);
|
||||
context.save();
|
||||
context.rotate(this.rotate * Math.PI / 180);
|
||||
context.drawImage(this.url, mx.sx, mx.sy, mx.sw, mx.sh, mx.dx, mx.dy, mx.dw, mx.dh);
|
||||
context.restore();
|
||||
await new Promise((resolve) => {
|
||||
context.draw(false, resolve);
|
||||
});
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: this.canvasId,
|
||||
destWidth: mx.tw,
|
||||
destHeight: mx.th,
|
||||
success: (rst) => {
|
||||
var path = rst.tempFilePath;
|
||||
// #ifdef H5
|
||||
var base64 = path;
|
||||
path = this.parseBlob(path);
|
||||
this.$emit("ok", {
|
||||
path: path,
|
||||
base64: base64
|
||||
});
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
this.$emit("ok", {
|
||||
path: path
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
complete: () => {
|
||||
uni.hideLoading();
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
computeMatrix() {
|
||||
var width = this.width;
|
||||
var height = this.height;
|
||||
var mul = this.image.width / this.real.width;
|
||||
if (this.rotate % 180 != 0) {
|
||||
mul = this.image.height / this.real.width;
|
||||
}
|
||||
if (this.mode != "fixed") {
|
||||
width = this.frame.width / mul;
|
||||
height = this.frame.height / mul;
|
||||
}
|
||||
var rate = width / height;
|
||||
if (width > this.maxWidth) {
|
||||
width = this.maxWidth;
|
||||
height = width / rate;
|
||||
}
|
||||
if (height > this.maxHeight) {
|
||||
height = this.maxHeight;
|
||||
width = height * rate;
|
||||
}
|
||||
var sx = (this.frame.left - this.image.left) / mul;
|
||||
var sy = (this.frame.top - this.image.top) / mul;
|
||||
var sw = this.frame.width / mul;
|
||||
var sh = this.frame.height / mul;
|
||||
var ox = sx + sw / 2;
|
||||
var oy = sy + sh / 2;
|
||||
if (this.rotate % 180 != 0) {
|
||||
var temp = sw;
|
||||
sw = sh;
|
||||
sh = temp;
|
||||
}
|
||||
var angle = this.rotate % 360;
|
||||
if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
if (angle == 270) {
|
||||
var x = this.real.width - oy;
|
||||
var y = ox;
|
||||
ox = x;
|
||||
oy = y;
|
||||
}
|
||||
if (angle == 180) {
|
||||
var x = this.real.width - ox;
|
||||
var y = this.real.height - oy;
|
||||
ox = x;
|
||||
oy = y;
|
||||
}
|
||||
if (angle == 90) {
|
||||
var x = oy;
|
||||
var y = this.real.height - ox;
|
||||
ox = x;
|
||||
oy = y;
|
||||
}
|
||||
sx = ox - sw / 2;
|
||||
sy = oy - sh / 2;
|
||||
var dr = {x: 0, y: 0, w: width, h: height};
|
||||
dr = this.parseRect(dr, -this.rotate);
|
||||
return {
|
||||
tw: width,
|
||||
th: height,
|
||||
sx: sx,
|
||||
sy: sy,
|
||||
sw: sw,
|
||||
sh: sh,
|
||||
dx: dr.x,
|
||||
dy: dr.y,
|
||||
dw: dr.w,
|
||||
dh: dr.h
|
||||
};
|
||||
},
|
||||
parsePoint(point, angle) {
|
||||
var result = {};
|
||||
result.x = point.x * Math.cos(angle * Math.PI / 180) - point.y * Math.sin(angle * Math.PI / 180);
|
||||
result.y = point.y * Math.cos(angle * Math.PI / 180) + point.x * Math.sin(angle * Math.PI / 180);
|
||||
return result;
|
||||
},
|
||||
parseRect(rect, angle) {
|
||||
var x1 = rect.x;
|
||||
var y1 = rect.y;
|
||||
var x2 = rect.x + rect.w;
|
||||
var y2 = rect.y + rect.h;
|
||||
var p1 = this.parsePoint({x: x1, y: y1}, angle);
|
||||
var p2 = this.parsePoint({x: x2, y: y2}, angle);
|
||||
var result = {};
|
||||
result.x = Math.min(p1.x, p2.x);
|
||||
result.y = Math.min(p1.y, p2.y);
|
||||
result.w = Math.abs(p2.x - p1.x);
|
||||
result.h = Math.abs(p2.y - p1.y);
|
||||
return result;
|
||||
},
|
||||
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}));
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script module="mwx" lang="wxs">
|
||||
var mode = "";
|
||||
var rotate = 0;
|
||||
var image = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
var frame = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
var touches = [];
|
||||
var touchType = "";
|
||||
var start = {
|
||||
frame: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
image: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
};
|
||||
function changeMode(value) {
|
||||
mode = value;
|
||||
}
|
||||
function changeRotate(value, old, oi, instance) {
|
||||
rotate = value;
|
||||
delayUpdateStyle(oi);
|
||||
}
|
||||
function changeImage(value, old, oi, instance) {
|
||||
image = value;
|
||||
delayUpdateStyle(oi);
|
||||
}
|
||||
function changeFrame(value, old, oi, instance) {
|
||||
frame = value;
|
||||
delayUpdateStyle(oi);
|
||||
}
|
||||
function delayUpdateStyle(oi) {
|
||||
// #ifdef APP-PLUS || H5
|
||||
setTimeout(() => {
|
||||
updateStyle(oi);
|
||||
});
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
updateStyle(oi);
|
||||
// #endif
|
||||
}
|
||||
function touchstart(event, oi) {
|
||||
// #ifdef APP-PLUS || H5
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// #endif
|
||||
touches = event.touches;
|
||||
var instance = event.instance;
|
||||
if (instance.hasClass("body")) {
|
||||
touchType = "body";
|
||||
} else if (instance.hasClass("frame-left-top")) {
|
||||
touchType = "left-top";
|
||||
} else if (instance.hasClass("frame-left-bottom")) {
|
||||
touchType = "left-bottom";
|
||||
} else if (instance.hasClass("frame-right-top")) {
|
||||
touchType = "right-top";
|
||||
} else if (instance.hasClass("frame-right-bottom")) {
|
||||
touchType = "right-bottom";
|
||||
}
|
||||
start.frame.left = frame.left;
|
||||
start.frame.top = frame.top;
|
||||
start.frame.width = frame.width;
|
||||
start.frame.height = frame.height;
|
||||
start.image.left = image.left;
|
||||
start.image.top = image.top;
|
||||
start.image.width = image.width;
|
||||
start.image.height = image.height;
|
||||
return false;
|
||||
}
|
||||
function touchmove(event, oi) {
|
||||
// #ifdef APP-PLUS || H5
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// #endif
|
||||
var instance = event.instance;
|
||||
if (touches.length == 1) {
|
||||
if (touchType == "body") {
|
||||
moveImage(touches[0], event.touches[0], oi);
|
||||
} else {
|
||||
scaleFrame(touches[0], event.touches[0], oi);
|
||||
}
|
||||
} else if (touches.length == 2 && event.touches.length == 2) {
|
||||
var ta = touches[0];
|
||||
var tb = touches[1];
|
||||
var tc = event.touches[0];
|
||||
var td = event.touches[1];
|
||||
if (ta.identifier != tc.identifier) {
|
||||
var temp = tc;
|
||||
tc = td;
|
||||
td = temp;
|
||||
}
|
||||
scaleImage(ta, tb, tc, td, oi);
|
||||
}
|
||||
}
|
||||
function touchend(event, oi) {
|
||||
touches = [];
|
||||
oi.callMethod("updateData", {frame: frame, image: image});
|
||||
}
|
||||
function touchcancel(event, oi) {
|
||||
touches = [];
|
||||
oi.callMethod("updateData", {frame: frame, image: image});
|
||||
}
|
||||
function moveImage(ta, tb, oi) {
|
||||
var ax = tb.clientX - ta.clientX;
|
||||
var ay = tb.clientY - ta.clientY;
|
||||
image.left = start.image.left + ax;
|
||||
image.top = start.image.top + ay;
|
||||
var left = frame.left;
|
||||
var top = frame.top;
|
||||
var width = frame.width;
|
||||
var height = frame.height;
|
||||
if (image.left > left) {
|
||||
image.left = left;
|
||||
}
|
||||
if (image.top > top) {
|
||||
image.top = top;
|
||||
}
|
||||
if (image.left + image.width < left + width) {
|
||||
image.left = left + width - image.width;
|
||||
}
|
||||
if (image.top + image.height < top + height) {
|
||||
image.top = top + height - image.height;
|
||||
}
|
||||
updateStyle(oi);
|
||||
}
|
||||
function scaleImage(ta, tb, tc, td, oi) {
|
||||
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 (start.image.width * scale < frame.width) {
|
||||
scale = frame.width / start.image.width;
|
||||
}
|
||||
if (start.image.height * scale < frame.height) {
|
||||
scale = frame.height / start.image.height;
|
||||
}
|
||||
if (start.image.width * scale < frame.width) {
|
||||
scale = frame.width / start.image.width;
|
||||
}
|
||||
image.left = start.image.left + ax - (ocx - start.image.left) * (scale - 1);
|
||||
image.top = start.image.top + ay - (ocy - start.image.top) * (scale - 1);
|
||||
image.width = start.image.width * scale;
|
||||
image.height = start.image.height * scale;
|
||||
if (image.left > frame.left) {
|
||||
image.left = frame.left;
|
||||
}
|
||||
if (image.top > frame.top) {
|
||||
image.top = frame.top;
|
||||
}
|
||||
if (image.left + image.width < frame.left + frame.width) {
|
||||
image.left = frame.left + frame.width - image.width;
|
||||
}
|
||||
if (image.top + image.height < frame.top + frame.height) {
|
||||
image.top = frame.top + frame.height - image.height;
|
||||
}
|
||||
updateStyle(oi);
|
||||
}
|
||||
function scaleFrame(ta, tb, oi) {
|
||||
var ax = tb.clientX - ta.clientX;
|
||||
var ay = tb.clientY - ta.clientY;
|
||||
var x1 = start.frame.left;
|
||||
var y1 = start.frame.top;
|
||||
var x2 = start.frame.left + start.frame.width;
|
||||
var y2 = start.frame.top + start.frame.height;
|
||||
var cx1 = false;
|
||||
var cy1 = false;
|
||||
var cx2 = false;
|
||||
var cy2 = false;
|
||||
var mix = 30;
|
||||
var rate = frame.width / frame.height;
|
||||
if (touchType == "left-top") {
|
||||
x1 += ax;
|
||||
y1 += ay;
|
||||
cx1 = true;
|
||||
cy1 = true;
|
||||
} else if (touchType == "left-bottom") {
|
||||
x1 += ax;
|
||||
y2 += ay;
|
||||
cx1 = true;
|
||||
cy2 = true;
|
||||
} else if (touchType == "right-top") {
|
||||
x2 += ax;
|
||||
y1 += ay;
|
||||
cx2 = true;
|
||||
cy1 = true;
|
||||
} else if (touchType == "right-bottom") {
|
||||
x2 += ax;
|
||||
y2 += ay;
|
||||
cx2 = true;
|
||||
cy2 = true;
|
||||
}
|
||||
if (x1 < image.left) {
|
||||
x1 = image.left;
|
||||
}
|
||||
if (y1 < image.top) {
|
||||
y1 = image.top;
|
||||
}
|
||||
if (x2 > image.left + image.width) {
|
||||
x2 = image.left + image.width;
|
||||
}
|
||||
if (y2 > image.top + image.height) {
|
||||
y2 = image.top + image.height;
|
||||
}
|
||||
if (cx1) {
|
||||
if (x1 > x2 - mix) {
|
||||
x1 = x2 - mix;
|
||||
}
|
||||
}
|
||||
if (cy1) {
|
||||
if (y1 > y2 - mix) {
|
||||
y1 = y2 - mix;
|
||||
}
|
||||
}
|
||||
if (cx2) {
|
||||
if (x2 < x1 + mix) {
|
||||
x2 = x1 + mix;
|
||||
}
|
||||
}
|
||||
if (cy2) {
|
||||
if (y2 < y1 + mix) {
|
||||
y2 = y1 + mix;
|
||||
}
|
||||
}
|
||||
if (cx1) {
|
||||
if (mode != "free") {
|
||||
var val = x2 - rate * (y2 - y1);
|
||||
if (x1 < val) {
|
||||
x1 = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cy1) {
|
||||
if (mode != "free") {
|
||||
var val = y2 - (x2 - x1) / rate;
|
||||
if (y1 < val) {
|
||||
y1 = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cx2) {
|
||||
if (mode != "free") {
|
||||
var val = rate * (y2 - y1) + x1;
|
||||
if (x2 > val) {
|
||||
x2 = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cy2) {
|
||||
if (mode != "free") {
|
||||
var val = (x2 - x1) / rate + y1;
|
||||
if (y2 > val) {
|
||||
y2 = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
frame.left = x1;
|
||||
frame.top = y1;
|
||||
frame.width = x2 - x1;
|
||||
frame.height = y2 - y1;
|
||||
updateStyle(oi);
|
||||
}
|
||||
function updateStyle(oi) {
|
||||
oi.selectComponent(".frame").setStyle({
|
||||
"left": frame.left + "px",
|
||||
"top": frame.top + "px",
|
||||
"width": frame.width + "px",
|
||||
"height": frame.height + "px"
|
||||
});
|
||||
oi.selectComponent(".image-wrap").setStyle({
|
||||
"left": image.left + "px",
|
||||
"top": image.top + "px",
|
||||
"width": image.width + "px",
|
||||
"height": image.height + "px"
|
||||
});
|
||||
oi.selectComponent(".image-rect").setStyle({
|
||||
"left": image.left - frame.left + "px",
|
||||
"top": image.top - frame.top + "px",
|
||||
"width": image.width + "px",
|
||||
"height": image.height + "px"
|
||||
});
|
||||
var left = 0;
|
||||
var top = 0;
|
||||
var width = image.width;
|
||||
var height = image.height;
|
||||
if (rotate % 180 != 0) {
|
||||
width = image.height;
|
||||
height = image.width;
|
||||
top = width / 2 - height / 2;
|
||||
left = height / 2 - width/ 2;
|
||||
}
|
||||
oi.selectComponent(".image-wrap .image").setStyle({
|
||||
"left": left + "px",
|
||||
"top": top + "px",
|
||||
"width": width + "px",
|
||||
"height": height + "px",
|
||||
"transform": "rotate(" + rotate + "deg)"
|
||||
});
|
||||
oi.selectComponent(".image-rect .image").setStyle({
|
||||
"left": left + "px",
|
||||
"top": top + "px",
|
||||
"width": width + "px",
|
||||
"height": height + "px",
|
||||
"transform": "rotate(" + rotate + "deg)"
|
||||
});
|
||||
}
|
||||
module.exports = {
|
||||
changeMode: changeMode,
|
||||
changeRotate: changeRotate,
|
||||
changeImage: changeImage,
|
||||
changeFrame: changeFrame,
|
||||
touchstart: touchstart,
|
||||
touchmove: touchmove,
|
||||
touchend: touchend,
|
||||
touchcancel: touchcancel
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.panel {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
}
|
||||
.canvas {
|
||||
position: absolute;
|
||||
top: 5000px;
|
||||
left: 5000px;
|
||||
}
|
||||
.toolbar {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
left: 0rpx;
|
||||
bottom: 0rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.btn-cancel {
|
||||
font-size: 40rpx;
|
||||
color: #d5dfe5;
|
||||
font-weight: bold;
|
||||
}
|
||||
.btn-ok {
|
||||
font-size: 40rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
.btn-rotate {
|
||||
font-size: 40rpx;
|
||||
color: #d5dfe5;
|
||||
font-weight: bold;
|
||||
}
|
||||
.body {
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
right: 0rpx;
|
||||
top: 0rpx;
|
||||
bottom: 0rpx;
|
||||
background: black;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mask {
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
right: 0rpx;
|
||||
top: 0rpx;
|
||||
bottom: 0rpx;
|
||||
background: black;
|
||||
opacity: 0.4;
|
||||
}
|
||||
.plank {
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
right: 0rpx;
|
||||
top: 0rpx;
|
||||
bottom: 0rpx;
|
||||
}
|
||||
.image-wrap {
|
||||
position: absolute;
|
||||
}
|
||||
.image-rect {
|
||||
position: absolute;
|
||||
}
|
||||
.image {
|
||||
position: absolute;
|
||||
}
|
||||
.frame {
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 100px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
.rect {
|
||||
position: absolute;
|
||||
left: -2px;
|
||||
top: -2px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid white;
|
||||
overflow: hidden;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.line-one {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: white;
|
||||
left: 0;
|
||||
top: 33.3%;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.line-two {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: white;
|
||||
left: 0;
|
||||
top: 66.7%;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.line-three {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: white;
|
||||
top: 0;
|
||||
left: 33.3%;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.line-four {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: white;
|
||||
top: 0;
|
||||
left: 66.7%;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.frame-left-top {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
left: -6px;
|
||||
top: -6px;
|
||||
border-left: 4px solid red;
|
||||
border-top: 4px solid red;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.frame-left-bottom {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
left: -6px;
|
||||
bottom: -6px;
|
||||
border-left: 4px solid red;
|
||||
border-bottom: 4px solid red;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.frame-right-top {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
right: -6px;
|
||||
top: -6px;
|
||||
border-right: 4px solid red;
|
||||
border-top: 4px solid red;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.frame-right-bottom {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
right: -6px;
|
||||
bottom: -6px;
|
||||
border-right: 4px solid red;
|
||||
border-bottom: 4px solid red;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
.transit {
|
||||
transition: width 0.3s, height 0.3s, left 0.3s, top 0.3s, transform 0.3s;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"id": "ksp-cropper",
|
||||
"displayName": "ksp-cropper",
|
||||
"version": "1.1.5",
|
||||
"description": "高性能图片裁剪工具",
|
||||
"keywords": [
|
||||
"头像",
|
||||
"图片",
|
||||
"裁剪"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": {
|
||||
"minVersion": "2.9.0"
|
||||
},
|
||||
"阿里": "n",
|
||||
"百度": "n",
|
||||
"字节跳动": "n",
|
||||
"QQ": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
# ksp-cropper
|
||||
|
||||
## 高性能图片裁剪工具
|
||||
|
||||
### 属性说明
|
||||
|属性 |类型 |默认 |备注 |
|
||||
| :--------: | :-----: | :----: | :----: |
|
||||
| url |String | "" | 需要裁剪的图片路径,为空时控件隐藏,不为空时控件显示|
|
||||
| mode |String | "free" | 裁剪模式|
|
||||
| width |Number | 200 | 图片裁剪后的宽度,固定大小时有效|
|
||||
| height |Number | 200 | 图片裁剪后的高度,固定大小时有效|
|
||||
| maxWidth |Number | 1024 | 图片裁剪后的最大宽度 |
|
||||
| maxHeight |Number | 1024 | 图片裁剪后的最大高度 |
|
||||
|
||||
### mode有效值
|
||||
|
||||
| 模式 |值 |说明 |
|
||||
| :-----: | :-----: | :----: |
|
||||
| 固定模式 |fixed | 裁剪出指定大小的图片,一般用于头像上传 |
|
||||
| 等比缩放 |ratio | 限定宽高比,裁剪大小不固定 |
|
||||
| 自由模式 |free | 不限定宽高比,裁剪大小不固定 |
|
||||
|
||||
### 事件说明
|
||||
|事件名称 |说明 |返回 |
|
||||
| :--------: | :-----: | :----: |
|
||||
| ok |点击确定按钮 | e:{path} |
|
||||
| cancel |点击取消按钮 | - |
|
||||
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<button @click="select">选择图片</button>
|
||||
<image mode="widthFix" :src="path"/>
|
||||
<ksp-cropper mode="free" :width="200" :height="140" :maxWidth="1024" :maxHeight="1024" :url="url" @cancel="oncancel" @ok="onok"></ksp-cropper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
url: "",
|
||||
path: ""
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
},
|
||||
methods: {
|
||||
select() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (rst) => {
|
||||
// 设置url的值,显示控件
|
||||
this.url = rst.tempFilePaths[0];
|
||||
}
|
||||
});
|
||||
},
|
||||
onok(ev) {
|
||||
this.url = "";
|
||||
this.path = ev.path;
|
||||
},
|
||||
oncancel() {
|
||||
// url设置为空,隐藏控件
|
||||
this.url = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
Loading…
Reference in New Issue