报名工具小程序初始代码
This commit is contained in:
697
components/cropper/cropper.vue
Normal file
697
components/cropper/cropper.vue
Normal file
@@ -0,0 +1,697 @@
|
||||
<template>
|
||||
<view class="cropper" id="cropper" :class="{ show: show }">
|
||||
<view class="cropper-head">
|
||||
<view class="cropper-btn cropper-reset" @tap="resetCrop">重做</view>
|
||||
</view>
|
||||
<view class="cropper-body">
|
||||
<image id="image" class="cropper-image" :src="imagePath" mode="aspectFit"></image>
|
||||
<view
|
||||
:style="{ width: stageWidth + 'px', height: stageHeight + 'px', left: stageLeft + 'px', top: stageTop + 'px' }"
|
||||
class="cropper-stage" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="touchMove">
|
||||
<view id="box" class="cropper-box"
|
||||
:style="{ width: boxWidth + 'px', height: boxHeight + 'px', left: boxLeft + 'px', top: boxTop + 'px' }">
|
||||
<view id="lt" class="lt"></view>
|
||||
<view id="lb" class="lb"></view>
|
||||
<view id="rt" class="rt"></view>
|
||||
<view id="rb" class="rb"></view>
|
||||
|
||||
<view class="line-v" style="left:33.3%;"></view>
|
||||
<view class="line-v" style="left:66.6%;"></view>
|
||||
<view class="line-h" style="top:33.3%;"></view>
|
||||
<view class="line-h" style="top:66.6%;"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<canvas class="cropper-canvas" canvas-id="canvas"
|
||||
:style="{ height: canvasHeight + 'px', width: canvasWidth + 'px' }"></canvas>
|
||||
</view>
|
||||
<view class="cropper-bottom">
|
||||
<view class="cropper-btn cropper-cancel" @tap="cancelCrop">取消</view>
|
||||
<view class="cropper-btn cropper-ok" @tap="completeCrop">裁剪</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
//无须渲染的变量
|
||||
|
||||
let layoutLeft = 0;
|
||||
let layoutTop = 0;
|
||||
let layoutWidth = 0;
|
||||
let layoutHeight = 0;
|
||||
|
||||
let stageLeft = 0;
|
||||
let stageTop = 0;
|
||||
let stageWidth = 0;
|
||||
let stageHeight = 0;
|
||||
|
||||
let imageWidth = 0;
|
||||
let imageHeight = 0;
|
||||
|
||||
let pixelRatio = 1; //todo设备像素密度//暂不使用//
|
||||
|
||||
let imageStageRatio = 1; //图片实际尺寸与剪裁舞台大小的比值,用于尺寸换算。
|
||||
|
||||
let minBoxWidth = 0;
|
||||
let minBoxHeight = 0;
|
||||
|
||||
let touchStartBoxLeft = 0;
|
||||
let touchStartBoxTop = 0;
|
||||
let touchStartBoxWidth = 0;
|
||||
let touchStartBoxHeight = 0;
|
||||
|
||||
let touchStartX = 0;
|
||||
let touchStartY = 0;
|
||||
|
||||
export default {
|
||||
name: 'cropper',
|
||||
props: {
|
||||
quality: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
|
||||
//目标文件的类型。默认值为jpg,jpg:输出jpg格式图片;png:输出png格式图片
|
||||
outputFileType: {
|
||||
type: String,
|
||||
default: 'jpg'
|
||||
},
|
||||
//目标图片的宽高比,默认null,即不限制剪裁宽高比。aspectRatio需大于0
|
||||
aspectRatio: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
//最小剪裁尺寸与原图尺寸的比率,默认0.15,即宽度最小剪裁到原图的0.15宽。
|
||||
minBoxWidthRatio: {
|
||||
type: Number,
|
||||
default: 0.15
|
||||
},
|
||||
//同minBoxWidthRatio,当设置aspectRatio时,minBoxHeight值设置无效。minBoxHeight值由minBoxWidth 和 aspectRatio自动计算得到。
|
||||
minBoxHeightRatio: {
|
||||
type: Number,
|
||||
default: 0.15
|
||||
},
|
||||
//剪裁框初始大小比率。默认值0.8,即剪裁框默认宽度为图片宽度的0.8倍。
|
||||
initialBoxWidthRatio: {
|
||||
type: Number,
|
||||
default: 0.8
|
||||
},
|
||||
//同initialBoxWidthRatio,当设置aspectRatio时,initialBoxHeightRatio值设置无效。initialBoxHeightRatio值由initialBoxWidthRatio 和 aspectRatio自动计算得到。
|
||||
initialBoxHeightRatio: {
|
||||
type: Number,
|
||||
default: 0.8
|
||||
},
|
||||
// 保存的模式 None(无限制)ScaleTo500(缩放到指定的尺寸 如 500宽度) Max500(不超过500宽度)
|
||||
saveMode: {
|
||||
type: String,
|
||||
default: "None"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//data
|
||||
stageLeft: 0,
|
||||
stageTop: 0,
|
||||
stageWidth: 0,
|
||||
stageHeight: 0,
|
||||
|
||||
boxWidth: 0,
|
||||
boxHeight: 0,
|
||||
boxLeft: 0,
|
||||
boxTop: 0,
|
||||
|
||||
canvasWidth: 0,
|
||||
canvasHeight: 0,
|
||||
show: false,
|
||||
imagePath: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
resetCrop() {
|
||||
this.$emit('reset');
|
||||
this.init(this.imagePath);
|
||||
},
|
||||
cancelCrop() {
|
||||
this.$emit('cancel');
|
||||
},
|
||||
completeCrop() {
|
||||
|
||||
let imagePath = this.imagePath;
|
||||
let canvasContext = uni.createCanvasContext('canvas', this);
|
||||
|
||||
let boxLeft = this.boxLeft;
|
||||
let boxTop = this.boxTop;
|
||||
let boxWidth = this.boxWidth;
|
||||
let boxHeight = this.boxHeight;
|
||||
|
||||
let sx = Math.ceil(boxLeft * imageStageRatio);
|
||||
let sy = Math.ceil(boxTop * imageStageRatio);
|
||||
|
||||
let sWidth = Math.ceil(boxWidth * imageStageRatio);
|
||||
let sHeight = Math.ceil(boxHeight * imageStageRatio);
|
||||
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
|
||||
let dWidth = Math.ceil(sWidth * pixelRatio);
|
||||
let dHeight = Math.ceil(sHeight * pixelRatio);
|
||||
const param = {
|
||||
x: sx,
|
||||
y: sy,
|
||||
width: dWidth,
|
||||
height: dHeight,
|
||||
rotate: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1
|
||||
};
|
||||
|
||||
|
||||
let _this = this
|
||||
|
||||
canvasContext.drawImage(imagePath, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
|
||||
canvasContext.draw(false, () => {
|
||||
|
||||
let targetWidth = 0
|
||||
let targetHeight = 0
|
||||
let mode = _this.saveMode
|
||||
|
||||
if (_this.IsEmpty(mode) || mode == "None") {
|
||||
targetWidth = sWidth
|
||||
targetHeight = sHeight
|
||||
} else {
|
||||
|
||||
if (mode.startsWith("ScaleTo")) {
|
||||
let w = Number(mode.substring(7, mode.length))
|
||||
if (isNaN(w)) {
|
||||
targetWidth = sWidth
|
||||
targetHeight = sHeight
|
||||
} else {
|
||||
// 缩放到指定
|
||||
targetWidth = w
|
||||
targetHeight = sHeight * w / sWidth
|
||||
}
|
||||
} else if (mode.startsWith("Max")) {
|
||||
let w = Number(mode.substring(3, mode.length))
|
||||
if (isNaN(w)) {
|
||||
targetWidth = sWidth
|
||||
targetHeight = sHeight
|
||||
} else {
|
||||
if (sWidth > w) {
|
||||
// 超出缩小
|
||||
targetWidth = w
|
||||
targetHeight = sHeight * w / sWidth
|
||||
} else {
|
||||
targetWidth = sWidth
|
||||
targetHeight = sHeight
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targetWidth = sWidth
|
||||
targetHeight = sHeight
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uni.canvasToTempFilePath({
|
||||
x: dx,
|
||||
y: dy,
|
||||
width: dWidth,
|
||||
height: dHeight,
|
||||
destWidth: targetWidth,
|
||||
destHeight: targetHeight,
|
||||
canvasId: 'canvas',
|
||||
fileType: this.outputFileType,
|
||||
quality: this.quality,
|
||||
success: res => {
|
||||
this.$emit('complete', {
|
||||
param,
|
||||
path: res.tempFilePath,
|
||||
source: this.imagePath
|
||||
});
|
||||
}
|
||||
},
|
||||
this
|
||||
);
|
||||
});
|
||||
},
|
||||
touchMove(e) {
|
||||
let targetId = e.target.id;
|
||||
let touch = e.touches[0];
|
||||
let pageX = touch.pageX;
|
||||
let pageY = touch.pageY;
|
||||
|
||||
let offsetX = pageX - touchStartX;
|
||||
let offsetY = pageY - touchStartY;
|
||||
|
||||
if (targetId == 'box') {
|
||||
let newBoxLeft = touchStartBoxLeft + offsetX;
|
||||
let newBoxTop = touchStartBoxTop + offsetY;
|
||||
|
||||
if (newBoxLeft < 0) {
|
||||
newBoxLeft = 0;
|
||||
}
|
||||
if (newBoxTop < 0) {
|
||||
newBoxTop = 0;
|
||||
}
|
||||
if (newBoxLeft + touchStartBoxWidth > stageWidth) {
|
||||
newBoxLeft = stageWidth - touchStartBoxWidth;
|
||||
}
|
||||
if (newBoxTop + touchStartBoxHeight > stageHeight) {
|
||||
newBoxTop = stageHeight - touchStartBoxHeight;
|
||||
}
|
||||
this.boxLeft = newBoxLeft;
|
||||
this.boxTop = newBoxTop;
|
||||
} else if (targetId == 'lt') {
|
||||
if (this.aspectRatio) {
|
||||
offsetY = offsetX / this.aspectRatio;
|
||||
}
|
||||
|
||||
let newBoxLeft = touchStartBoxLeft + offsetX;
|
||||
let newBoxTop = touchStartBoxTop + offsetY;
|
||||
|
||||
if (newBoxLeft < 0) {
|
||||
newBoxLeft = 0;
|
||||
}
|
||||
if (newBoxTop < 0) {
|
||||
newBoxTop = 0;
|
||||
}
|
||||
|
||||
if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
|
||||
newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
|
||||
}
|
||||
if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
|
||||
newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
|
||||
}
|
||||
|
||||
let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
|
||||
let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);
|
||||
|
||||
//约束比例
|
||||
if (newBoxTop == 0 && this.aspectRatio && newBoxLeft != 0) {
|
||||
newBoxWidth = newBoxHeight * this.aspectRatio;
|
||||
newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
|
||||
}
|
||||
if (newBoxLeft == 0 && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
|
||||
}
|
||||
|
||||
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
|
||||
}
|
||||
this.boxTop = newBoxTop;
|
||||
this.boxLeft = newBoxLeft;
|
||||
this.boxWidth = newBoxWidth;
|
||||
this.boxHeight = newBoxHeight;
|
||||
} else if (targetId == 'rt') {
|
||||
if (this.aspectRatio) {
|
||||
offsetY = -offsetX / this.aspectRatio;
|
||||
}
|
||||
|
||||
let newBoxWidth = touchStartBoxWidth + offsetX;
|
||||
if (newBoxWidth < minBoxWidth) {
|
||||
newBoxWidth = minBoxWidth;
|
||||
}
|
||||
if (touchStartBoxLeft + newBoxWidth > stageWidth) {
|
||||
newBoxWidth = stageWidth - touchStartBoxLeft;
|
||||
}
|
||||
|
||||
let newBoxTop = touchStartBoxTop + offsetY;
|
||||
|
||||
if (newBoxTop < 0) {
|
||||
newBoxTop = 0;
|
||||
}
|
||||
|
||||
if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
|
||||
newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
|
||||
}
|
||||
let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);
|
||||
|
||||
//约束比例
|
||||
if (newBoxTop == 0 && this.aspectRatio && newBoxWidth != stageWidth - touchStartBoxLeft) {
|
||||
newBoxWidth = newBoxHeight * this.aspectRatio;
|
||||
}
|
||||
|
||||
if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
|
||||
}
|
||||
|
||||
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
|
||||
}
|
||||
|
||||
this.boxTop = newBoxTop;
|
||||
this.boxHeight = newBoxHeight;
|
||||
this.boxWidth = newBoxWidth;
|
||||
} else if (targetId == 'lb') {
|
||||
if (this.aspectRatio) {
|
||||
offsetY = -offsetX / this.aspectRatio;
|
||||
}
|
||||
let newBoxLeft = touchStartBoxLeft + offsetX;
|
||||
|
||||
if (newBoxLeft < 0) {
|
||||
newBoxLeft = 0;
|
||||
}
|
||||
if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
|
||||
newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
|
||||
}
|
||||
|
||||
let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
|
||||
|
||||
let newBoxHeight = touchStartBoxHeight + offsetY;
|
||||
if (newBoxHeight < minBoxHeight) {
|
||||
newBoxHeight = minBoxHeight;
|
||||
}
|
||||
if (touchStartBoxTop + newBoxHeight > stageHeight) {
|
||||
newBoxHeight = stageHeight - touchStartBoxTop;
|
||||
}
|
||||
|
||||
//约束比例
|
||||
if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxLeft != 0) {
|
||||
newBoxWidth = newBoxHeight * this.aspectRatio;
|
||||
newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
|
||||
}
|
||||
if (newBoxLeft == 0 && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
}
|
||||
|
||||
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
}
|
||||
|
||||
this.boxLeft = newBoxLeft;
|
||||
this.boxWidth = newBoxWidth;
|
||||
this.boxHeight = newBoxHeight;
|
||||
} else if (targetId == 'rb') {
|
||||
if (this.aspectRatio) {
|
||||
offsetY = offsetX / this.aspectRatio;
|
||||
}
|
||||
let newBoxWidth = touchStartBoxWidth + offsetX;
|
||||
if (newBoxWidth < minBoxWidth) {
|
||||
newBoxWidth = minBoxWidth;
|
||||
}
|
||||
if (touchStartBoxLeft + newBoxWidth > stageWidth) {
|
||||
newBoxWidth = stageWidth - touchStartBoxLeft;
|
||||
}
|
||||
|
||||
let newBoxHeight = touchStartBoxHeight + offsetY;
|
||||
if (newBoxHeight < minBoxHeight) {
|
||||
newBoxHeight = minBoxHeight;
|
||||
}
|
||||
if (touchStartBoxTop + newBoxHeight > stageHeight) {
|
||||
newBoxHeight = stageHeight - touchStartBoxTop;
|
||||
}
|
||||
|
||||
//约束比例
|
||||
if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxWidth != stageWidth -
|
||||
touchStartBoxLeft) {
|
||||
newBoxWidth = newBoxHeight * this.aspectRatio;
|
||||
}
|
||||
|
||||
if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
}
|
||||
|
||||
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
|
||||
newBoxHeight = newBoxWidth / this.aspectRatio;
|
||||
}
|
||||
|
||||
this.boxWidth = newBoxWidth;
|
||||
this.boxHeight = newBoxHeight;
|
||||
}
|
||||
},
|
||||
touchStart(e) {
|
||||
let touch = e.touches[0];
|
||||
let pageX = touch.pageX;
|
||||
let pageY = touch.pageY;
|
||||
|
||||
touchStartX = pageX;
|
||||
touchStartY = pageY;
|
||||
|
||||
touchStartBoxLeft = this.boxLeft;
|
||||
touchStartBoxTop = this.boxTop;
|
||||
touchStartBoxWidth = this.boxWidth;
|
||||
touchStartBoxHeight = this.boxHeight;
|
||||
},
|
||||
close(force = true) {
|
||||
this.show = false;
|
||||
if (force) {
|
||||
this.imagePath = ''
|
||||
}
|
||||
},
|
||||
init(src) {
|
||||
|
||||
let _this = this
|
||||
|
||||
if (!src) {
|
||||
return '';
|
||||
}
|
||||
this.imagePath = src;
|
||||
uni.showLoading({
|
||||
mask: true,
|
||||
title: '载入图片中'
|
||||
});
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('.cropper-body')
|
||||
.boundingClientRect(rect => {
|
||||
|
||||
layoutLeft = rect.left;
|
||||
layoutTop = rect.top;
|
||||
layoutWidth = rect.width;
|
||||
layoutHeight = rect.height;
|
||||
|
||||
uni.getImageInfo({
|
||||
src: _this.imagePath,
|
||||
success: imageInfo => {
|
||||
imageWidth = imageInfo.width;
|
||||
imageHeight = imageInfo.height;
|
||||
let imageWH = imageWidth / imageHeight;
|
||||
let layoutWH = layoutWidth / layoutHeight;
|
||||
if (imageWH >= layoutWH) {
|
||||
stageWidth = layoutWidth;
|
||||
stageHeight = stageWidth / imageWH;
|
||||
imageStageRatio = imageHeight / stageHeight;
|
||||
} else {
|
||||
stageHeight = layoutHeight;
|
||||
stageWidth = layoutHeight * imageWH;
|
||||
imageStageRatio = imageWidth / stageWidth;
|
||||
}
|
||||
stageLeft = (layoutWidth - stageWidth) / 2;
|
||||
stageTop = (layoutHeight - stageHeight) / 2;
|
||||
|
||||
minBoxWidth = stageWidth * _this.minBoxWidthRatio;
|
||||
minBoxHeight = stageHeight * _this.minBoxHeightRatio;
|
||||
|
||||
let boxWidth = stageWidth * _this.initialBoxWidthRatio;
|
||||
let boxHeight = stageHeight * _this.initialBoxHeightRatio;
|
||||
|
||||
if (_this.aspectRatio) {
|
||||
boxHeight = boxWidth / _this.aspectRatio;
|
||||
}
|
||||
if (boxHeight > stageHeight) {
|
||||
boxHeight = stageHeight;
|
||||
boxWidth = boxHeight * _this.aspectRatio;
|
||||
}
|
||||
|
||||
let boxLeft = (stageWidth - boxWidth) / 2;
|
||||
let boxTop = (stageHeight - boxHeight) / 2;
|
||||
|
||||
_this.canvasWidth = imageWidth * pixelRatio;
|
||||
_this.canvasHeight = imageHeight * pixelRatio;
|
||||
|
||||
_this.stageLeft = stageLeft;
|
||||
_this.stageTop = stageTop;
|
||||
_this.stageWidth = stageWidth;
|
||||
_this.stageHeight = stageHeight;
|
||||
|
||||
_this.boxWidth = boxWidth;
|
||||
_this.boxHeight = boxHeight;
|
||||
_this.boxLeft = boxLeft;
|
||||
_this.boxTop = boxTop;
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
_this.show = true;
|
||||
}, 100);
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '图片载入失败'
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.cropper {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #000;
|
||||
z-index: -1000000;
|
||||
opacity: 0;
|
||||
|
||||
&.show {
|
||||
z-index: 9901;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cropper-head {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 750rpx;
|
||||
z-index: 6;
|
||||
height: calc(var(--status-bar-height) + 88rpx);
|
||||
padding-top: var(--status-bar-height);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cropper-btn {
|
||||
height: 64rpx;
|
||||
margin: 0 20rpx;
|
||||
padding: 0 30rpx;
|
||||
line-height: 64rpx;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.cropper-body {
|
||||
margin: calc(var(--status-bar-height) + 88rpx) 30rpx 0 30rpx;
|
||||
height: calc(100vh - var(--status-bar-height) - 88rpx - 100rpx - var(--safe-area-inset-bottom));
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cropper-bottom {
|
||||
height: calc(var(--safe-area-inset-bottom) + 100rpx);
|
||||
padding-top: var(--safe-area-inset-bottom);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: fixed;
|
||||
z-index: 6;
|
||||
width: 750rpx;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.cropper-ok {
|
||||
color: #39f;
|
||||
}
|
||||
|
||||
// 此属性导致Android显示异常
|
||||
.cropper-image {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cropper-stage {
|
||||
position: absolute;
|
||||
|
||||
.cropper-box {
|
||||
position: absolute;
|
||||
border: 4rpx solid #ddd;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 0 2000rpx rgba(0, 0, 0, 0.5);
|
||||
|
||||
.lt {
|
||||
position: absolute;
|
||||
height: 48rpx;
|
||||
width: 48rpx;
|
||||
left: -6rpx;
|
||||
top: -6rpx;
|
||||
border-left: 12rpx solid #ffffff;
|
||||
border-top: 12rpx solid #ffffff;
|
||||
}
|
||||
|
||||
.lb {
|
||||
position: absolute;
|
||||
height: 48rpx;
|
||||
width: 48rpx;
|
||||
left: -6rpx;
|
||||
bottom: -6rpx;
|
||||
border-left: 12rpx solid #ffffff;
|
||||
border-bottom: 12rpx solid #ffffff;
|
||||
}
|
||||
|
||||
.rt {
|
||||
position: absolute;
|
||||
height: 48rpx;
|
||||
width: 48rpx;
|
||||
right: -6rpx;
|
||||
top: -6rpx;
|
||||
border-right: 12rpx solid #ffffff;
|
||||
border-top: 12rpx solid #ffffff;
|
||||
}
|
||||
|
||||
.rb {
|
||||
position: absolute;
|
||||
height: 48rpx;
|
||||
width: 48rpx;
|
||||
right: -6rpx;
|
||||
bottom: -6rpx;
|
||||
border-right: 12rpx solid #ffffff;
|
||||
border-bottom: 12rpx solid #ffffff;
|
||||
}
|
||||
|
||||
.line-v,
|
||||
.line-h {
|
||||
position: absolute;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.line-v {
|
||||
width: 2rpx;
|
||||
border-left: 2rpx dashed #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.line-h {
|
||||
height: 2rpx;
|
||||
border-bottom: 2rpx dashed #fff;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-canvas {
|
||||
position: fixed;
|
||||
background-color: red;
|
||||
left: 5000rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 安全域兼容样式
|
||||
page {
|
||||
--safe-area-inset-top: 0px;
|
||||
--safe-area-inset-right: 0px;
|
||||
--safe-area-inset-bottom: 0px;
|
||||
--safe-area-inset-left: 0px;
|
||||
|
||||
@supports (top: constant(safe-area-inset-top)) {
|
||||
--safe-area-inset-top: constant(safe-area-inset-top);
|
||||
--safe-area-inset-right: constant(safe-area-inset-right);
|
||||
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
|
||||
--safe-area-inset-left: constant(safe-area-inset-left);
|
||||
}
|
||||
|
||||
@supports (top: env(safe-area-inset-top)) {
|
||||
--safe-area-inset-top: env(safe-area-inset-top);
|
||||
--safe-area-inset-right: env(safe-area-inset-right);
|
||||
//--safe-area-inset-bottom: 12px;
|
||||
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
||||
--safe-area-inset-left: env(safe-area-inset-left);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
79
components/cropper/readme.md
Normal file
79
components/cropper/readme.md
Normal file
@@ -0,0 +1,79 @@
|
||||
#### 【注意】由于赶时间,只做了功能性测试,兼容性测试暂时无法进行,更不用说优化测试了。欢迎有遇到问题的留言,或者自己动手修改也可以。
|
||||
#### 【注意】因为属于业余时间开发的一个项目所依赖的插件,所以更新较缓慢。
|
||||
|
||||
### 使用方式
|
||||
|
||||
```javascript
|
||||
import Cropper from '@/components/cropper/cropper.vue';
|
||||
```
|
||||
|
||||
```html
|
||||
<cropper :imagePath="originalFaceSrc" :aspectRatio="1" @complete="complete" @cancel="cancel" v-if="handleFaceStatus"></cropper>
|
||||
```
|
||||
|
||||
使用v-if控制显示,mounted内执行init方法,使用简单,操作便利。
|
||||
|
||||
## 参数
|
||||
| 参数 | 类型 | 解释 | 默认 | 必填 | 备注 |
|
||||
| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ |
|
||||
| quality | Number | 返回图片质量 | 1 | 否 | 如返回的图片仅需预览不用上传,请适当调低 |
|
||||
| imagePath | String | 原始图片路径 |"" | 是 | blob、静态图片资源地址或者base64(未测试) |
|
||||
| outputFileType | String | 目标文件的类型 | jpg | 否 | jpg:输出jpg格式图片;png:输出png格式图片 |
|
||||
| aspectRatio | Number, null | 目标图片的宽高比 | null | 否 | null,即不限制剪裁宽高比。aspectRatio需大于0 |
|
||||
| minBoxWidthRatio | Number | 最小剪裁尺寸与原图尺寸的比率(宽)| 0.15 | 否 | 宽度最小剪裁到原图的0.15宽 |
|
||||
| minBoxHeightRatio | Number | 最小剪裁尺寸与原图尺寸的比率(高)| 0.15 | 否 | 当设置aspectRatio时,minBoxHeight值设置无效。minBoxHeight值由minBoxWidth 和 aspectRatio自动计算得到 |
|
||||
| initialBoxWidthRatio | Number | 剪裁框初始大小比率(宽) | 0.8 | 否 | 裁框默认宽度为图片宽度的0.8倍 |
|
||||
| initialBoxHeightRatio| Number | 剪裁框初始大小比率(高) | 0.8 | 否 | 当设置aspectRatio时,initialBoxHeightRatio值设置无效。initialBoxHeightRatio值由initialBoxWidthRatio 和 aspectRatio自动计算得到|
|
||||
| saveMode | String | 保存模式 | "None" | 否 | None(无限制)ScaleTo500(缩放到指定的尺寸 如 500宽度) Max500(不超过500宽度) |
|
||||
## 事件
|
||||
|
||||
| 事件名 | 解释 | 参数 | 备注 |
|
||||
| ------------ | ------------ | ------------ | ------------ |
|
||||
| reset | 用户点击重做后触发 | 无 | 可能无用,但留一个给开发者备用 |
|
||||
| cancel | 用户点击取消后触发 | 无 | 开发者可以将组件置为消失v-if(尽量不要用v-show) |
|
||||
| complete | 用户点击裁剪后触发 | path:路径,h5为base64,app与小程序为临时路径;param:裁切参数,见【备注1】 | 核心功能 |
|
||||
|
||||
|
||||
### 备注1
|
||||
param:
|
||||
```json
|
||||
{
|
||||
x:Number, //裁切左上角X坐标
|
||||
y:Number, //裁切左上角Y坐标
|
||||
width:Number, //裁切宽度
|
||||
height:Number, //裁切高度
|
||||
rotate:Number, //旋转(未实现,预留)
|
||||
scaleX:Number, //X轴缩放(未实现,预留)
|
||||
scaleY:Number, //Y轴缩放(未实现,预留)
|
||||
}
|
||||
```
|
||||
|
||||
### 备注2
|
||||
本组件样式支持iphone安全范围但需要手动开启
|
||||
请在插件被样式中最后备注的地方解除备注即可。
|
||||
控制台会报错,无视即可。
|
||||
或者复制如下样式样式代码
|
||||
```css
|
||||
// 安全域兼容样式
|
||||
page {
|
||||
--safe-area-inset-top: 0px;
|
||||
--safe-area-inset-right: 0px;
|
||||
--safe-area-inset-bottom: 0px;
|
||||
--safe-area-inset-left: 0px;
|
||||
|
||||
@supports (top: constant(safe-area-inset-top)) {
|
||||
--safe-area-inset-top: constant(safe-area-inset-top);
|
||||
--safe-area-inset-right: constant(safe-area-inset-right);
|
||||
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
|
||||
--safe-area-inset-left: constant(safe-area-inset-left);
|
||||
}
|
||||
|
||||
@supports (top: env(safe-area-inset-top)) {
|
||||
--safe-area-inset-top: env(safe-area-inset-top);
|
||||
--safe-area-inset-right: env(safe-area-inset-right);
|
||||
//--safe-area-inset-bottom: 12px;
|
||||
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
||||
--safe-area-inset-left: env(safe-area-inset-left);
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user