提交 a8b1baa4 编写于 作者: DCloud_JSON's avatar DCloud_JSON

修复微信小程序端裁剪图片问题

上级 827afca9
<template>
<view class="l-clipper" disable-scroll :style="'z-index: ' + zIndex">
<view
class="l-clipper-mask"
@touchstart.stop.prevent="clipTouchStart"
@touchmove.stop.prevent="clipTouchMove"
@touchend.stop.prevent="clipTouchEnd">
<view class="l-clipper__content" :style="clipStyle">
<view class="l-clipper__edge" v-for="(item, index) in [0,0,0,0]" :key="index"></view>
</view>
<view class="l-clipper" :class="{open: value}" disable-scroll :style="'z-index: ' + zIndex + ';' + customStyle">
<view class="l-clipper-mask" @touchstart.stop.prevent="clipTouchStart" @touchmove.stop.prevent="clipTouchMove" @touchend.stop.prevent="clipTouchEnd">
<view class="l-clipper__content" :style="clipStyle"><view class="l-clipper__edge" v-for="(item, index) in [0, 0, 0, 0]" :key="index"></view></view>
</view>
<image
class="l-clipper-image"
@error="imageLoad"
@load="imageLoad"
@touchstart="imageTouchStart"
@touchmove="imageTouchMove"
@touchend="imageTouchEnd"
@touchstart.stop.prevent="imageTouchStart"
@touchmove.stop.prevent="imageTouchMove"
@touchend.stop.prevent="imageTouchEnd"
:src="image"
mode="widthFix"
:mode="imageWidth == 'auto' ? 'widthFix' : ''"
v-if="image"
:style="imageStyle"/>
:style="imageStyle"
/>
<canvas
canvas-id="l-clipper"
:canvas-id="canvasId"
id="l-clipper"
disable-scroll
:style="'width: ' + (canvasWidth * scaleRatio) + 'px; height:' + (canvasHeight * scaleRatio) + 'px;'"
:style="'width: ' + canvasWidth * scaleRatio + 'px; height:' + canvasHeight * scaleRatio + 'px;'"
class="l-clipper-canvas"
></canvas>
<view class="l-clipper-tools">
<view class="l-clipper-tools__btns">
<view v-if="isShowCancelBtn" @click="cancel">
<view v-if="isShowCancelBtn" @tap="cancel">
<slot name="cancel" v-if="$slots.cancel" />
<view v-else class="cancel" >取消</view>
<view v-else class="cancel">取消</view>
</view>
<view v-if="isShowPhotoBtn" @click="uploadImage">
<view v-if="isShowPhotoBtn" @tap="uploadImage">
<slot name="photo" v-if="$slots.photo" />
<image v-else :src="require('./images/photo.svg')" />
<image v-else :src="require('./images/photo.svg')" />
</view>
<view v-if="isShowRotateBtn" @click="rotate">
<view v-if="isShowRotateBtn" @tap="rotate">
<slot name="rotate" v-if="$slots.rotate" />
<image v-else :src="require('./images/rotate.svg')" data-type="inverse" />
<image v-else :src="require('./images/rotate.svg')" data-type="inverse" />
</view>
<view v-if="isShowConfirmBtn" @click="confirm">
<view v-if="isShowConfirmBtn" @tap="confirm">
<slot name="confirm" v-if="$slots.confirm" />
<view v-else class="confirm" >确定</view>
<view v-else class="confirm">确定</view>
</view>
</view>
<slot></slot>
......@@ -53,10 +48,28 @@
<script>
import { pathToBase64, determineDirection, calcImageOffset, calcImageScale, calcImageSize, calcPythagoreanTheorem, clipTouchMoveOfCalculate, imageTouchMoveOfCalcOffset } from './utils';
const cache = {}
export default {
// version: '0.1.0',
// version: '0.6.3',
name: 'l-clipper',
props: {
value: {
type: Boolean,
default: true
},
// #ifdef MP-WEIXIN
type: {
type: String,
default: '2d'
},
// #endif
customStyle: {
type: String,
},
canvasId: {
type: String,
default: 'l-clipper'
},
zIndex: {
type: Number,
default: 99
......@@ -64,6 +77,10 @@ export default {
imageUrl: {
type: String
},
fileType: {
type: String,
default: 'png'
},
quality: {
type: Number,
default: 1
......@@ -147,20 +164,28 @@ export default {
rotateAngle: {
type: Number,
default: 90
},
source: {
type: Object,
default: () => ({
album: '从相册中选择',
camera: '拍照',
// #ifdef MP-WEIXIN
message: '从微信中选择'
// #endif
})
}
},
data() {
return {
canvasWidth: 0,
canvasHeight: 0,
cutX: 0,
cutY: 0,
aWidth: this.width,
aHeight: this.height,
clipX: 0,
clipY: 0,
clipWidth: 0,
clipHeight: 0,
cutAnimation: false,
imageWidth: 0,
animation: false,
imageWidth: 0,
imageHeight: 0,
imageTop: 0,
imageLeft: 0,
......@@ -168,13 +193,13 @@ export default {
angle: 0,
image: this.imageUrl,
sysinfo: {},
moveThrottleTimer: null,
moveThrottleFlag: true,
timeCutcenter: null,
flagCutTouch: false,
throttleTimer: null,
throttleFlag: true,
timeClipCenter: null,
flagClipTouch: false,
flagEndTouch: false,
cutstart: {},
cutAnimationTime: null,
clipStart: {},
animationTimer: null,
touchRelative: [{x: 0,y: 0}],
hypotenuseLength: 0,
ctx: null
......@@ -182,208 +207,259 @@ export default {
},
computed: {
clipStyle() {
const {clipWidth, clipHeight, cutY, cutX, cutAnimation} = this
const {clipWidth, clipHeight, clipY, clipX, animation} = this
return `
width: ${clipWidth}px;
height:${clipHeight}px;
transition-property: ${cutAnimation ? '' : 'background'};
left: ${cutX}px;
top: ${cutY}px
width: ${clipWidth}px;
height:${clipHeight}px;
transition-property: ${animation ? '' : 'background'};
left: ${clipX}px;
top: ${clipY}px
`
},
imageStyle() {
const {imageWidth, imageHeight, imageLeft, imageTop, cutAnimation, scale, angle} = this
const {imageWidth, imageHeight, imageLeft, imageTop, animation, scale, angle} = this
return `
width:${imageWidth ? imageWidth + 'px' : 'auto'}; height: ${imageHeight ? imageHeight + 'px' : 'auto'};
transform: translate3d(${imageLeft - imageWidth / 2}px, ${imageTop - imageHeight / 2}px, 0) scale(${scale}) rotate(${angle}deg);
transition-duration: ${cutAnimation ? 0.35 : 0}s
width: ${imageWidth ? imageWidth + 'px' : 'auto'};
height: ${imageHeight ? imageHeight + 'px' : 'auto'};
transform: translate3d(${imageLeft - imageWidth / 2}px, ${imageTop - imageHeight / 2}px, 0) scale(${scale}) rotate(${angle}deg);
transition-duration: ${animation ? 0.35 : 0}s
`
},
clipSize() {
const { clipWidth, clipHeight } = this;
return { clipWidth, clipHeight };
},
cutPoint() {
const { cutY, cutX } = this;
return { cutY, cutX };
clipPoint() {
const { clipY, clipX } = this;
return { clipY, clipX };
}
},
watch: {
// #ifdef H5
imageUrl: {
handler: async function(url) {
const res = await pathToBase64(url)
if(res) {
this.image = res
value(val) {
if(!val) {
this.animation = 0
this.angle = 0
} else {
if(this.imageUrl) {
const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight, path} = cache?.[this.imageUrl] || {}
if(path != this.image) {
this.image = this.imageUrl;
} else {
this.setDiffData({imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight})
}
}
},
immediate: true,
}
},
imageUrl(url) {
this.image = url
},
// #endif
image:{
handler: function (url) {
this.getImageInfo(url)
handler: async function(url) {
this.getImageInfo(url)
},
immediate: true,
// immediate: true,
},
clipSize({ widthVal, heightVal }) {
let { minWidth, minHeight } = this;
minWidth = minWidth / 2;
minHeight = minHeight / 2;
if (widthVal < minWidth) {
this.clipWidth = minWidth;
this.setDiffData({clipWidth: minWidth})
}
if (heightVal < minHeight) {
this.clipHeight = minHeight;
this.setDiffData({clipHeight: minHeight})
}
this.computeCutSize();
this.calcClipSize();
},
angle(val) {
this.cutAnimation = true;
this.animation = true;
this.moveStop();
const { isLimitMove } = this;
if (isLimitMove && val % 90) {
this.angle = Math.round(val / 90) * 90;
this.setDiffData({
angle: Math.round(val / 90) * 90
})
}
this.imgMarginDetectionScale();
},
cutAnimation(val) {
clearTimeout(this.cutAnimationTime);
animation(val) {
clearTimeout(this.animationTimer);
if (val) {
let cutAnimationTime = setTimeout(() => {
this.cutAnimation = false;
let animationTimer = setTimeout(() => {
this.setDiffData({
animation: false
})
}, 260);
this.cutAnimationTime = cutAnimationTime;
this.setDiffData({animationTimer})
this.animationTimer = animationTimer;
}
},
isLimitMove(val) {
if (val) {
if (this.angle % 90) {
this.angle = Math.round(this.angle / 90) * 90;
this.setDiffData({
angle : Math.round(this.angle / 90) * 90
})
}
this.imgMarginDetectionScale();
}
},
cutPoint() {
clipPoint() {
this.cutDetectionPosition();
},
aWidth(width, oWidth) {
width(width, oWidth) {
if (width !== oWidth) {
this.clipWidth = width / 2
this.setDiffData({
clipWidth: width / 2
})
}
},
aHeight(height, oHeight) {
height(height, oHeight) {
if (height !== oHeight) {
this.clipHeight = height / 2
this.setDiffData({
clipHeight: height / 2
})
}
}
},
mounted() {
const sysinfo = uni.getSystemInfoSync();
this.sysinfo = sysinfo;
this.setCutInfo();
this.setCutCenter();
this.computeCutSize();
this.setClipInfo();
if(this.image) {
this.getImageInfo(this.image)
}
this.setClipCenter();
this.calcClipSize();
this.cutDetectionPosition();
},
methods: {
setDiffData(data) {
Object.keys(data).forEach(key => {
if (this[key] !== data[key]) {
this[key] = data[key];
}
});
},
getImageInfo(url) {
if (!url) return;
uni.showLoading({
title: '请稍候...',
mask: true
});
if(this.value) {
uni.showLoading({
title: '请稍候...',
mask: true
});
}
uni.getImageInfo({
src: url,
success: res => {
this.imgComputeSize(res.width, res.height);
this.image = res.path;
if (this.isLimitMove) {
this.imgMarginDetectionScale();
this.$emit('ready', res);
}
const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight} = this
cache[url] = Object.assign(res, {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight});
},
fail: () => {
fail: (err) => {
this.imgComputeSize();
if (this.isLimitMove) {
this.imgMarginDetectionScale();
}
}
});
},
setCutInfo() {
const { aWidth, aHeight, sysinfo } = this;
const clipWidth = aWidth / 2;
const clipHeight = aHeight / 2;
const cutY = (sysinfo.windowHeight - clipHeight) / 2;
const cutX = (sysinfo.windowWidth - clipWidth) / 2;
setClipInfo() {
const { width, height, sysinfo, canvasId } = this;
const clipWidth = width / 2;
const clipHeight = height / 2;
const clipY = (sysinfo.windowHeight - clipHeight) / 2;
const clipX = (sysinfo.windowWidth - clipWidth) / 2;
const imageLeft = sysinfo.windowWidth / 2;
const imageTop = sysinfo.windowHeight / 2;
this.ctx = uni.createCanvasContext('l-clipper', this);
this.ctx = uni.createCanvasContext(canvasId, this);
this.clipWidth = clipWidth;
this.clipHeight = clipHeight;
this.cutX = cutX;
this.cutY = cutY;
this.clipX = clipX;
this.clipY = clipY;
this.canvasHeight = clipHeight;
this.canvasWidth = clipWidth;
this.imageLeft = imageLeft;
this.imageTop = imageTop;
},
setCutCenter() {
setClipCenter() {
const { sysInfo, clipHeight, clipWidth, imageTop, imageLeft } = this;
let sys = sysInfo || uni.getSystemInfoSync();
let cutY = (sys.windowHeight - clipHeight) * 0.5;
let cutX = (sys.windowWidth - clipWidth) * 0.5;
this.imageTop = imageTop - this.cutY + cutY;
this.imageLeft = imageLeft - this.cutX + cutX;
this.cutY = cutY;
this.cutX = cutX;
},
computeCutSize() {
const { clipHeight, clipWidth, sysinfo, cutX, cutY } = this;
let clipY = (sys.windowHeight - clipHeight) * 0.5;
let clipX = (sys.windowWidth - clipWidth) * 0.5;
this.imageTop = imageTop - this.clipY + clipY;
this.imageLeft = imageLeft - this.clipX + clipX;
this.clipY = clipY;
this.clipX = clipX;
},
calcClipSize() {
const { clipHeight, clipWidth, sysinfo, clipX, clipY } = this;
if (clipWidth > sysinfo.windowWidth) {
this.clipWidth = sysinfo.windowWidth;
} else if (clipWidth + cutX > sysinfo.windowWidth) {
this.cutX = sysinfo.windowWidth - cutX;
this.setDiffData({
clipWidth: sysinfo.windowWidth
})
} else if (clipWidth + clipX > sysinfo.windowWidth) {
this.setDiffData({
clipX: sysinfo.windowWidth - clipX
})
}
if (clipHeight > sysinfo.windowHeight) {
this.clipHeight = sysinfo.windowHeight;
} else if (clipHeight + cutY > sysinfo.windowHeight) {
this.cutY = sysinfo.windowHeight - cutY;
this.setDiffData({
clipHeight: sysinfo.windowHeight
})
} else if (clipHeight + clipY > sysinfo.windowHeight) {
this.clipY = sysinfo.windowHeight - clipY;
this.setDiffData({
clipY: sysinfo.windowHeight - clipY
})
}
},
cutDetectionPosition() {
const { cutX, cutY, sysinfo, clipHeight, clipWidth } = this;
const { clipX, clipY, sysinfo, clipHeight, clipWidth } = this;
let cutDetectionPositionTop = () => {
if (cutY < 0) {
this.cutY = 0;
if (clipY < 0) {
this.setDiffData({clipY: 0})
}
if (cutY > sysinfo.windowHeight - clipHeight) {
this.cutY = sysinfo.windowHeight - clipHeight;
if (clipY > sysinfo.windowHeight - clipHeight) {
this.setDiffData({clipY: sysinfo.windowHeight - clipHeight})
}
},
cutDetectionPositionLeft = () => {
if (cutX < 0) {
this.cutX = 0;
if (clipX < 0) {
this.setDiffData({clipX: 0})
}
if (cutX > sysinfo.windowWidth - clipWidth) {
this.cutX = sysinfo.windowWidth - clipWidth;
if (clipX > sysinfo.windowWidth - clipWidth) {
this.setDiffData({clipX: sysinfo.windowWidth - clipWidth})
}
};
if (cutY === null && cutX === null) {
let newCutY = (sysinfo.windowHeight - clipHeight) * 0.5;
let newCutX = (sysinfo.windowWidth - clipWidth) * 0.5;
this.cutX = newCutX;
this.cutY = newCutY;
} else if (cutY !== null && cutX !== null) {
if (clipY === null && clipX === null) {
let newClipY = (sysinfo.windowHeight - clipHeight) * 0.5;
let newClipX = (sysinfo.windowWidth - clipWidth) * 0.5;
this.setDiffData({
clipX: newClipX,
clipY: newClipY
})
} else if (clipY !== null && clipX !== null) {
cutDetectionPositionTop();
cutDetectionPositionLeft();
} else if (cutY !== null && cutX === null) {
} else if (clipY !== null && clipX === null) {
cutDetectionPositionTop();
this.cutX = (sysinfo.windowWidth - clipWidth) / 2;
} else if (cutY === null && cutX !== null) {
this.setDiffData({
clipX: (sysinfo.windowWidth - clipWidth) / 2
})
} else if (clipY === null && clipX !== null) {
cutDetectionPositionLeft();
this.cutY = (sysinfo.windowHeight - clipHeight) / 2;
this.setDiffData({
clipY: (sysinfo.windowHeight - clipHeight) / 2
})
}
},
imgComputeSize(width, height) {
......@@ -399,27 +475,29 @@ export default {
imgMarginDetectionPosition(scale) {
if (!this.isLimitMove) return;
const { scale: currentScale, left, top } = calcImageOffset(this, scale);
this.imageLeft = left;
this.imageTop = top;
this.scale = currentScale;
this.setDiffData({
imageLeft: left,
imageTop: top,
scale: currentScale
})
},
moveThrottle() {
if( this.moveThrottleFlag !== true) {
this.moveThrottleFlag = true
}
throttle() {
this.setDiffData({
throttleFlag: true
})
},
moveDuring() {
clearTimeout(this.timeCutcenter);
clearTimeout(this.timeClipCenter);
},
moveStop() {
clearTimeout(this.timeCutcenter);
const timeCutcenter = setTimeout(() => {
if (!this.cutAnimation) {
this.cutAnimation = true;
clearTimeout(this.timeClipCenter);
const timeClipCenter = setTimeout(() => {
if (!this.animation) {
this.setDiffData({animation: true})
}
this.setCutCenter();
this.setClipCenter();
}, 800);
this.timeCutcenter = timeCutcenter;
this.setDiffData({timeClipCenter})
},
clipTouchStart(event) {
// #ifdef H5
......@@ -434,22 +512,20 @@ export default {
}
const currentX = event.touches[0].clientX;
const currentY = event.touches[0].clientY;
const { cutX, cutY, clipWidth, clipHeight } = this;
const corner = determineDirection(cutX, cutY, clipWidth, clipHeight, currentX, currentY);
const { clipX, clipY, clipWidth, clipHeight } = this;
const corner = determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY);
this.moveDuring();
if(!corner) {
return
}
this.cutstart = {
if(!corner) {return}
this.clipStart = {
width: clipWidth,
height: clipHeight,
x: currentX,
y: currentY,
cutY,
cutX,
clipY,
clipX,
corner
};
this.flagCutTouch = true;
this.flagClipTouch = true;
this.flagEndTouch = true;
},
clipTouchMove(event) {
......@@ -464,44 +540,57 @@ export default {
});
return;
}
const { flagCutTouch, moveThrottleFlag } = this;
if (flagCutTouch && moveThrottleFlag) {
// 只针对单指点击做处理
if (event.touches.length !== 1) {
return;
}
const { flagClipTouch, throttleFlag } = this;
if (flagClipTouch && throttleFlag) {
const { isLockRatio, isLockHeight, isLockWidth } = this;
if (isLockRatio && (isLockWidth || isLockHeight)) return;
this.moveThrottleFlag = false;
if(this.moveThrottleFlag !== false) {
this.moveThrottleFlag = false;
}
this.moveThrottle();
const { width, height, cutX, cutY } = clipTouchMoveOfCalculate(this, event);
if (!isLockWidth && !isLockHeight) {
this.clipWidth = width;
this.clipHeight = height;
this.cutX = cutX;
this.cutY = cutY;
} else if (!isLockWidth) {
this.clipWidth = width// clipWidth;
this.cutX = cutX;
} else if (!isLockHeight) {
this.clipHeight = height//clipHeight;
this.cutY = cutY;
this.setDiffData({
throttleFlag: false
})
this.throttle();
const clipData = clipTouchMoveOfCalculate(this, event);
if(clipData) {
const { width, height, clipX, clipY } = clipData;
if (!isLockWidth && !isLockHeight) {
this.setDiffData({
clipWidth: width,
clipHeight: height,
clipX,
clipY
})
} else if (!isLockWidth) {
this.setDiffData({
clipWidth: width,
clipX
})
} else if (!isLockHeight) {
this.setDiffData({
clipHeight: height,
clipY
})
}
this.imgMarginDetectionScale();
}
this.imgMarginDetectionScale();
}
},
clipTouchEnd() {
this.moveStop();
this.flagCutTouch = false;
this.flagClipTouch = false;
},
imageTouchStart(e) {
// #ifdef H5
event.preventDefault()
// #endif
this.flagEndTouch = false;
this.cutAnimation = false;
const { imageLeft, imageTop } = this;
const clientXForLeft = Math.round(e.touches[0].clientX);
const clientYForLeft = Math.round(e.touches[0].clientY);
const clientXForLeft = e.touches[0].clientX;
const clientYForLeft = e.touches[0].clientY;
let touchRelative = [];
if (e.touches.length === 1) {
......@@ -511,8 +600,8 @@ export default {
};
this.touchRelative = touchRelative;
} else {
const clientXForRight = Math.round(e.touches[1].clientX);
const clientYForRight = Math.round(e.touches[1].clientY);
const clientXForRight = e.touches[1].clientX;
const clientYForRight = e.touches[1].clientY;
let width = Math.abs(clientXForLeft - clientXForRight);
let height = Math.abs(clientYForLeft - clientYForRight);
const hypotenuseLength = calcPythagoreanTheorem(width, height);
......@@ -535,26 +624,29 @@ export default {
// #ifdef H5
event.preventDefault()
// #endif
const { flagEndTouch, moveThrottleFlag } = this;
if (flagEndTouch || !moveThrottleFlag) return;
const clientXForLeft = Math.round(e.touches[0].clientX);
const clientYForLeft = Math.round(e.touches[0].clientY);
this.moveThrottleFlag = false;
this.moveThrottle();
const { flagEndTouch, throttleFlag } = this;
if (flagEndTouch || !throttleFlag) return;
const clientXForLeft = e.touches[0].clientX;
const clientYForLeft = e.touches[0].clientY;
this.setDiffData({throttleFlag: false})
this.throttle();
this.moveDuring();
if (e.touches.length === 1) {
const { left, top } = imageTouchMoveOfCalcOffset(this, clientXForLeft, clientYForLeft);
this.imageLeft = left;
this.imageTop = top;
const { left: imageLeft, top: imageTop} = imageTouchMoveOfCalcOffset(this, clientXForLeft, clientYForLeft);
this.setDiffData({
imageLeft,
imageTop
})
this.imgMarginDetectionPosition();
} else {
const clientXForRight = Math.round(e.touches[1].clientX);
const clientYForRight = Math.round(e.touches[1].clientY);
const clientXForRight = e.touches[1].clientX;
const clientYForRight = e.touches[1].clientY;
let width = Math.abs(clientXForLeft - clientXForRight),
height = Math.abs(clientYForLeft - clientYForRight),
hypotenuse = calcPythagoreanTheorem(width, height),
hypotenuse = calcPythagoreanTheorem(width, height),
scale = this.scale * (hypotenuse / this.hypotenuseLength);
if (this.isDisableScale) {
scale = 1;
} else {
scale = scale <= this.minRatio ? this.minRatio : scale;
......@@ -571,19 +663,46 @@ export default {
}
},
imageTouchEnd() {
this.flagEndTouch = true;
this.setDiffData({
flagEndTouch: true
})
this.moveStop();
},
uploadImage() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: res => {
const tempFilePaths = res.tempFilePaths;
this.image = tempFilePaths[0];
const itemList = Object.entries(this.source)
const sizeType = ['original', 'compressed']
const success = ({tempFilePaths:a, tempFiles: b}) => {
this.image = a ? a[0] : b[0].path
};
const _uploadImage = (type) => {
if(type !== 'message') {
uni.chooseImage({
count: 1,
sizeType,
sourceType: [type],
success
});
}
});
// #ifdef MP-WEIXIN
if(type == 'message') {
wx.chooseMessageFile({
count: 1,
type: 'image',
success
})
}
// #endif
}
if(itemList.length > 1) {
uni.showActionSheet({
itemList: itemList.map(v => v[1]),
success: ({tapIndex: i}) => {
_uploadImage(itemList[i][0])
}
})
} else {
_uploadImage(itemList[0][0])
}
},
imageReset() {
const sys = this.sysinfo || uni.getSystemInfoSync();
......@@ -627,20 +746,18 @@ export default {
uni.showLoading({
title: '加载中'
});
const { clipHeight, clipWidth, ctx, scale, imageLeft, imageTop, cutX, cutY, angle, scaleRatio, image, quality, type: imageType } = this;
let { canvasHeight, canvasWidth } = this;
const { canvasHeight, canvasWidth, clipHeight, clipWidth, ctx, scale, imageLeft, imageTop, clipX, clipY, angle, scaleRatio: dpr, image, quality, fileType, type: imageType, canvasId } = this;
const draw = () => {
const imageWidth = this.imageWidth * scale * scaleRatio;
const imageHeight = this.imageHeight * scale * scaleRatio;
const xpos = imageLeft - cutX;
const ypos = imageTop - cutY;
ctx.translate(xpos * scaleRatio, ypos * scaleRatio);
const imageWidth = this.imageWidth * scale * dpr;
const imageHeight = this.imageHeight * scale * dpr;
const xpos = imageLeft - clipX;
const ypos = imageTop - clipY;
ctx.translate(xpos * dpr, ypos * dpr);
ctx.rotate((angle * Math.PI) / 180);
ctx.drawImage(image, -imageWidth / 2, -imageHeight / 2, imageWidth, imageHeight);
ctx.draw(false, () => {
const width = clipWidth * scaleRatio
const height = clipHeight * scaleRatio
const width = clipWidth * dpr
const height = clipHeight * dpr
let params = {
x: 0,
y: 0,
......@@ -648,17 +765,19 @@ export default {
height,
destWidth: width,
destHeight: height,
canvasId: 'l-clipper',
fileType: 'png',
canvasId: canvasId,
fileType,
quality,
success: (res) => {
data.url = res.tempFilePath;
uni.hideLoading();
this.$emit('success', data);
this.$emit('input', false)
},
fail: (error) => {
console.error('error', error)
this.$emit('fail', error);
this.$emit('input', false)
}
};
......@@ -672,23 +791,26 @@ export default {
};
if (canvasWidth !== clipWidth || canvasHeight !== clipHeight) {
canvasWidth = clipWidth;
canvasHeight = clipHeight;
this.canvasWidth = clipWidth;
this.canvasHeight = clipHeight;
ctx.draw();
setTimeout(() => {
draw();
}, 100);
this.$nextTick(() => {
setTimeout(() => {
draw();
}, 100);
})
} else {
draw();
}
},
cancel() {
this.$emit('cancel', false)
this.$emit('input', false)
},
}
};
</script>
<style scoped>
@import './index.css'
</style>
@import './index'
</style>
\ No newline at end of file
/**
* h5网络地址转base64
*/
export function pathToBase64(path) {
// #ifdef H5
return new Promise((resolve, reject) => {
const _fileReader = (blob) => {
const fileReader = new FileReader();
fileReader.onload = (e) => {
resolve(e.target.result);
};
fileReader.readAsDataURL(blob);
fileReader.onerror = (error) => {
console.error('blobToBase64 error:', JSON.stringify(error))
reject(new Error('blobToBase64 error'));
};
}
if (/^(http|\/\/)/.test(path) && typeof FileReader === 'function') {
window.URL = window.URL || window.webkitURL;
const xhr = new XMLHttpRequest();
xhr.open("get", path, true);
xhr.timeout = 2000;
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status == 200) {
_fileReader(this.response)
}
}
xhr.send();
}
})
// #endif
}
/**
* 判断手指触摸位置
*/
export function determineDirection(cutX, cutY, clipWidth, clipHeight, currentX, currentY) {
const EXPAND_SIZE = 24;
let leftx1 = cutX - EXPAND_SIZE;
let leftx2 = cutX + EXPAND_SIZE;
let topy1 = cutY - EXPAND_SIZE;
let topy2 = cutY + EXPAND_SIZE;
let rightx1 = cutX + clipWidth - EXPAND_SIZE;
let rightx2 = cutX + clipWidth + EXPAND_SIZE;
let bottomy1 = cutY + clipHeight - EXPAND_SIZE;
let bottomy2 = cutY + clipHeight + EXPAND_SIZE;
let corner;
const isRight = currentX > rightx1 && currentX < rightx2;
const isLeft = currentX > leftx1 && currentX < leftx2;
const isBottom = currentY > bottomy1 && currentY < bottomy2;
const isTop = currentY > topy1 && currentY < topy2;
if (isRight && isBottom) {
corner = 1;
} else if (isRight && isTop) {
corner = 2;
} else if (isLeft && isTop) {
corner = 3;
} else if (isLeft && isBottom) {
corner = 4;
}
return corner;
}
/**
* 图片边缘检测检测时,计算图片偏移量
*/
export function calcImageOffset(data, scale) {
let {
imageLeft: left,
imageTop: top,
imageWidth,
imageHeight,
cutX,
clipWidth,
cutY,
clipHeight
} = data
scale = scale || data.scale;
if ((data.angle / 90) % 2) {
imageWidth = mageHeight;
imageHeight = mageWidth;
}
// 当前图片宽度/高度
const currentImageSize = (size) => (size * scale) / 2;
const currentImageWidth = currentImageSize(imageWidth);
const currentImageHeight = currentImageSize(imageHeight);
left = cutX + currentImageWidth >= left ? left : cutX + currentImageWidth;
left = cutX + clipWidth - currentImageWidth <= left ? left : cutX + clipWidth - currentImageWidth;
top = cutY + currentImageHeight >= top ? top : cutY + currentImageHeight;
top = cutY + clipHeight - currentImageHeight <= top ? top : cutY + clipHeight - currentImageHeight;
return {
left,
top,
scale
};
}
/**
* 图片边缘检测时,计算图片缩放比例
*/
export function calcImageScale(data, scale) {
scale = scale || data.scale;
let {
imageWidth,
imageHeight,
clipWidth,
clipHeight,
angle
} = data
if ((angle / 90) % 2) {
imageWidth = imageHeight;
imageHeight = imageWidth;
}
if (imageWidth * scale < clipWidth) {
scale = clipWidth / imageWidth;
}
if (imageHeight * scale < clipHeight) {
scale = Math.max(scale, clipHeight / imageHeight);
}
return scale;
}
/**
* 计算图片尺寸
*/
export function calcImageSize(width, height, data) {
let imageWidth = width,
imageHeight = height;
let {
clipWidth,
clipHeight,
sysinfo,
width: oWidth,
height: oHeight
} = data
if (imageWidth && imageHeight) {
if (imageWidth / imageHeight > (clipWidth || oWidth) / (clipWidth || oHeight)) {
imageHeight = clipHeight || oHeight;
imageWidth = (width / height) * imageHeight;
} else {
imageWidth = clipWidth || oWidth;
imageHeight = (height / width) * imageWidth;
}
} else {
let sys = sysinfo || uni.getSystemInfoSync();
imageWidth = sys.windowWidth;
imageHeight = 0;
}
return {
imageWidth,
imageHeight
};
}
/**
* 勾股定理求斜边
*/
export function calcPythagoreanTheorem(width, height) {
return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
}
/**
* 拖动裁剪框时计算
*/
export function clipTouchMoveOfCalculate(data, event) {
const clientX = event.touches[0].clientX;
const clientY = event.touches[0].clientY;
const {
clipWidth,
clipHeight,
cutY: oldCutY,
cutX: oldCutX,
cutstart,
isLockRatio
} = data;
let {
maxWidth,
minWidth,
maxHeight,
minHeight
} = data;
maxWidth = maxWidth / 2;
minWidth = minWidth / 2;
minHeight = minHeight / 2;
maxHeight = maxHeight / 2;
let width = clipWidth,
height = clipHeight,
cutY = oldCutY,
cutX = oldCutX,
sizecorrect = () => {
width = width <= maxWidth ? (width >= minWidth ? width : minWidth) : maxWidth;
height = height <= maxHeight ? (height >= minHeight ? height : minHeight) : maxHeight;
},
sizeinspect = () => {
sizecorrect();
if ((width > maxWidth || width < minWidth || height > maxHeight || height < minHeight) && isLockRatio) {
return false;
} else {
return true;
}
};
if (cutstart.corner) {
height = cutstart.height + (cutstart.corner > 1 && cutstart.corner < 4 ? 1 : -1) * (cutstart.y - clientY);
}
switch (cutstart.corner) {
case 1:
width = cutstart.width - cutstart.x + clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) return;
break;
case 2:
width = cutstart.width - cutstart.x + clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) {
return;
} else {
cutY = cutstart.cutY - (height - cutstart.height);
}
break;
case 3:
width = cutstart.width + cutstart.x - clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) {
return;
} else {
cutY = cutstart.cutY - (height - cutstart.height);
cutX = cutstart.cutX - (width - cutstart.width);
}
break;
case 4:
width = cutstart.width + cutstart.x - clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) {
return;
} else {
cutX = cutstart.cutX - (width - cutstart.width);
}
break;
default:
break;
}
return {
width,
height,
cutX,
cutY
};
}
/**
* 单指拖动图片计算偏移
*/
export function imageTouchMoveOfCalcOffset(data, clientXForLeft, clientYForLeft) {
let left = clientXForLeft - data.touchRelative[0].x,
top = clientYForLeft - data.touchRelative[0].y;
return {
left,
top
};
}
/**
* 判断手指触摸位置
*/
export function determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY) {
/*
* (右下>>1 右上>>2 左上>>3 左下>>4)
*/
let corner;
/**
* 思路:(利用直角坐标系)
* 1.找出裁剪框中心点
* 2.如点击坐标在上方点与左方点区域内,则点击为左上角
* 3.如点击坐标在下方点与右方点区域内,则点击为右下角
* 4.其他角同理
*/
const mainPoint = [clipX + clipWidth / 2, clipY + clipHeight / 2]; // 中心点
const currentPoint = [currentX, currentY]; // 触摸点
if (currentPoint[0] <= mainPoint[0] && currentPoint[1] <= mainPoint[1]) {
corner = 3; // 左上
} else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] <= mainPoint[1]) {
corner = 2; // 右上
} else if (currentPoint[0] <= mainPoint[0] && currentPoint[1] >= mainPoint[1]) {
corner = 4; // 左下
} else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] >= mainPoint[1]) {
corner = 1; // 右下
}
return corner;
}
/**
* 图片边缘检测检测时,计算图片偏移量
*/
export function calcImageOffset(data, scale) {
let left = data.imageLeft;
let top = data.imageTop;
scale = scale || data.scale;
let imageWidth = data.imageWidth;
let imageHeight = data.imageHeight;
if ((data.angle / 90) % 2) {
imageWidth = data.imageHeight;
imageHeight = data.imageWidth;
}
const {
clipX,
clipWidth,
clipY,
clipHeight
} = data;
// 当前图片宽度/高度
const currentImageSize = (size) => (size * scale) / 2;
const currentImageWidth = currentImageSize(imageWidth);
const currentImageHeight = currentImageSize(imageHeight);
left = clipX + currentImageWidth >= left ? left : clipX + currentImageWidth;
left = clipX + clipWidth - currentImageWidth <= left ? left : clipX + clipWidth - currentImageWidth;
top = clipY + currentImageHeight >= top ? top : clipY + currentImageHeight;
top = clipY + clipHeight - currentImageHeight <= top ? top : clipY + clipHeight - currentImageHeight;
return {
left,
top,
scale
};
}
/**
* 图片边缘检测时,计算图片缩放比例
*/
export function calcImageScale(data, scale) {
scale = scale || data.scale;
let {
imageWidth,
imageHeight,
clipWidth,
clipHeight,
angle
} = data
if ((angle / 90) % 2) {
imageWidth = imageHeight;
imageHeight = imageWidth;
}
if (imageWidth * scale < clipWidth) {
scale = clipWidth / imageWidth;
}
if (imageHeight * scale < clipHeight) {
scale = Math.max(scale, clipHeight / imageHeight);
}
return scale;
}
/**
* 计算图片尺寸
*/
export function calcImageSize(width, height, data) {
let imageWidth = width,
imageHeight = height;
let {
clipWidth,
clipHeight,
sysinfo,
width: originWidth,
height: originHeight
} = data
if (imageWidth && imageHeight) {
if (imageWidth / imageHeight > (clipWidth || originWidth) / (clipWidth || originHeight)) {
imageHeight = clipHeight || originHeight;
imageWidth = (width / height) * imageHeight;
} else {
imageWidth = clipWidth || originWidth;
imageHeight = (height / width) * imageWidth;
}
} else {
let sys = sysinfo || uni.getSystemInfoSync();
imageWidth = sys.windowWidth;
imageHeight = 0;
}
return {
imageWidth,
imageHeight
};
}
/**
* 勾股定理求斜边
*/
export function calcPythagoreanTheorem(width, height) {
return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
}
/**
* 拖动裁剪框时计算
*/
export function clipTouchMoveOfCalculate(data, event) {
const clientX = event.touches[0].clientX;
const clientY = event.touches[0].clientY;
let {
clipWidth,
clipHeight,
clipY: oldClipY,
clipX: oldClipX,
clipStart,
isLockRatio,
maxWidth,
minWidth,
maxHeight,
minHeight
} = data;
maxWidth = maxWidth / 2;
minWidth = minWidth / 2;
minHeight = minHeight / 2;
maxHeight = maxHeight / 2;
let width = clipWidth,
height = clipHeight,
clipY = oldClipY,
clipX = oldClipX,
// 获取裁剪框实际宽度/高度
// 如果大于最大值则使用最大值
// 如果小于最小值则使用最小值
sizecorrect = () => {
width = width <= maxWidth ? (width >= minWidth ? width : minWidth) : maxWidth;
height = height <= maxHeight ? (height >= minHeight ? height : minHeight) : maxHeight;
},
sizeinspect = () => {
sizecorrect();
if ((width > maxWidth || width < minWidth || height > maxHeight || height < minHeight) && isLockRatio) {
return false;
} else {
return true;
}
};
//if (clipStart.corner) {
height = clipStart.height + (clipStart.corner > 1 && clipStart.corner < 4 ? 1 : -1) * (clipStart.y - clientY);
//}
switch (clipStart.corner) {
case 1:
width = clipStart.width - clipStart.x + clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) return;
break;
case 2:
width = clipStart.width - clipStart.x + clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) {
return;
} else {
clipY = clipStart.clipY - (height - clipStart.height);
}
break;
case 3:
width = clipStart.width + clipStart.x - clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) {
return;
} else {
clipY = clipStart.clipY - (height - clipStart.height);
clipX = clipStart.clipX - (width - clipStart.width);
}
break;
case 4:
width = clipStart.width + clipStart.x - clientX;
if (isLockRatio) {
height = width / (clipWidth / clipHeight);
}
if (!sizeinspect()) {
return;
} else {
clipX = clipStart.clipX - (width - clipStart.width);
}
break;
default:
break;
}
return {
width,
height,
clipX,
clipY
};
}
/**
* 单指拖动图片计算偏移
*/
export function imageTouchMoveOfCalcOffset(data, clientXForLeft, clientYForLeft) {
let left = clientXForLeft - data.touchRelative[0].x,
top = clientYForLeft - data.touchRelative[0].y;
return {
left,
top
};
}
......@@ -11,20 +11,14 @@ export default {
data() {return {path: '',options:{"width":600,"height":600}}},
onLoad({path,options}) {
this.path = path
console.log('path-path-path-path',path);
if(options){
this.options = JSON.parse(options)
}
},
methods:{
successFn(e){
// uni.getImageInfo({
// src:e.url,
// complete: (e) => {
// console.log(e);
// }
// })
this.uploadImgToUnicloud(e.url,(url)=>{
//console.log(url);
this.getOpenerEventChannel().emit('uploadAvatarAfter', {url})
uni.navigateBack()
})
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册