Files
vpUni/.svn/pristine/de/de6415475f0d55fdbcad59bd207d6eb86f0f32d1.svn-base
2026-03-09 16:39:03 +08:00

1108 lines
32 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="t-cropper" v-show="imageUrl" disable-scroll>
<!-- #ifdef MP-WEIXIN || MP-ALIPAY-->
<canvas
type="2d"
canvas-id="canvas-cropper"
id="canvas-cropper"
class="canvas"
></canvas>
<!-- #endif -->
<!-- #ifdef APP-PLUS || H5 -->
<canvas
:canvas-id="canvasId"
:style="{
width: origin.canvasWidth + 'px',
height: origin.canvasHeight + 'px',
}"
></canvas>
<!-- #endif -->
<!-- 裁剪区域 -->
<view class="t-preview-container" :class="{ showPage: inInit }">
<view
class="preview-body"
@touchstart="(e) => touchStart(e, 'body')"
@touchmove="touchMove"
@touchend="touchEnd"
@touchcancel="touchCancel"
>
<!-- 完整背景图片 -->
<view
class="image-wrap"
:class="{ transit: origin.transit }"
:style="[comImageStyle('image-wrap')]"
>
<image
class="image"
:class="{ transit: origin.transit }"
:src="origin.imageUrl"
@load="imageLoad"
:style="[comImageStyle('image')]"
/>
</view>
<view class="mask-model"></view>
<view
class="frame-box"
:class="{ transit: origin.transit }"
:style="[comImageStyle('frame-box')]"
>
<view class="rect" :style="cirStyle">
<!-- 裁剪框图片 -->
<view
class="image-rect"
:class="{ transit: origin.transit }"
:style="[comImageStyle('image-rect')]"
>
<image
class="rect-img"
:class="{ transit: origin.transit }"
:src="origin.imageUrl"
:style="[comImageStyle('image')]"
/>
</view>
</view>
<!-- 矩阵框线条 -->
<view v-if="radius < 50" class="line-box">
<view class="line-one"></view>
<view class="line-two"></view>
<view class="line-three"></view>
<view class="line-four"></view>
</view>
<view
class="frame-left-top"
@touchstart.stop="(e) => touchStart(e, 'left-top')"
></view>
<view
class="frame-left-bottom"
@touchstart.stop="(e) => touchStart(e, 'left-bottom')"
></view>
<view
class="frame-right-top"
@touchstart.stop="(e) => touchStart(e, 'right-top')"
></view>
<view
class="frame-right-bottom"
@touchstart.stop="(e) => touchStart(e, 'right-bottom')"
></view>
</view>
</view>
<!-- 底部工具栏 -->
<view class="toolbar">
<view @tap.stop="onCancle" class="btn-cancel">取消</view>
<view
v-if="isRotateBtn && mode !== 'free'"
@tap.stop="onAngle"
class="btn-rotate"
>
<image src="../../static/svg/rotate.svg" data-type="inverse" />
</view>
<view @tap.stop="onConfirm" class="btn-confirm">确定</view>
</view>
</view>
</view>
</template>
<script>
/**
* @name:防抖
* @param {function} fn
* @param {wait} wait
* @return {function}
*/
const debounce = (fn, wait = 1000) => {
let time = null;
return function (...args) {
if (time) clearTimeout(time);
time = setTimeout(() => {
fn.apply(this, args);
}, wait);
};
};
export default {
/**
* @property {String} mode 模式
* @value fixed 固定模式,裁剪出固定大小
* @value ratio 等比模式,宽高等比缩放
* @value free 自由模式,不限制宽高比
* @property {String} imageUrl 图片路径
* @property {Number} width 宽度
* @property {Number} height 高度
* @property {Number} maxWidth 最大宽度
* @property {Number} minHeight 最大高度
* @property {Number} scaleRatio 裁剪比列缩放
* @property {Number} minRatio 最小缩放
* @property {Number} maxRatio 最大缩放
* @property {Boolean} isRotateBtn 是否显示旋转
* @property {Boolean} isCutSize 是否导出高清裁剪原图
* @property {Number} radius 裁剪图片圆角半径单位px
* @property {Number} delay 快速重复点击时间
*/
props: {
mode: {
type: String,
default: "ratio",
},
imageUrl: {
type: String,
default: "",
},
width: {
type: Number,
default: 200,
},
height: {
type: Number,
default: 200,
},
maxWidth: {
type: Number,
default: 1024,
},
maxHeight: {
type: Number,
default: 1024,
},
scaleRatio: {
type: Number,
default: 0.7,
},
minRatio: {
type: Number,
default: 1,
},
maxRatio: {
type: Number,
default: 3,
},
isRotateBtn: {
type: Boolean,
default: true,
},
isCutSize: {
type: Boolean,
default: true,
},
radius: {
type: Number,
default: 0,
},
delay: {
type: Number,
default: 250,
},
},
mounted() {
this.onConfirm = this.createConfirm(); //确定按钮防抖
},
data() {
return {
inInit: false, //是否初始化
canvasId: Math.random().toString(36).slice(-6), //获取组件实例
// 获取原始图片的大小
origin: {
imageUrl: "", //设置图片url
real: {
//原始图片宽高
width: 100,
height: 100,
},
body: {
//页面宽高
width: 100,
height: 100,
},
frame: {
//矩形框
left: 50,
top: 50,
width: 200,
height: 300,
},
bgImage: {
//背景框
left: 20,
top: 20,
width: 300,
height: 400,
},
rotate: 0,
transit: false,
canvasWidth: 100,
canvasHeight: 100,
},
//绘制手指触摸
move: {
touchType: "body", //移动的类型
start: {
//开始的位置
frame: {
left: 0,
top: 0,
width: 0,
height: 0,
},
bgImage: {
left: 0,
top: 0,
width: 0,
height: 0,
},
},
},
touch: {
touchStart: [], //记录开始触摸
},
frameSize: 150, //裁剪框最小尺寸
};
},
watch: {
// 监听原始图片的变动
imageUrl(val) {
if (val) {
uni.showLoading({
title: "请稍候...",
mask: true,
});
this.origin.imageUrl = val; //设置图片url
}
},
},
computed: {
//计算圆角比列
cirStyle() {
const { width } = this.origin.frame;
let scale = this.width / width;
let radius = this.radius / scale;
return `border-radius: ${radius}px`;
},
// 公共图片背景位置
comImageStyle() {
return (source) => {
const { left, top, width, height } = this.origin.bgImage;
if (source == "image-wrap") {
//计算背景盒子样式
const style = {};
style.left = left + "px";
style.top = top + "px";
style.width = width + "px";
style.height = height + "px";
return style;
} else if (source == "image") {
// 计算背景图片样式
let left = 0;
let top = 0;
let width = this.origin.bgImage.width;
let height = this.origin.bgImage.height;
if (this.origin.rotate % 180 != 0) {
width = this.origin.bgImage.height;
height = this.origin.bgImage.width;
top = width / 2 - height / 2;
left = height / 2 - width / 2;
}
const style = {};
style.left = left + "px";
style.top = top + "px";
style.width = width + "px";
style.height = height + "px";
style.transform = `rotate(${this.origin.rotate}deg)`;
return style;
} else if (source == "image-rect") {
// 计算裁剪框上显示的图片位置
const style = {};
style.left = left - this.origin.frame.left + "px";
style.top = top - this.origin.frame.top + "px";
style.width = width + "px";
style.height = height + "px";
return style;
} else if (source == "frame-box") {
//裁剪矩形框
const style = {};
style.left = this.origin.frame.left + "px";
style.top = this.origin.frame.top + "px";
style.width = this.origin.frame.width + "px";
style.height = this.origin.frame.height + "px";
return style;
}
};
},
},
methods: {
// 检测图片加载完成
imageLoad(e) {
const { width, height } = e.detail;
this.origin.real.width = width;
this.origin.real.height = height;
var query = uni.createSelectorQuery().in(this);
query
.select(".preview-body")
.boundingClientRect((data) => {
this.origin.body.width = data.width;
this.origin.body.height = data.height;
this.inInit = true;
this.imageReset(); //重置图片
})
.exec();
},
//重置图片
imageReset() {
this.origin.rotate = 0;
let frameRate = this.width / this.height; //裁剪比列
let frameHeight = this.origin.body.height * this.scaleRatio; //裁剪框图片高度 * 缩小0.7倍
let frameWidth = this.origin.body.width * this.scaleRatio; //裁剪框图片宽度 * 缩小0.7倍
// 缩放后的宽度/高度 > 组件裁剪的比例,就要对宽度重写
if (frameWidth / frameHeight > frameRate) {
frameWidth = frameHeight * frameRate;
} else {
frameHeight = frameWidth / frameRate;
}
// 裁剪框左边距=页面宽度-裁剪框宽度 / 2
let frameleft = (this.origin.body.width - frameWidth) / 2;
let frameTop = (this.origin.body.height - frameHeight) / 2;
this.origin.frame = {
left: frameleft,
top: frameTop,
width: frameWidth,
height: frameHeight,
};
// 背景图片位置:
let bgRate = this.origin.real.width / this.origin.real.height; //背景比列
let bgWidth = frameWidth;
let bgHeight = frameHeight;
// 裁剪框图片宽度/裁剪框图片高度>实际的图片比例
if (bgWidth / bgHeight > bgRate) {
bgHeight = bgWidth / bgRate;
} else {
bgWidth = bgHeight * bgRate;
}
// 背景左边距
let bgLeft = (frameWidth - bgWidth) / 2 + this.origin.frame.left;
// 背景右边距
let bgTop = (frameHeight - bgHeight) / 2 + this.origin.frame.top;
this.origin.bgImage = {
left: bgLeft,
top: bgTop,
width: bgWidth,
height: bgHeight,
};
uni.hideLoading();
},
// 取消按钮
onCancle() {
this.$emit("cancel");
},
onAngle() {
this.origin.rotate -= 90; // 旋转的角度
let width = this.origin.bgImage.height; // 背景框宽度
let height = this.origin.bgImage.width; // 背景框高度
let left = this.origin.bgImage.left; // 背景框左边距
let top = this.origin.bgImage.top; // 背景框顶边距
let fWidth = this.origin.frame.width; // 裁剪框宽度
let fLeft = this.origin.frame.left; // 裁剪框左边距
let fTop = this.origin.frame.top; // 裁剪框顶边距
// 左边距 = 矩形框左边距 + (背景框顶部 - 裁剪框顶部)
left = fLeft + (top - fTop);
// 顶部边距 = 矩形框顶边距 -(背景框高度 - 矩形框宽度 -(矩形框左边距 - 背景框左边距))
top = fTop - (height - fWidth - (fLeft - this.origin.bgImage.left));
this.origin.bgImage = {
left: left,
top: top,
width: width,
height: height,
};
this.origin.transit = true;
setTimeout(() => {
this.origin.transit = false;
}, 300);
},
// 确定按钮
onConfirm() {},
//确定按钮防抖
createConfirm() {
return debounce(() => {
// #ifdef H5 || APP-PLUS
this.confirmH5();
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY
this.confirmWx();
// #endif
}, this.delay);
},
async confirmH5() {
let that = this;
let mx = this.computeMatrix(); //获取画布的信息
// 设备画布的宽高
this.origin.canvasWidth = mx.tw;
this.origin.canvasHeight = mx.th;
uni.showLoading({
title: "处理中",
});
// 设置宽高后等待更新完成在获取Canvas节点
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 200);
});
// 创建一个画布
let ctx = uni.createCanvasContext(this.canvasId, this);
// 矩形内清除指定的像素
await ctx.clearRect(0, 0, mx.tw, mx.th);
//剪切圆角
await this.drawClipImage(ctx, mx);
// 旋转
ctx.rotate((this.origin.rotate * Math.PI) / 180);
//绘制图片
ctx.drawImage(
that.imageUrl,
mx.sx,
mx.sy,
mx.sw,
mx.sh,
mx.dx,
mx.dy,
mx.dw,
mx.dh
);
ctx.restore();
ctx.draw(false, () => {
uni.canvasToTempFilePath(
{
canvasId: that.canvasId,
// #ifdef H5
destWidth: that.isCutSize ? mx.tw : that.origin.frame.width,
destHeight: that.isCutSize ? mx.th : that.origin.frame.height,
// #endif
success: (res) => {
var path = res.tempFilePath;
// #ifdef H5
path = that.parseBlob(path);
that.$emit("confirm", {
errMsg: "parseBlob:ok",
tempFilePath: path,
});
// #endif
// #ifdef APP-PLUS
that.$emit("confirm", res);
// #endif
},
fail: (err) => {
console.log(err);
},
complete: () => {
uni.hideLoading(); //关闭loading
},
},
that
);
});
},
confirmWx() {
uni.showLoading({
title: "处理中",
});
let mx = this.computeMatrix(); //获取画布的信息
uni
.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select("#canvas-cropper")
.fields({
node: true,
size: true,
})
.exec(async (res) => {
const textCanvas = res[0].node;
const ctx = textCanvas.getContext("2d");
// 初始化画布大小
textCanvas.width = mx.tw;
textCanvas.height = mx.th;
// //绘制canvas
await this.drawClipImage(ctx, mx); //剪切圆角
await ctx.rotate((this.origin.rotate * Math.PI) / 180);
await this.createImage(this.origin.imageUrl, mx, ctx, textCanvas);
// #ifdef MP-WEIXIN
wx.canvasToTempFilePath({
canvas: textCanvas,
canvasId: "canvasID",
success: (res) => {
this.$emit("confirm", res);
},
fail: (err) => {
console.log(err);
},
});
// #endif
// #ifdef MP-ALIPAY
textCanvas.toTempFilePath({
success: (res) => {
this.$emit("confirm", res);
},
fail: (err) => {
console.log(err);
},
});
// #endif
uni.hideLoading(); //关闭loading
});
},
//剪切图片圆角
drawClipImage(ctx, mx) {
let { radius } = this;
if (radius > 0) {
const w = Math.round(mx.tw); //画布宽度
const h = Math.round(mx.th); //画布高度
// 被缩放后的尺寸且裁剪的宽高相同
if (this.width === this.height && w == h && this.width < w) {
radius = (w / this.width) * radius;
}
// 被缩放后的尺寸且裁剪的宽高不相同
if (this.width != this.height && w != h && this.width < w) {
if (w > h) {
radius = (w / h) * radius;
} else {
radius = (h / w) * radius;
}
}
// 如果裁剪是圆形
if (w === h && radius >= w / 2) {
// 圆形
ctx.arc(w / 2, h / 2, w / 2, 0, 2 * Math.PI);
} else {
// 圆角矩形
if (w !== h) {
// 限制圆角半径不能超过短边的一半
radius = Math.min(w / 2, h / 2, radius);
}
// 其他形状
ctx.moveTo(radius, 0);
ctx.arcTo(w, 0, w, h, radius);
ctx.arcTo(w, h, 0, h, radius);
ctx.arcTo(0, h, 0, 0, radius);
ctx.arcTo(0, 0, w, 0, radius);
ctx.closePath();
}
ctx.clip(); //从原始画布剪切任意形状和尺寸的区域
ctx.restore(); //返回之前保存过的路径状态和属性
}
},
// 创建一个图片
createImage(img, mx, ctx, textCanvas) {
return new Promise((resolve) => {
const headerImg = textCanvas.createImage();
headerImg.src = img;
headerImg.onload = () => {
let result = ctx.drawImage(
headerImg,
mx.sx,
mx.sy,
mx.sw,
mx.sh,
mx.dx,
mx.dy,
mx.dw,
mx.dh
);
resolve(result);
};
});
},
// 计算矩阵
computeMatrix() {
let width = this.width;
let height = this.height;
let mul = this.origin.bgImage.width / this.origin.real.width;
if (this.origin.rotate % 180 != 0) {
mul = this.origin.bgImage.height / this.origin.real.width;
}
if (this.mode != "fixed") {
width = this.origin.frame.width / mul;
height = this.origin.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.origin.frame.left - this.origin.bgImage.left) / mul;
var sy = (this.origin.frame.top - this.origin.bgImage.top) / mul;
var sw = this.origin.frame.width / mul;
var sh = this.origin.frame.height / mul;
var ox = sx + sw / 2;
var oy = sy + sh / 2;
if (this.origin.rotate % 180 != 0) {
var temp = sw;
sw = sh;
sh = temp;
}
var angle = this.origin.rotate % 360;
if (angle < 0) {
angle += 360;
}
if (angle == 270) {
var x = this.origin.real.width - oy;
var y = ox;
ox = x;
oy = y;
}
if (angle == 180) {
var x = this.origin.real.width - ox;
var y = this.origin.real.height - oy;
ox = x;
oy = y;
}
if (angle == 90) {
var x = oy;
var y = this.origin.real.height - ox;
ox = x;
oy = y;
}
sx = ox - sw / 2;
sy = oy - sh / 2;
let dr = { x: 0, y: 0, w: width, h: height };
dr = this.parseRect(dr, -this.origin.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,
};
},
//计算矩阵
parseRect(rect, angle) {
let x1 = rect.x;
let y1 = rect.y;
let x2 = rect.x + rect.w;
let y2 = rect.y + rect.h;
let p1 = this.parsePoint({ x: x1, y: y1 }, angle);
let p2 = this.parsePoint({ x: x2, y: y2 }, angle);
let 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;
},
//计算x、y
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;
},
//base64转blob
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 }));
},
touchStart(e, touchType = "") {
// #ifdef APP-PLUS || H5
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
// #endif
switch (touchType) {
case "body":
this.move.touchType = "body";
break;
case "left-top":
this.move.touchType = "left-top";
break;
case "left-bottom":
this.move.touchType = "left-bottom";
break;
case "right-top":
this.move.touchType = "right-top";
break;
case "right-bottom":
this.move.touchType = "right-bottom";
break;
default:
this.move.touchType = "body";
break;
}
this.touch.touchStart = e.touches;
// 裁剪框的开始位置
this.move.start.frame.left = this.origin.frame.left;
this.move.start.frame.top = this.origin.frame.top;
this.move.start.frame.width = this.origin.frame.width;
this.move.start.frame.height = this.origin.frame.height;
// 背景图片的开始位置
this.move.start.bgImage.left = this.origin.bgImage.left;
this.move.start.bgImage.top = this.origin.bgImage.top;
this.move.start.bgImage.width = this.origin.bgImage.width;
this.move.start.bgImage.height = this.origin.bgImage.height;
return false;
},
touchMove(e) {
// #ifdef APP-PLUS || H5
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
// #endif
// 单手操作
if (this.touch.touchStart.length == 1) {
if (this.move.touchType == "body") {
// 移动图片
this.moveImage(this.touch.touchStart[0], e.touches[0]);
} else {
if (this.mode != "fixed") {
// 放大缩小图片
this.scaleFrame(this.touch.touchStart[0], e.touches[0]);
}
}
} else if (this.touch.touchStart.length == 2 && e.touches.length == 2) {
var ta = this.touch.touchStart[0];
var tb = this.touch.touchStart[1];
var tc = e.touches[0];
var td = e.touches[1];
if (ta.identifier != tc.identifier) {
var temp = tc;
tc = td;
td = temp;
}
this.scaleImage(ta, tb, tc, td);
}
},
// 移动图片
moveImage(ta, tb) {
let mx = tb.clientX - ta.clientX;
let my = tb.clientY - ta.clientY;
// 开始移动图片背景 + 移动的左边距离
this.origin.bgImage.left = this.move.start.bgImage.left + mx;
this.origin.bgImage.top = this.move.start.bgImage.top + my;
let frameLeft = this.origin.frame.left;
let frameTop = this.origin.frame.top;
let frameWidth = this.origin.frame.width;
let frameHeight = this.origin.frame.height;
// 左边触边
if (this.origin.bgImage.left > frameLeft) {
this.origin.bgImage.left = frameLeft;
}
// 头部触边
if (this.origin.bgImage.top > frameTop) {
this.origin.bgImage.top = frameTop;
}
// 右边触边
if (this.origin.bgImage.left + this.origin.bgImage.width < frameLeft + frameWidth) {
this.origin.bgImage.left = frameLeft + frameWidth - this.origin.bgImage.width;
}
// 底部触边:背景距离头部+背景的高度 < 背景高度 + 矩形框顶部
if (this.origin.bgImage.top + this.origin.bgImage.height < frameTop + frameHeight) {
this.origin.bgImage.top = frameTop + frameHeight - this.origin.bgImage.height;
}
},
// 缩放裁剪框
scaleFrame(ta, tb) {
let mx = tb.clientX - ta.clientX;
let my = tb.clientY - ta.clientY;
let x1 = this.move.start.frame.left;
let y1 = this.move.start.frame.top;
let x2 = this.move.start.frame.left + this.move.start.frame.width;
let y2 = this.move.start.frame.top + this.move.start.frame.height;
let cx1 = false;
let cy1 = false;
let cx2 = false;
let cy2 = false;
let mix = this.frameSize;
let rate = this.origin.frame.width / this.origin.frame.height;
const { width: fWidth, height: fHeight } = this.origin.frame;
const { width: bgWidth, height: bgHeight } = this.origin.bgImage;
const { width: realWidth, height: realHeight } = this.origin.real;
//左上角
if (this.move.touchType == "left-top") {
x1 += mx;
y1 += my;
cx1 = true;
cy1 = true;
// 左下角
} else if (this.move.touchType == "left-bottom") {
x1 += mx;
y2 += my;
cx1 = true;
cy2 = true;
// 右上角
} else if (this.move.touchType == "right-top") {
x2 += mx;
y1 += my;
cx2 = true;
cy1 = true;
// 右下角
} else if (this.move.touchType == "right-bottom") {
x2 += mx;
y2 += my;
cx2 = true;
cy2 = true;
}
if (x1 < this.origin.bgImage.left) {
x1 = this.origin.bgImage.left;
}
if (y1 < this.origin.bgImage.top) {
y1 = this.origin.bgImage.top;
}
if (x2 > this.origin.bgImage.left + this.origin.bgImage.width) {
x2 = this.origin.bgImage.left + this.origin.bgImage.width;
}
if (y2 > this.origin.bgImage.top + this.origin.bgImage.height) {
y2 = this.origin.bgImage.top + this.origin.bgImage.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 (this.mode != "free") {
var val = x2 - rate * (y2 - y1);
if (x1 < val) {
x1 = val;
}
}
}
if (cy1) {
if (this.mode != "free") {
var val = y2 - (x2 - x1) / rate;
if (y1 < val) {
y1 = val;
}
}
}
if (cx2) {
if (this.mode != "free") {
var val = rate * (y2 - y1) + x1;
if (x2 > val) {
x2 = val;
}
}
}
if (cy2) {
if (this.mode != "free") {
var val = (x2 - x1) / rate + y1;
if (y2 > val) {
y2 = val;
}
}
}
// 裁剪框缩放限制
const owFrame = x2 - x1;
const ohFrame = y2 - y1;
if (owFrame < fWidth || ohFrame < fHeight) {
const currentRatio = this.scaleRatio * this.maxRatio;
const isScale =
bgWidth * currentRatio >= realWidth / this.scaleRatio &&
bgHeight * currentRatio >= realHeight / this.scaleRatio;
if (isScale) {
return false;
}
}
this.origin.frame.left = x1;
this.origin.frame.top = y1;
this.origin.frame.width = x2 - x1;
this.origin.frame.height = y2 - y1;
},
scaleImage(ta, tb, tc, td) {
let x1 = ta.clientX;
let y1 = ta.clientY;
let x2 = tb.clientX;
let y2 = tb.clientY;
let x3 = tc.clientX;
let y3 = tc.clientY;
let x4 = td.clientX;
let y4 = td.clientY;
let ol = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
let el = Math.sqrt((x3 - x4) * (x3 - x4) + (y3 - y4) * (y3 - y4));
let ocx = (x1 + x2) / 2;
let ocy = (y1 + y2) / 2;
let ecx = (x3 + x4) / 2;
let ecy = (y3 + y4) / 2;
let ax = ecx - ocx;
let ay = ecy - ocy;
let scale = el / ol;
// 检测放大/缩小倍率
if (this.move.start.bgImage.width * scale < this.origin.frame.width) {
scale = this.origin.frame.width / this.move.start.bgImage.width;
}
if (this.move.start.bgImage.height * scale < this.origin.frame.height) {
scale = this.origin.frame.height / this.move.start.bgImage.height;
}
// 最大缩放
if (
this.move.start.bgImage.width * scale >
this.origin.frame.width * this.maxRatio
) {
return;
}
this.origin.bgImage.left =
this.move.start.bgImage.left +
ax -
(ocx - this.move.start.bgImage.left) * (scale - 1);
this.origin.bgImage.top =
this.move.start.bgImage.top +
ay -
(ocy - this.move.start.bgImage.top) * (scale - 1);
this.origin.bgImage.width = this.move.start.bgImage.width * scale;
this.origin.bgImage.height = this.move.start.bgImage.height * scale;
if (this.origin.bgImage.left > this.origin.frame.left) {
this.origin.bgImage.left = this.origin.frame.left;
}
if (this.origin.bgImage.top > this.origin.frame.top) {
this.origin.bgImage.top = this.origin.frame.top;
}
if (
this.origin.bgImage.left + this.origin.bgImage.width <
this.origin.frame.left + this.origin.frame.width
) {
this.origin.bgImage.left =
this.origin.frame.left + this.origin.frame.width - this.origin.bgImage.width;
}
if (
this.origin.bgImage.top + this.origin.bgImage.height <
this.origin.frame.top + this.origin.frame.height
) {
this.origin.bgImage.top =
this.origin.frame.top + this.origin.frame.height - this.origin.bgImage.height;
}
},
// 手指触摸动作结束
touchEnd() {
this.touch.touchStart = [];
this.resizeImage(); //调整图片的大小
},
// 手指触摸动作被打断,如来电提醒,弹窗
touchCancel() {
this.touch.touchStart = [];
},
// 调整图片的大小
resizeImage() {
var rate = this.origin.frame.width / this.origin.frame.height; //比列
var width = this.origin.body.width * this.scaleRatio; //裁剪框图片高度 * 缩小0.7倍
var height = this.origin.body.height * this.scaleRatio; //裁剪框图片宽度 * 缩小0.7倍
// 图片的位置:
// 缩放后的宽度/高度 > 组件裁剪的比例,就要对宽度重写
if (width / height > rate) {
width = height * rate;
} else {
height = width / rate;
}
var left = (this.origin.body.width - width) / 2;
var top = (this.origin.body.height - height) / 2;
var mul = width / this.origin.frame.width;
var ox = this.origin.frame.left - this.origin.bgImage.left;
var oy = this.origin.frame.top - this.origin.bgImage.top;
this.origin.frame = {
left: left,
top: top,
width: width,
height: height,
};
width = this.origin.bgImage.width * mul;
height = this.origin.bgImage.height * mul;
left = this.origin.frame.left - ox * mul;
top = this.origin.frame.top - oy * mul;
this.origin.bgImage = {
left: left,
top: top,
width: width,
height: height,
};
if (mul != 1) {
this.origin.transit = true;
setTimeout(() => {
this.origin.transit = false;
}, 300);
}
},
},
};
</script>
<style>
page {
overflow: hidden;
overscroll-behavior: none;
}
</style>
<style lang="scss" scoped>
@import "./tt-cropper";
</style>