123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107 |
- <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>
|