<template>
|
<view class="bt-container" :style="[containerStyle]">
|
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<view class="mainContent" data-type="image" @touchstart="wxsModule.touchStart" @touchmove="wxsModule.touchMove" @touchend="wxsModule.touchEnd">
|
<!-- #endif -->
|
<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<view class="mainContent" data-type="image" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
|
<!-- #endif -->
|
<template v-if="imageRect && cropperRect">
|
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<image mode="aspectFit" :src="imageSrc" class="image" :rotateAngle="rotateAngle" :change:rotateAngle="wxsModule.changeRotateAngle" :change:imageRect="wxsModule.changeImageRect" :imageRect="imageRect" :class="{ anim }">
|
<!-- #endif -->
|
<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<image mode="aspectFit" :src="imageSrc" class="image" :style="[imageStyle]" :class="{ anim }">
|
<!-- #endif -->
|
</image>
|
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<view class="cropper" :class="{ anim }" :change:cropperRect="wxsModule.changeCropper" :cropperRect="cropperRect" :change:ratio="wxsModule.changeRatio" :ratio="ratio" >
|
<!-- #endif -->
|
<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<view class="cropper" :class="{ anim }" :style="[cropperStyle]">
|
<!-- #endif -->
|
<image class="mask" :src="mask"></image>
|
<template v-if="showGrid">
|
<view class="line row row1"></view>
|
<view class="line row row2"></view>
|
<view class="line col col1"></view>
|
<view class="line col col2"></view>
|
</template>
|
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<view class="controller vertical left" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller vertical right" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller horizon top" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller horizon bottom" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller left top" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller left bottom" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller right top" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<view class="controller right bottom" @touchstart="wxsModule.touchStart" data-type="controller" />
|
<!-- #endif -->
|
<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<view class="controller vertical left" @touchstart.stop="touchStart(-1, 0, $event)" />
|
<view class="controller vertical right" @touchstart.stop="touchStart(1, 0, $event)" />
|
<view class="controller horizon top" @touchstart.stop="touchStart(0, -1, $event)" />
|
<view class="controller horizon bottom" @touchstart.stop="touchStart(0, 1, $event)" />
|
<view class="controller left top" @touchstart.stop="touchStart(-1, -1, $event)" />
|
<view class="controller left bottom" @touchstart.stop="touchStart(-1, 1, $event)" />
|
<view class="controller right top" @touchstart.stop="touchStart(1, -1, $event)" />
|
<view class="controller right bottom" @touchstart.stop="touchStart(1, 1, $event)" />
|
<!-- #endif -->
|
</view>
|
</template>
|
</view>
|
<canvas v-if="type2d" type="2d" class="bt-canvas" :width="target.width" :height="target.height"></canvas>
|
<canvas
|
v-else
|
:canvas-id="canvasId"
|
class="bt-canvas"
|
:style="{
|
width: target.width + 'px',
|
height: target.height + 'px'
|
}"
|
:width="target.width * pixel"
|
:height="target.height * pixel"
|
></canvas>
|
</view>
|
</template>
|
|
<script>
|
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
import touches from './js/touchs.js';
|
// #endif
|
import { parseUnit,sleep } from './utils/tools.js';
|
/**
|
* better-cropper 图片裁切插件
|
*/
|
export default {
|
name: 'bt-cropper',
|
props: {
|
// 图片路径,支持网络路径和本地路径
|
imageSrc: {
|
type: String,
|
default: '',
|
required: true
|
},
|
mask: {
|
type: String,
|
default: ''
|
},
|
// 手动指定容器的大小
|
containerSize: {
|
type: Object,
|
default: null
|
},
|
// 输出图片的格式,默认jpg
|
fileType: {
|
type: String,
|
default: 'png'
|
},
|
// 生成的图片的宽度,不传或者传0表示按照原始分辨率裁切
|
dWidth: Number,
|
maxWidth: {
|
type: Number,
|
default: 2000
|
},
|
// 裁切比例,0表示自由
|
ratio: {
|
type: Number,
|
default: 0,
|
validator(value) {
|
if (typeof value === 'number') {
|
if (value < 0) {
|
return false;
|
}
|
} else {
|
return false;
|
}
|
return true;
|
}
|
},
|
// 旋转角度
|
rotate: Number,
|
// 是否展示网格
|
showGrid: {
|
type: Boolean,
|
default: false
|
},
|
// 图片质量,0-1 越大质量越好
|
quality: {
|
type: Number,
|
default: 1
|
},
|
canvas2d: {
|
type: Boolean,
|
default: false
|
},
|
// 初始的图片位置
|
initPosition: {
|
type: Object,
|
default() {
|
return null;
|
}
|
},
|
// 是否开启操作结束后自动放大
|
autoZoom: {
|
type: Boolean,
|
default: true
|
}
|
},
|
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
mixins: [touches],
|
// #endif
|
data() {
|
return {
|
canvasId: 'bt-cropper',
|
containerRect: null,
|
imageInfo: null,
|
operationHistory: [],
|
operationIndex: 0,
|
anim: false,
|
timer: null,
|
// 是否使用2D canvas
|
type2d: false,
|
pixel: 1,
|
imageRect: null,
|
cropperRect: null,
|
target:{
|
width:0,
|
height:0
|
}
|
};
|
},
|
watch: {
|
imageSrc: {
|
handler(src) {
|
if (typeof src === 'string' && src !== '') {
|
this.imageInit(src);
|
} else {
|
this.imageInfo = null;
|
}
|
},
|
immediate: true
|
},
|
ratio() {
|
if (this.ratio != 0) {
|
this.startAnim();
|
this.init();
|
}
|
}
|
},
|
computed: {
|
containerStyle() {
|
if (this.containerSize && this.containerRect) {
|
return {
|
width: this.containerRect.width + 'px',
|
height: this.containerRect.height + 'px'
|
};
|
}
|
return {};
|
},
|
rotateAngle(){
|
const angel = Number(this.rotate)
|
if(isNaN(angel)){
|
return 0
|
}else{
|
return angel % 360
|
}
|
}
|
},
|
methods: {
|
startAnim() {
|
this.stopAnim();
|
this.anim = true;
|
this.timer = setTimeout(() => {
|
this.anim = false;
|
}, 200);
|
},
|
stopAnim() {
|
this.anim = false;
|
clearTimeout(this.timer);
|
},
|
imageInit(src) {
|
uni.showLoading({
|
title: '载入中...'
|
});
|
uni.getImageInfo({
|
src,
|
success: res => {
|
this.imageInfo = res;
|
this.$nextTick(() => {
|
this.getContainer().then(rect => {
|
this.containerRect = rect;
|
this.init();
|
});
|
});
|
},
|
fail: (err) => {
|
this.$emit('loadFail',err);
|
uni.showToast({
|
title: '图片下载失败!',
|
icon: 'none'
|
});
|
},
|
complete(res) {
|
uni.hideLoading();
|
}
|
});
|
},
|
initCropper() {
|
const imageRate = this.imageInfo.width / this.imageInfo.height;
|
const containerRate = this.containerRect.width / this.containerRect.height;
|
const imageRect = {};
|
let cropperRate = this.ratio;
|
if (cropperRate == 0) {
|
if (this.cropperRect) {
|
cropperRate = this.cropperRect.width / this.cropperRect.height;
|
} else {
|
cropperRate = 1;
|
}
|
}
|
const cropperRect = {};
|
if (containerRate > cropperRate) {
|
cropperRect.height = this.containerRect.height * 0.85;
|
cropperRect.width = cropperRect.height * cropperRate;
|
} else {
|
cropperRect.width = this.containerRect.width * 0.85;
|
cropperRect.height = cropperRect.width / cropperRate;
|
}
|
if (cropperRate > imageRate) {
|
imageRect.width = cropperRect.width;
|
imageRect.height = imageRect.width / imageRate;
|
} else {
|
imageRect.height = cropperRect.height;
|
imageRect.width = imageRect.height * imageRate;
|
}
|
imageRect.left = (this.containerRect.width - imageRect.width) / 2;
|
imageRect.top = (this.containerRect.height - imageRect.height) / 2;
|
cropperRect.left = imageRect.left + (imageRect.width - cropperRect.width) / 2;
|
cropperRect.top = imageRect.top + (imageRect.height - cropperRect.height) / 2;
|
return {
|
imageRect,
|
cropperRect
|
};
|
},
|
init() {
|
const {imageRect,cropperRect} = this.initCropper()
|
if(this.initPosition){
|
const scale = this.imageInfo.width/imageRect.width;
|
const {left,top,width,height} = this.initPosition;
|
if(left!==undefined&&top!==undefined&&width!==undefined&&height!==undefined){
|
cropperRect.width = width/scale;
|
cropperRect.height = height/scale;
|
cropperRect.left = left/scale;
|
cropperRect.top = top/scale;
|
this.$nextTick(this.zoomToFill);
|
}
|
}
|
this.imageRect = imageRect
|
this.cropperRect = cropperRect
|
this.operationHistory = [{imageRect,cropperRect}];
|
this.operationIndex = 0;
|
this.setTarget();
|
// #ifdef MP-WEIXIN
|
const systemInfo = uni.getSystemInfoSync();
|
if (this.canvas2d === false || systemInfo.platform === 'windows' || systemInfo.platform === 'mac') {
|
this.type2d = false;
|
} else {
|
this.type2d = true;
|
this.pixel = systemInfo.pixelRatio;
|
}
|
// #endif
|
//非微信小程序端强制关闭canvas2d模式
|
// #ifndef MP-WEIXIN
|
this.type2d = false;
|
// #endif
|
// #ifdef MP-TOUTIAO || MP-LARK || MP-ALIPAY
|
this.type2d = this.canvas2d;
|
// #endif
|
},
|
// 设置目标图像的大小
|
setTarget(){
|
const ratio = this.cropperRect.width / this.cropperRect.height;
|
if (!!this.dWidth) {
|
this.target = {
|
width: this.dWidth,
|
height: this.dWidth / (ratio || 1)
|
}
|
} else {
|
const width = Math.min(this.maxWidth, this.cropperRect.width * (this.imageInfo.width / this.imageRect.width));
|
this.target = {
|
width,
|
height: width / (ratio || 1)
|
}
|
}
|
},
|
addHistory({imageRect,cropperRect}){
|
if(this.operationIndex!==this.operationHistory.length-1){
|
this.operationHistory = this.operationHistory.slice(0,this.operationIndex)
|
}
|
this.operationHistory.push({
|
imageRect,
|
cropperRect
|
});
|
if (this.operationHistory.length > 10) {
|
this.operationHistory.shift();
|
}
|
this.operationIndex = this.operationHistory.length - 1;
|
},
|
updateData(data) {
|
this.imageRect = data.imageRect
|
this.cropperRect = data.cropperRect
|
this.addHistory(data);
|
this.setTarget();
|
if (this.autoZoom) {
|
this.timer = setTimeout(() => {
|
this.zoomToFill();
|
}, 600);
|
}
|
const {imageRect,cropperRect} = data
|
const scale = imageRect.width/this.imageInfo.width
|
this.$emit('change', {
|
left: (cropperRect.left - imageRect.left) /scale,
|
top: (cropperRect.top - imageRect.top) / scale,
|
width: cropperRect.width / scale,
|
height: cropperRect.height / scale
|
});
|
},
|
getContainer() {
|
if (this.containerSize !== null && typeof this.containerSize == 'object') {
|
const { width, height } = this.containerSize;
|
return Promise.resolve({
|
width: parseUnit(width),
|
height: parseUnit(height)
|
});
|
} else {
|
return new Promise(resolve => {
|
const query = uni.createSelectorQuery().in(this);
|
query
|
.select('.mainContent')
|
.boundingClientRect(rect => {
|
resolve(rect);
|
})
|
.exec();
|
});
|
}
|
},
|
zoomToFill() {
|
this.startAnim();
|
const beforeCropper = {
|
...this.cropperRect
|
};
|
const operation = {
|
imageRect:this.imageRect,
|
cropperRect:this.cropperRect
|
};
|
this.cropperRect = this.initCropper().cropperRect;
|
const scale = this.cropperRect.width / beforeCropper.width;
|
const ox = beforeCropper.left - this.imageRect.left;
|
const oy = beforeCropper.top - this.imageRect.top;
|
this.imageRect = {
|
width: this.imageRect.width * scale,
|
height: this.imageRect.height * scale,
|
left: this.imageRect.left + (this.cropperRect.left - beforeCropper.left) - (scale - 1) * ox,
|
top: this.imageRect.top + (this.cropperRect.top - beforeCropper.top) - (scale - 1) * oy
|
};
|
},
|
onTouchStart() {
|
this.stopAnim();
|
},
|
// 撤销
|
undo() {
|
if (this.operationIndex > 0) {
|
this.operationIndex--;
|
this.imageRect = this.operationHistory[this.operationIndex].imageRect;
|
this.cropperRect = this.operationHistory[this.operationIndex].cropperRect;
|
return true;
|
}
|
return false;
|
},
|
// 重做
|
resume() {
|
if (this.operationIndex < this.operationHistory.length - 1) {
|
this.operationIndex++;
|
this.imageRect = this.operationHistory[this.operationIndex].imageRect;
|
this.cropperRect = this.operationHistory[this.operationIndex].cropperRect;
|
return true;
|
}
|
return false;
|
},
|
async drawImage(ctx,image,x,y,w,h){
|
if (this.type2d) {
|
await new Promise(resolve => (image.onload = resolve));
|
ctx.drawImage(image, x * this.pixel, y * this.pixel, w * this.pixel, h * this.pixel);
|
} else {
|
const path = await new Promise((resolve)=>{
|
uni.getImageInfo({
|
src:image,
|
success({path}){
|
resolve(path)
|
}
|
})
|
})
|
ctx.drawImage(path, x * this.pixel, y * this.pixel, w * this.pixel, h * this.pixel);
|
await new Promise((resolve) => ctx.draw(false,resolve));
|
}
|
},
|
async crop() {
|
let ctx;
|
let canvas;
|
this.$emit('cropStart')
|
this.setTarget()
|
if (this.type2d) {
|
const query = uni.createSelectorQuery().in(this);
|
canvas = await new Promise(resolve =>
|
query
|
.select('.bt-canvas')
|
.node(({ node }) => resolve(node))
|
.exec()
|
);
|
canvas.width = this.target.width * this.pixel;
|
canvas.height = this.target.height * this.pixel;
|
ctx = canvas.getContext('2d');
|
// #ifdef MP-TOUTIAO
|
if(this.type2d){
|
console.warn("请注意:目前头条系小程序暂时无法使用2d canvas保存图片,建议换成V1版本")
|
}
|
// #endif
|
} else {
|
ctx = uni.createCanvasContext(this.canvasId,this);
|
}
|
const scale = this.cropperRect.width / this.target.width;
|
const dx = (this.cropperRect.left - this.imageRect.left) / scale;
|
const dy = (this.cropperRect.top - this.imageRect.top) / scale;
|
let image;
|
if(this.type2d){
|
image = canvas.createImage()
|
image.src = this.imageSrc;
|
}else{
|
image = this.imageSrc;
|
}
|
const x = -dx,
|
y = -dy,
|
w = this.imageRect.width / scale,
|
h = this.imageRect.height / scale;
|
ctx.save();
|
ctx.translate(x + w / 2, y + h / 2);
|
ctx.rotate((this.rotateAngle * Math.PI) / 180);
|
ctx.translate(-(x + w / 2), -(y + h / 2));
|
await this.drawImage(ctx,image, x, y, w, h);
|
ctx.restore();
|
if (this.mask !== '') {
|
let imageData;
|
if (this.type2d) {
|
imageData = ctx.getImageData(0, 0, this.target.width, this.target.height);
|
} else {
|
imageData = await new Promise(resolve => {
|
uni.canvasGetImageData({
|
canvasId: this.canvasId,
|
x: 0,
|
y: 0,
|
width: this.target.width,
|
height: this.target.height,
|
success(res) {
|
resolve(res);
|
}
|
},this);
|
});
|
}
|
ctx.clearRect(0, 0, this.target.width, this.target.height);
|
if(this.type2d){
|
image.src = this.mask;
|
}else{
|
image = this.mask;
|
}
|
await this.drawImage(ctx,image, 0, 0, this.target.width, this.target.height);
|
let maskData;
|
if (this.type2d) {
|
maskData = ctx.getImageData(0, 0, this.target.width, this.target.height);
|
} else {
|
maskData = await new Promise((resolve,reject) => {
|
uni.canvasGetImageData({
|
canvasId: this.canvasId,
|
x: 0,
|
y: 0,
|
width: this.target.width,
|
height: this.target.height,
|
success(res) {
|
resolve(res);
|
}
|
},this);
|
});
|
}
|
ctx.clearRect(0, 0, this.target.width, this.target.height);
|
for (let index = 3; index < maskData.data.length; index += 4) {
|
const alpha = maskData.data[index];
|
if (alpha !== 0) {
|
imageData.data[index] = 0;
|
}
|
}
|
if (this.type2d) {
|
ctx.putImageData(imageData, 0, 0);
|
} else {
|
await new Promise(resolve => {
|
uni.canvasPutImageData({
|
canvasId: this.canvasId,
|
x: 0,
|
y: 0,
|
width: imageData.width,
|
height: imageData.height,
|
data: imageData.data,
|
complete: res => {
|
resolve(res);
|
}
|
},this);
|
});
|
}
|
}
|
return new Promise((resolve,reject) => {
|
const params = {};
|
if (this.type2d) {
|
params.canvas = canvas;
|
} else {
|
params.canvasId = this.canvasId;
|
}
|
|
uni.canvasToTempFilePath({
|
...params,
|
destWidth: this.target.width,
|
destHeight: this.target.height,
|
quality: Number(this.quality) || 1,
|
fileType: this.fileType,
|
success: ({ tempFilePath }) => {
|
// #ifdef H5
|
var arr = tempFilePath.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;
|
resolve(
|
url.createObjectURL(
|
new Blob([u8arr], {
|
type: mime
|
})
|
)
|
);
|
// #endif
|
resolve(tempFilePath);
|
},
|
fail(err) {
|
console.log('保存失败,错误信息:',err);
|
reject(err);
|
}
|
},this);
|
});
|
}
|
}
|
};
|
</script>
|
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
<script module="wxsModule" lang="wxs">
|
var startTouchs = [];
|
var startDistance = 0;
|
var touchCenter = [];
|
var ratio = 0;
|
var imageInstance = null;
|
var cropperInstance = null;
|
var touchType = "";
|
var touchInstance = null;
|
var cropperRect = null;
|
var imageRect = null;
|
// 旋转角度
|
var rotateAngle = 0;
|
// 操作时改变的对象
|
var changes = {
|
imageRect: null,
|
cropperRect: null
|
}
|
|
function updateImageStyle() {
|
var imageRect = changes.imageRect
|
imageInstance.setStyle({
|
left: imageRect.left + 'px',
|
top: imageRect.top + 'px',
|
width: imageRect.width + 'px',
|
height: imageRect.height + 'px',
|
transform: 'rotate(' + rotateAngle + 'deg)'
|
})
|
}
|
|
function updateCopperStyle() {
|
var cropperRect = changes.cropperRect
|
cropperInstance.setStyle({
|
left: cropperRect.left + "px",
|
top: cropperRect.top + "px",
|
width: cropperRect.width + "px",
|
height: cropperRect.height + "px"
|
})
|
}
|
|
function imageScale(scaleRate) {
|
var cw = imageRect.width * (scaleRate - 1)
|
var ch = imageRect.height * (scaleRate - 1)
|
changes.imageRect = {
|
width: imageRect.width + cw,
|
height: imageRect.height + ch,
|
left: imageRect.left - cw * (touchCenter[0]),
|
top: imageRect.top - ch * (touchCenter[1])
|
}
|
}
|
function getImageRotateSizeChange(w,h){
|
var cw = h*Math.sin(rotateAngle/180 * Math.PI)
|
var ch = w*Math.sin(rotateAngle/180 * Math.PI)
|
return {cw,ch}
|
}
|
// 角度转弧度
|
function ang2deg(ang){
|
return ang/180*Math.PI
|
}
|
// 计算旋转后真实的图片大小
|
function getRealSize(){
|
var w = changes.imageRect.width
|
var h = changes.imageRect.height
|
var l = changes.imageRect.left
|
var t = changes.imageRect.top
|
// 内斜边
|
var R = Math.sqrt(w*w+h*h)
|
var angle = Math.atan(h/w) / Math.PI * 180
|
var rorate = rotateAngle%90
|
var direct = Math.floor(rotateAngle/90)
|
var width = R*Math.cos(ang2deg(angle-rorate))
|
var height = R*Math.sin(ang2deg(angle+rorate))
|
if(direct % 2 === 1){
|
var temp = width
|
width = height
|
height = temp
|
}
|
return {
|
width: width,
|
height: height,
|
left: l - (width - w)/2,
|
top: t - (height - h)/2,
|
dw: width - w,
|
dh: height - h
|
}
|
}
|
module.exports = {
|
touchStart: function (ev, oi) {
|
// #ifdef APP-PLUS || H5
|
ev.preventDefault();
|
ev.stopPropagation();
|
// #endif
|
touchInstance = ev.instance;
|
var dataSet = ev.instance.getDataset()
|
touchType = dataSet.type;
|
startTouchs = ev.touches;
|
oi.callMethod('onTouchStart')
|
if (startTouchs.length == 2) {
|
touchType = "image"
|
var x1 = startTouchs[0].clientX
|
var y1 = startTouchs[0].clientY
|
var x2 = startTouchs[1].clientX
|
var y2 = startTouchs[1].clientY
|
var distance = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)
|
startDistance = Math.sqrt(distance)
|
var leftPercent = ((x1 + x2) / 2 - imageRect.left) / imageRect.width
|
var topPercent = ((y1 + y2) / 2 - imageRect.top) / imageRect.height
|
touchCenter = [leftPercent, topPercent]
|
}
|
return false;
|
},
|
touchMove: function (ev, io) {
|
if (touchType == "") return false
|
// #ifdef H5
|
ev.preventDefault();
|
ev.stopPropagation();
|
// #endif
|
var touches = ev.touches;
|
var changeX1 = touches[0].clientX - startTouchs[0].clientX;
|
var changeY1 = touches[0].clientY - startTouchs[0].clientY;
|
if (startTouchs.length == 1) {
|
if (touchType === 'image') {
|
changes.imageRect.left = imageRect.left + changeX1;
|
changes.imageRect.top = imageRect.top + changeY1;
|
updateImageStyle()
|
} else if (touchType === 'controller') {
|
var directionX = 0;
|
if (touchInstance.hasClass('left')) {
|
directionX = -1;
|
}
|
if (touchInstance.hasClass('right')) {
|
directionX = 1;
|
}
|
var directionY = 0;
|
if (touchInstance.hasClass('top')) {
|
directionY = -1
|
}
|
if (touchInstance.hasClass('bottom')) {
|
directionY = 1
|
}
|
var changeX = changeX1 * directionX;
|
var changeY = changeY1 * directionY;
|
// 比例缩放控制
|
if (ratio !== 0) {
|
if (directionX * directionY !== 0) {
|
if (changeX / ratio > changeY) {
|
changeY = changeX / ratio
|
changeX = changeY * ratio
|
} else {
|
changeX = changeY * ratio
|
changeY = changeX / ratio
|
}
|
} else {
|
if (directionX == 0) {
|
changeX = changeY * ratio
|
} else {
|
changeY = changeX / ratio
|
}
|
}
|
}
|
var realSize = getRealSize()
|
var width = cropperRect.width + changeX
|
var height = cropperRect.height + changeY
|
// var imageRight = imageRect.left + imageRect.width
|
// var imageBottom = imageRect.top + imageRect.height
|
var imageRight = realSize.left+realSize.width
|
var imageBottom = realSize.top+realSize.height
|
if (directionX != -1) {
|
if (cropperRect.left + width > imageRight) {
|
width = imageRight - cropperRect.left
|
if (ratio !== 0) {
|
height = width / ratio
|
}
|
}
|
} else {
|
var cLeft = cropperRect.left - changeX
|
if (cLeft < realSize.left) {
|
width = cropperRect.left + cropperRect.width - realSize.left
|
if (ratio !== 0) {
|
height = width / ratio
|
}
|
}
|
}
|
// 判断是否触底
|
if (directionY != -1) {
|
if (cropperRect.top + height > imageBottom) {
|
height = imageBottom - cropperRect.top
|
if (ratio !== 0) {
|
width = height * ratio
|
}
|
}
|
} else {
|
var cTop = cropperRect.top - changeY
|
if (cTop < realSize.top) {
|
height = cropperRect.top + cropperRect.height - realSize.top
|
if (ratio !== 0) {
|
width = height * ratio
|
}
|
}
|
}
|
if (directionX == -1) {
|
changes.cropperRect.left = cropperRect.left + cropperRect.width - width
|
}
|
if (directionY == -1) {
|
changes.cropperRect.top = cropperRect.top + cropperRect.height - height
|
}
|
// 边界控制
|
changes.cropperRect.width = width
|
changes.cropperRect.height = height
|
updateCopperStyle()
|
}
|
} else if (touches.length == 2 && startTouchs.length == 2) {
|
var changeX2 = touches[0].clientX - touches[1].clientX;
|
var changeY2 = touches[0].clientY - touches[1].clientY;
|
var distance = Math.pow(changeX2, 2) + Math.pow(changeY2, 2)
|
distance = Math.sqrt(distance)
|
// 放大比例
|
var scaleRate = distance / startDistance
|
imageScale(scaleRate)
|
updateImageStyle()
|
}
|
return false;
|
},
|
touchEnd: function (ev, oi) {
|
if (touchType === "image") {
|
var cropperLeft = cropperRect.left
|
var cropperRight = cropperRect.left + cropperRect.width
|
var cropperTop = cropperRect.top
|
var cropperBottom = cropperTop + cropperRect.height
|
var cropperRate = cropperRect.width / cropperRect.height
|
var realSize = getRealSize()
|
var rate = realSize.width / realSize.height
|
if (realSize.width < cropperRect.width || realSize.height < cropperRect.height) {
|
var scale = 1
|
if (rate < cropperRate) {
|
scale = cropperRect.width / realSize.width
|
} else {
|
scale = cropperRect.height / realSize.height
|
}
|
imageRect.width = changes.imageRect.width
|
imageRect.height = changes.imageRect.height
|
imageScale(scale)
|
}
|
// 边界控制start
|
if (cropperLeft < realSize.left) {
|
changes.imageRect.left = cropperLeft + realSize.dw/2
|
}
|
if (cropperRight > realSize.left + realSize.width) {
|
changes.imageRect.left = cropperRight - realSize.width + realSize.dw/2
|
}
|
if (cropperTop < realSize.top) {
|
changes.imageRect.top = cropperTop + realSize.dh/2
|
}
|
if (cropperBottom > realSize.top + realSize.height) {
|
changes.imageRect.top = cropperBottom - realSize.height + realSize.dh/2
|
}
|
// 边界控制end
|
updateImageStyle()
|
}
|
oi.callMethod('updateData', {
|
cropperRect: changes.cropperRect,
|
imageRect: changes.imageRect,
|
})
|
touchType = ""
|
startTouchs = []
|
return false;
|
},
|
// 将逻辑层的图像变换同步过来
|
// 裁剪比例变化
|
changeRatio: function (value) {
|
ratio = value
|
},
|
changeRotateAngle:function (value){
|
rotateAngle = value;
|
if(imageInstance){
|
updateImageStyle()
|
}
|
var realSize = getRealSize()
|
},
|
changeImageRect: function (value, oldValue, oi) {
|
if (value) {
|
imageRect = value;
|
changes.imageRect = {
|
left: value.left,
|
top: value.top,
|
width: value.width,
|
height: value.height
|
};
|
// #ifndef MP-WEIXIN || MP-QQ
|
setTimeout(function() {
|
imageInstance = oi.selectComponent('.mainContent > .image')
|
updateImageStyle();
|
});
|
// #endif
|
// #ifdef MP-WEIXIN || MP-QQ
|
imageInstance = oi.selectComponent('.mainContent > .image')
|
updateImageStyle();
|
// #endif
|
}
|
},
|
changeCropper: function (value, oldValue, oi) {
|
if (value) {
|
cropperRect = value
|
changes.cropperRect = {
|
left: value.left,
|
top: value.top,
|
width: value.width,
|
height: value.height
|
}
|
// #ifdef H5 || APP-VUE
|
setTimeout(function() {
|
// #endif
|
cropperInstance = oi.selectComponent('.mainContent > .cropper')
|
updateCopperStyle()
|
// #ifdef H5 || APP-VUE
|
});
|
// #endif
|
}
|
}
|
}
|
</script>
|
<!-- #endif -->
|
<style lang="scss" scoped>
|
.bt-container {
|
// display: flex;
|
// flex-direction: column;
|
// justify-content: space-between;
|
height: 100%;
|
box-sizing: border-box;
|
// background-color: #0e1319;
|
position: relative;
|
overflow: hidden;
|
|
.bt-canvas {
|
position: fixed;
|
left: 100%;
|
top: 0;
|
}
|
|
.mainContent {
|
// flex: 1;
|
// flex-shrink:0;
|
// margin: 60rpx;
|
width: 100%;
|
height: 100%;
|
position: relative;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
// touch-action: none;
|
|
.image {
|
position: absolute;
|
// will-change: transform;
|
// transform-origin: center center;
|
width: 85%;
|
height: 85%;
|
will-change: left, top, width, height;
|
}
|
|
.controller {
|
position: absolute;
|
z-index: 99;
|
padding: 10rpx;
|
$offset: -20rpx;
|
|
&::after {
|
display: block;
|
content: '';
|
filter: drop-shadow(0 0px 10rpx rgba(0, 0, 0, 0.3));
|
}
|
|
&.vertical {
|
top: calc(50% - 30rpx);
|
}
|
|
&.horizon {
|
left: calc(50% - 30rpx);
|
}
|
|
&.left {
|
&::after {
|
height: 40rpx;
|
border-left: 10rpx solid #fff;
|
}
|
|
left: $offset;
|
}
|
|
&.right {
|
&::after {
|
height: 40rpx;
|
border-right: 10rpx solid #fff;
|
}
|
|
right: $offset;
|
}
|
|
&.top {
|
top: $offset;
|
|
&::after {
|
width: 40rpx;
|
border-top: 10rpx solid #fff;
|
}
|
}
|
|
&.bottom {
|
bottom: $offset;
|
|
&::after {
|
width: 40rpx;
|
border-bottom: 10rpx solid #fff;
|
}
|
}
|
|
&.left.bottom,
|
&.right.bottom,
|
&.left.top,
|
&.left.bottom {
|
&::after {
|
width: 30rpx;
|
height: 30rpx;
|
background-color: transparent;
|
}
|
}
|
}
|
|
.cropper {
|
position: absolute;
|
border: 1px solid #eee;
|
box-sizing: content-box;
|
// transform-origin: center center;
|
outline: 999px solid rgba(0, 0, 0, 0.5);
|
will-change: left, top, width, height;
|
|
// display: contain;
|
// pointer-events: none;
|
.mask {
|
position: absolute;
|
left: 0;
|
top: 0;
|
width: 100%;
|
height: 100%;
|
opacity: 0.5;
|
}
|
|
.line {
|
position: absolute;
|
// background-color: #eee;
|
}
|
|
.row {
|
width: 100%;
|
height: 0px;
|
left: 0;
|
border-top: 1px dashed #007aff;
|
}
|
|
.col {
|
height: 100%;
|
width: 0px;
|
border-left: 1px dashed #007aff;
|
}
|
|
.row1 {
|
top: 33%;
|
}
|
|
.row2 {
|
top: 66%;
|
}
|
|
.col1 {
|
left: 33%;
|
}
|
|
.col2 {
|
left: 66%;
|
}
|
}
|
}
|
|
// .slot {
|
// position: relative;
|
// padding-top: 20rpx;
|
// }
|
}
|
|
.anim {
|
transition: 0.2s;
|
}
|
</style>
|