uni-image-editor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1009 @@
1
+ <template>
2
+ <view
3
+ class="lime-clipper"
4
+ :class="{ open: value }"
5
+ disable-scroll
6
+ :style="'z-index: ' + zIndex + ';' + customStyle"
7
+ >
8
+ <view
9
+ class="lime-clipper-mask"
10
+ @touchstart.stop.prevent="clipTouchStart"
11
+ @touchmove.stop.prevent="clipTouchMove"
12
+ @touchend.stop.prevent="clipTouchEnd"
13
+ >
14
+ <view class="lime-clipper__content" :style="clipStyle"
15
+ ><view
16
+ class="lime-clipper__edge"
17
+ v-for="(item, index) in [0, 0, 0, 0]"
18
+ :key="index"
19
+ ></view
20
+ ></view>
21
+ </view>
22
+ <image
23
+ class="lime-clipper-image"
24
+ @error="imageLoad"
25
+ @load="imageLoad"
26
+ @touchstart="imageTouchStart"
27
+ @touchmove="imageTouchMove"
28
+ @touchend="imageTouchEnd"
29
+ :src="image"
30
+ :mode="imageWidth == 'auto' ? 'widthFix' : 'scaleToFill'"
31
+ v-if="image"
32
+ :style="imageStyle"
33
+ />
34
+ <canvas
35
+ canvas-id="lime-clipper"
36
+ id="lime-clipper"
37
+ disable-scroll
38
+ :style="
39
+ 'width: ' +
40
+ canvasWidth * scaleRatio +
41
+ 'px; height:' +
42
+ canvasHeight * scaleRatio +
43
+ 'px;'
44
+ "
45
+ class="lime-clipper-canvas"
46
+ ></canvas>
47
+ <view class="lime-clipper-tools">
48
+ <view class="lime-clipper-tools__btns">
49
+ <view v-if="isShowCancelBtn" @tap="cancel">
50
+ <slot name="cancel" v-if="$slots.cancel" />
51
+ <view v-else class="cancel">取消</view>
52
+ </view>
53
+ <view v-if="isShowPhotoBtn" @tap="uploadImage">
54
+ <slot name="photo" v-if="$slots.photo" />
55
+ <image v-else src="/img/photo.svg" />
56
+ </view>
57
+ <view v-if="isShowRotateBtn" @tap="rotate">
58
+ <slot name="rotate" v-if="$slots.rotate" />
59
+ <image v-else src="/img/rotate.svg" data-type="inverse" />
60
+ </view>
61
+ <view v-if="isShowConfirmBtn" @tap="confirm">
62
+ <slot name="confirm" v-if="$slots.confirm" />
63
+ <view v-else class="confirm">确定</view>
64
+ </view>
65
+ </view>
66
+ <slot></slot>
67
+ </view>
68
+ </view>
69
+ </template>
70
+
71
+ <script>
72
+ import { validImgHasEmpty } from "../../js/utils";
73
+ import {
74
+ determineDirection,
75
+ calcImageOffset,
76
+ calcImageScale,
77
+ calcImageSize,
78
+ calcPythagoreanTheorem,
79
+ clipTouchMoveOfCalculate,
80
+ imageTouchMoveOfCalcOffset,
81
+ } from "./utils";
82
+ const cache = {};
83
+ export default {
84
+ name: "lime-clipper",
85
+ props: {
86
+ value: {
87
+ type: Boolean,
88
+ default: true,
89
+ },
90
+ // #ifdef MP-WEIXIN
91
+ type: {
92
+ type: String,
93
+ default: "2d",
94
+ },
95
+ // #endif
96
+ customStyle: {
97
+ type: String,
98
+ },
99
+ zIndex: {
100
+ type: Number,
101
+ default: 99,
102
+ },
103
+ imageUrl: {
104
+ type: String,
105
+ },
106
+ fileType: {
107
+ type: String,
108
+ default: "png",
109
+ },
110
+ quality: {
111
+ type: Number,
112
+ default: 1,
113
+ },
114
+ width: {
115
+ type: Number,
116
+ default: 400,
117
+ },
118
+ height: {
119
+ type: Number,
120
+ default: 400,
121
+ },
122
+ minWidth: {
123
+ type: Number,
124
+ default: 200,
125
+ },
126
+ maxWidth: {
127
+ type: Number,
128
+ default: 600,
129
+ },
130
+ destWidth: Number,
131
+ destHeight: Number,
132
+ minHeight: {
133
+ type: Number,
134
+ default: 200,
135
+ },
136
+ maxHeight: {
137
+ type: Number,
138
+ default: 600,
139
+ },
140
+ isLockWidth: {
141
+ type: Boolean,
142
+ default: false,
143
+ },
144
+ isLockHeight: {
145
+ type: Boolean,
146
+ default: false,
147
+ },
148
+ isLockRatio: {
149
+ type: Boolean,
150
+ default: true,
151
+ },
152
+ scaleRatio: {
153
+ type: Number,
154
+ default: 1,
155
+ },
156
+ minRatio: {
157
+ type: Number,
158
+ default: 0.5,
159
+ },
160
+ maxRatio: {
161
+ type: Number,
162
+ default: 2,
163
+ },
164
+ isDisableScale: {
165
+ type: Boolean,
166
+ default: false,
167
+ },
168
+ useRenderSize:{
169
+ type: Boolean,
170
+ default: false,
171
+ },
172
+ isDisableRotate: {
173
+ type: Boolean,
174
+ default: false,
175
+ },
176
+ isLimitMove: {
177
+ type: Boolean,
178
+ default: false,
179
+ },
180
+ isShowPhotoBtn: {
181
+ type: Boolean,
182
+ default: true,
183
+ },
184
+ isShowRotateBtn: {
185
+ type: Boolean,
186
+ default: true,
187
+ },
188
+ isShowConfirmBtn: {
189
+ type: Boolean,
190
+ default: true,
191
+ },
192
+ isShowCancelBtn: {
193
+ type: Boolean,
194
+ default: true,
195
+ },
196
+ rotateAngle: {
197
+ type: Number,
198
+ default: 90,
199
+ },
200
+ beforeConfirm: {
201
+ type: Function,
202
+ default: () => { }
203
+ },
204
+ source: {
205
+ type: Object,
206
+ default: () => ({
207
+ album: "从相册中选择",
208
+ camera: "拍照",
209
+ // #ifdef MP-WEIXIN
210
+ message: "从微信中选择",
211
+ // #endif
212
+ }),
213
+ },
214
+ },
215
+ data() {
216
+ return {
217
+ canvasWidth: 0,
218
+ canvasHeight: 0,
219
+ clipX: 0,
220
+ clipY: 0,
221
+ clipWidth: 0,
222
+ clipHeight: 0,
223
+ animation: false,
224
+ imageWidth: 0,
225
+ imageHeight: 0,
226
+ imageTop: 0,
227
+ imageLeft: 0,
228
+ scale: 1,
229
+ angle: 0,
230
+ image: "",
231
+ imageInit: false,
232
+ sysinfo: {},
233
+ throttleTimer: null,
234
+ throttleFlag: true,
235
+ timeClipCenter: null,
236
+ flagClipTouch: false,
237
+ flagEndTouch: false,
238
+ clipStart: {},
239
+ animationTimer: null,
240
+ touchRelative: [{ x: 0, y: 0 }],
241
+ hypotenuseLength: 0,
242
+ ctx: null,
243
+ canvasId: "lime-clipper",
244
+ };
245
+ },
246
+ computed: {
247
+ // canvasId() {
248
+ // return `l-clipper-${this._ ? this._.uid : this._uid}`
249
+ // },
250
+ clipStyle() {
251
+ const { clipWidth, clipHeight, clipY, clipX, animation } = this;
252
+ return `
253
+ width: ${clipWidth}px;
254
+ height:${clipHeight}px;
255
+ transition-property: ${animation ? "" : "background"};
256
+ left: ${clipX}px;
257
+ top: ${clipY}px
258
+ `;
259
+ },
260
+ imageStyle() {
261
+ const {
262
+ imageWidth,
263
+ imageHeight,
264
+ imageLeft,
265
+ imageTop,
266
+ animation,
267
+ scale,
268
+ angle,
269
+ } = this;
270
+ return `
271
+ width: ${imageWidth ? imageWidth + "px" : "auto"};
272
+ height: ${imageHeight ? imageHeight + "px" : "auto"};
273
+ transform: translate3d(${imageLeft - imageWidth / 2}px, ${
274
+ imageTop - imageHeight / 2
275
+ }px, 0) scale(${scale}) rotate(${angle}deg);
276
+ transition-duration: ${animation ? 0.35 : 0}s
277
+ `;
278
+ },
279
+ clipSize() {
280
+ const { clipWidth, clipHeight } = this;
281
+ return { clipWidth, clipHeight };
282
+ },
283
+ clipPoint() {
284
+ const { clipY, clipX } = this;
285
+ return { clipY, clipX };
286
+ },
287
+ },
288
+ watch: {
289
+ value(val) {
290
+ if (!val) {
291
+ this.animation = 0;
292
+ this.angle = 0;
293
+ } else {
294
+ if (this.imageUrl) {
295
+ const {
296
+ imageWidth,
297
+ imageHeight,
298
+ imageLeft,
299
+ imageTop,
300
+ scale,
301
+ clipX,
302
+ clipY,
303
+ clipWidth,
304
+ clipHeight,
305
+ path,
306
+ } = cache?.[this.imageUrl] || {};
307
+ if (path != this.image) {
308
+ this.image = this.imageUrl;
309
+ } else {
310
+ this.setDiffData({
311
+ imageWidth,
312
+ imageHeight,
313
+ imageLeft,
314
+ imageTop,
315
+ scale,
316
+ clipX,
317
+ clipY,
318
+ clipWidth,
319
+ clipHeight,
320
+ });
321
+ }
322
+ }
323
+ }
324
+ },
325
+ imageUrl(url) {
326
+ console.log(url,'asaas');
327
+ this.image = url;
328
+ },
329
+ image(url) {
330
+ console.log(url,'getImageInfo')
331
+ this.getImageInfo(url);
332
+ },
333
+ clipSize({ widthVal, heightVal }) {
334
+ let { minWidth, minHeight } = this;
335
+ minWidth = minWidth / 2;
336
+ minHeight = minHeight / 2;
337
+ if (widthVal < minWidth) {
338
+ this.setDiffData({ clipWidth: minWidth });
339
+ }
340
+ if (heightVal < minHeight) {
341
+ this.setDiffData({ clipHeight: minHeight });
342
+ }
343
+
344
+
345
+ this.calcClipSize();
346
+
347
+ },
348
+ angle(val) {
349
+ this.animation = this.imageInit;
350
+ this.moveStop();
351
+ const { isLimitMove } = this;
352
+ if (isLimitMove && val % 90) {
353
+ this.setDiffData({
354
+ angle: Math.round(val / 90) * 90,
355
+ });
356
+ }
357
+ this.imgMarginDetectionScale();
358
+ },
359
+ animation(val) {
360
+ clearTimeout(this.animationTimer);
361
+ if (val) {
362
+ let animationTimer = setTimeout(() => {
363
+ this.setDiffData({
364
+ animation: false,
365
+ });
366
+ }, 260);
367
+ this.setDiffData({ animationTimer });
368
+ this.animationTimer = animationTimer;
369
+ }
370
+ },
371
+ isLimitMove(val) {
372
+ if (val) {
373
+ if (this.angle % 90) {
374
+ this.setDiffData({
375
+ angle: Math.round(this.angle / 90) * 90,
376
+ });
377
+ }
378
+ this.imgMarginDetectionScale();
379
+ }
380
+ },
381
+ clipPoint() {
382
+ this.cutDetectionPosition();
383
+ },
384
+ width(width, oWidth) {
385
+ console.log(width);
386
+ if (width !== oWidth) {
387
+ this.setDiffData({
388
+ clipWidth: uni.upx2px(width), //width / 2
389
+ });
390
+ }
391
+ },
392
+ height(height, oHeight) {
393
+ console.log(height);
394
+ if (height !== oHeight) {
395
+ this.setDiffData({
396
+ clipHeight: uni.upx2px(height), //height / 2
397
+ });
398
+ }
399
+ },
400
+ },
401
+ mounted() {
402
+ const sysinfo = uni.getSystemInfoSync();
403
+ this.sysinfo = sysinfo;
404
+ this.setClipInfo();
405
+ console.log('getSystemInfoSync');
406
+ this.image = this.imageUrl || this.image;
407
+ this.setClipCenter();
408
+ this.calcClipSize();
409
+ this.cutDetectionPosition();
410
+ },
411
+ methods: {
412
+ setDiffData(data) {
413
+ Object.keys(data).forEach((key) => {
414
+ if (this[key] !== data[key]) {
415
+ this[key] = data[key];
416
+ }
417
+ });
418
+ },
419
+ getImageInfo(url) {
420
+ if (!url) return;
421
+ if (this.value) {
422
+ uni.showLoading({
423
+ title: "请稍候...",
424
+ mask: true,
425
+ });
426
+ }
427
+ this.imageInit = false;
428
+ uni.getImageInfo({
429
+ src: url,
430
+ success: (res) => {
431
+ if (["right", "left"].includes(res.orientation)) {
432
+ this.imgComputeSize(res.height, res.width);
433
+ } else {
434
+ this.imgComputeSize(res.width, res.height);
435
+ }
436
+ this.image = res.path;
437
+ if (this.isLimitMove) {
438
+ this.imgMarginDetectionScale();
439
+ this.$emit("ready", res);
440
+ }
441
+ const {
442
+ imageWidth,
443
+ imageHeight,
444
+ imageLeft,
445
+ imageTop,
446
+ scale,
447
+ clipX,
448
+ clipY,
449
+ clipWidth,
450
+ clipHeight,
451
+ } = this;
452
+ cache[url] = Object.assign(res, {
453
+ imageWidth,
454
+ imageHeight,
455
+ imageLeft,
456
+ imageTop,
457
+ scale,
458
+ clipX,
459
+ clipY,
460
+ clipWidth,
461
+ clipHeight,
462
+ });
463
+ },
464
+ fail: (err) => {
465
+ this.imgComputeSize();
466
+ if (this.isLimitMove) {
467
+ this.imgMarginDetectionScale();
468
+ }
469
+ },
470
+ });
471
+ },
472
+ setClipInfo() {
473
+ const { width, height, sysinfo, canvasId } = this;
474
+ console.log(width, height, "width");
475
+ const clipWidth = uni.upx2px(width);
476
+ const clipHeight = uni.upx2px(height);
477
+ const clipY = (sysinfo.windowHeight - clipHeight) / 2;
478
+ const clipX = (sysinfo.windowWidth - clipWidth) / 2;
479
+ const imageLeft = sysinfo.windowWidth / 2;
480
+ const imageTop = sysinfo.windowHeight / 2;
481
+ this.ctx = uni.createCanvasContext(canvasId, this);
482
+ this.clipWidth = clipWidth;
483
+ this.clipHeight = clipHeight;
484
+ this.clipX = clipX;
485
+ this.clipY = clipY;
486
+ this.canvasHeight = clipHeight;
487
+ this.canvasWidth = clipWidth;
488
+ this.imageLeft = imageLeft;
489
+ this.imageTop = imageTop;
490
+ },
491
+ setClipCenter() {
492
+ const { sysInfo, clipHeight, clipWidth, imageTop, imageLeft } = this;
493
+ let sys = sysInfo || uni.getSystemInfoSync();
494
+ let clipY = (sys.windowHeight - clipHeight) * 0.5;
495
+ let clipX = (sys.windowWidth - clipWidth) * 0.5;
496
+ this.imageTop = imageTop - this.clipY + clipY;
497
+ this.imageLeft = imageLeft - this.clipX + clipX;
498
+ this.clipY = clipY;
499
+ this.clipX = clipX;
500
+ },
501
+ calcClipSize() {
502
+ console.log(this.clipY,this.clipX,this.clipHeight,this.clipWidth,'this.clipY,this.clipX,this.clipHeight,this.clipWidth');
503
+ const { clipHeight, clipWidth, sysinfo, clipX, clipY } = this;
504
+ if (clipWidth >= sysinfo.windowWidth) {
505
+ this.setDiffData({
506
+ clipWidth: sysinfo.windowWidth,
507
+ });
508
+ } else if (clipWidth + clipX > sysinfo.windowWidth) {
509
+ this.setDiffData({
510
+ clipX: sysinfo.windowWidth - clipX,
511
+ });
512
+ }
513
+ if (clipHeight > sysinfo.windowHeight) {
514
+ this.setDiffData({
515
+ clipHeight: sysinfo.windowHeight,
516
+ });
517
+ } else if (clipHeight + clipY > sysinfo.windowHeight) {
518
+ this.clipY = sysinfo.windowHeight - clipY;
519
+ this.setDiffData({
520
+ clipY: sysinfo.windowHeight - clipY,
521
+ });
522
+ }
523
+
524
+ },
525
+ cutDetectionPosition() {
526
+ const { clipX, clipY, sysinfo, clipHeight, clipWidth } = this;
527
+ let cutDetectionPositionTop = () => {
528
+ if (clipY < 0) {
529
+ this.setDiffData({ clipY: 0 });
530
+ }
531
+ if (clipY > sysinfo.windowHeight - clipHeight) {
532
+ this.setDiffData({ clipY: sysinfo.windowHeight - clipHeight });
533
+ }
534
+ },
535
+ cutDetectionPositionLeft = () => {
536
+ if (clipX < 0) {
537
+ this.setDiffData({ clipX: 0 });
538
+ }
539
+ if (clipX > sysinfo.windowWidth - clipWidth) {
540
+ this.setDiffData({ clipX: sysinfo.windowWidth - clipWidth });
541
+ }
542
+ };
543
+ if (clipY === null && clipX === null) {
544
+ let newClipY = (sysinfo.windowHeight - clipHeight) * 0.5;
545
+ let newClipX = (sysinfo.windowWidth - clipWidth) * 0.5;
546
+ this.setDiffData({
547
+ clipX: newClipX,
548
+ clipY: newClipY,
549
+ });
550
+ } else if (clipY !== null && clipX !== null) {
551
+ cutDetectionPositionTop();
552
+ cutDetectionPositionLeft();
553
+ } else if (clipY !== null && clipX === null) {
554
+ cutDetectionPositionTop();
555
+ this.setDiffData({
556
+ clipX: (sysinfo.windowWidth - clipWidth) / 2,
557
+ });
558
+ } else if (clipY === null && clipX !== null) {
559
+ cutDetectionPositionLeft();
560
+ this.setDiffData({
561
+ clipY: (sysinfo.windowHeight - clipHeight) / 2,
562
+ });
563
+ }
564
+ },
565
+ imgComputeSize(width, height) {
566
+
567
+
568
+ const { imageWidth, imageHeight } = calcImageSize(width, height, this);
569
+ console.log( imageWidth, imageHeight,' imageWidth, imageHeigh');
570
+
571
+ this.imageWidth = imageWidth;
572
+ this.imageHeight = imageHeight;
573
+ },
574
+ imgMarginDetectionScale(scale) {
575
+ if (!this.isLimitMove) return;
576
+ const currentScale = calcImageScale(this, scale);
577
+ this.imgMarginDetectionPosition(currentScale);
578
+ },
579
+ imgMarginDetectionPosition(scale) {
580
+ if (!this.isLimitMove) return;
581
+ const { scale: currentScale, left, top } = calcImageOffset(this, scale);
582
+ this.setDiffData({
583
+ imageLeft: left,
584
+ imageTop: top,
585
+ scale: currentScale,
586
+ });
587
+ },
588
+ throttle() {
589
+ this.setDiffData({
590
+ throttleFlag: true,
591
+ });
592
+ },
593
+ moveDuring() {
594
+ clearTimeout(this.timeClipCenter);
595
+ },
596
+ moveStop() {
597
+ clearTimeout(this.timeClipCenter);
598
+ const timeClipCenter = setTimeout(() => {
599
+ if (!this.animation) {
600
+ this.setDiffData({
601
+ imageInit: true,
602
+ animation: true,
603
+ });
604
+ }
605
+ this.setClipCenter();
606
+ }, 800);
607
+ this.setDiffData({ timeClipCenter });
608
+ },
609
+ clipTouchStart(event) {
610
+ // #ifdef H5
611
+ event.preventDefault();
612
+ // #endif
613
+ if (!this.image) {
614
+ uni.showToast({
615
+ title: "请选择图片",
616
+ icon: "none",
617
+ });
618
+ return;
619
+ }
620
+ const currentX = event.touches[0].clientX;
621
+ const currentY = event.touches[0].clientY;
622
+ const { clipX, clipY, clipWidth, clipHeight } = this;
623
+ const corner = determineDirection(
624
+ clipX,
625
+ clipY,
626
+ clipWidth,
627
+ clipHeight,
628
+ currentX,
629
+ currentY
630
+ );
631
+ this.moveDuring();
632
+ if (!corner) {
633
+ return;
634
+ }
635
+ this.clipStart = {
636
+ width: clipWidth,
637
+ height: clipHeight,
638
+ x: currentX,
639
+ y: currentY,
640
+ clipY,
641
+ clipX,
642
+ corner,
643
+ };
644
+ this.flagClipTouch = true;
645
+ this.flagEndTouch = true;
646
+ },
647
+ clipTouchMove(event) {
648
+ // #ifdef H5
649
+ event.stopPropagation();
650
+ event.preventDefault();
651
+ // #endif
652
+ if (!this.image) {
653
+ uni.showToast({
654
+ title: "请选择图片",
655
+ icon: "none",
656
+ });
657
+ return;
658
+ }
659
+ // 只针对单指点击做处理
660
+ if (event.touches.length !== 1) {
661
+ return;
662
+ }
663
+ const { flagClipTouch, throttleFlag } = this;
664
+ if (flagClipTouch && throttleFlag) {
665
+ const { isLockRatio, isLockHeight, isLockWidth } = this;
666
+ if (isLockRatio && (isLockWidth || isLockHeight)) return;
667
+ this.setDiffData({
668
+ throttleFlag: false,
669
+ });
670
+ this.throttle();
671
+ const clipData = clipTouchMoveOfCalculate(this, event);
672
+ if (clipData) {
673
+ const { width, height, clipX, clipY } = clipData;
674
+ if (!isLockWidth && !isLockHeight) {
675
+ this.setDiffData({
676
+ clipWidth: width,
677
+ clipHeight: height,
678
+ clipX,
679
+ clipY,
680
+ });
681
+ } else if (!isLockWidth) {
682
+ this.setDiffData({
683
+ clipWidth: width,
684
+ clipX,
685
+ });
686
+ } else if (!isLockHeight) {
687
+ this.setDiffData({
688
+ clipHeight: height,
689
+ clipY,
690
+ });
691
+ }
692
+ this.imgMarginDetectionScale();
693
+ }
694
+ }
695
+ },
696
+ clipTouchEnd() {
697
+ this.moveStop();
698
+ this.flagClipTouch = false;
699
+ },
700
+ imageTouchStart(e) {
701
+ // #ifdef H5
702
+ event.preventDefault();
703
+ // #endif
704
+ this.flagEndTouch = false;
705
+ const { imageLeft, imageTop } = this;
706
+ const clientXForLeft = e.touches[0].clientX;
707
+ const clientYForLeft = e.touches[0].clientY;
708
+
709
+ let touchRelative = [];
710
+ if (e.touches.length === 1) {
711
+ touchRelative[0] = {
712
+ x: clientXForLeft - imageLeft,
713
+ y: clientYForLeft - imageTop,
714
+ };
715
+ this.touchRelative = touchRelative;
716
+ } else {
717
+ const clientXForRight = e.touches[1].clientX;
718
+ const clientYForRight = e.touches[1].clientY;
719
+ let width = Math.abs(clientXForLeft - clientXForRight);
720
+ let height = Math.abs(clientYForLeft - clientYForRight);
721
+ const hypotenuseLength = calcPythagoreanTheorem(width, height);
722
+
723
+ touchRelative = [
724
+ {
725
+ x: clientXForLeft - imageLeft,
726
+ y: clientYForLeft - imageTop,
727
+ },
728
+ {
729
+ x: clientXForRight - imageLeft,
730
+ y: clientYForRight - imageTop,
731
+ },
732
+ ];
733
+ this.touchRelative = touchRelative;
734
+ this.hypotenuseLength = hypotenuseLength;
735
+ }
736
+ },
737
+ imageTouchMove(e) {
738
+ // #ifdef H5
739
+ event.preventDefault();
740
+ // #endif
741
+ const { flagEndTouch, throttleFlag } = this;
742
+ if (flagEndTouch || !throttleFlag) return;
743
+ const clientXForLeft = e.touches[0].clientX;
744
+ const clientYForLeft = e.touches[0].clientY;
745
+ this.setDiffData({ throttleFlag: false });
746
+ this.throttle();
747
+ this.moveDuring();
748
+ if (e.touches.length === 1) {
749
+ const { left: imageLeft, top: imageTop } = imageTouchMoveOfCalcOffset(
750
+ this,
751
+ clientXForLeft,
752
+ clientYForLeft
753
+ );
754
+ this.setDiffData({
755
+ imageLeft,
756
+ imageTop,
757
+ });
758
+ this.imgMarginDetectionPosition();
759
+ } else {
760
+ const clientXForRight = e.touches[1].clientX;
761
+ const clientYForRight = e.touches[1].clientY;
762
+ let width = Math.abs(clientXForLeft - clientXForRight),
763
+ height = Math.abs(clientYForLeft - clientYForRight),
764
+ hypotenuse = calcPythagoreanTheorem(width, height),
765
+ scale = this.scale * (hypotenuse / this.hypotenuseLength);
766
+ if (this.isDisableScale) {
767
+ scale = 1;
768
+ } else {
769
+ scale = scale <= this.minRatio ? this.minRatio : scale;
770
+ scale = scale >= this.maxRatio ? this.maxRatio : scale;
771
+ this.$emit("change", {
772
+ width: this.imageWidth * scale,
773
+ height: this.imageHeight * scale,
774
+ });
775
+ }
776
+
777
+ this.imgMarginDetectionScale(scale);
778
+ this.hypotenuseLength = Math.sqrt(
779
+ Math.pow(width, 2) + Math.pow(height, 2)
780
+ );
781
+ this.scale = scale;
782
+ }
783
+ },
784
+ imageTouchEnd() {
785
+ this.setDiffData({
786
+ flagEndTouch: true,
787
+ });
788
+ this.moveStop();
789
+ },
790
+ uploadImage() {
791
+ const itemList = Object.entries(this.source);
792
+ const sizeType = ["original", "compressed"];
793
+ const success = ({ tempFilePaths: a, tempFiles: b }) => {
794
+ this.image = a ? a[0] : b[0].path;
795
+ };
796
+ const _uploadImage = (type) => {
797
+ if (type !== "message") {
798
+ uni.chooseImage({
799
+ count: 1,
800
+ sizeType,
801
+ sourceType: [type],
802
+ success,
803
+ });
804
+ }
805
+ // #ifdef MP-WEIXIN
806
+ if (type == "message") {
807
+ wx.chooseMessageFile({
808
+ count: 1,
809
+ type: "image",
810
+ success,
811
+ });
812
+ }
813
+ // #endif
814
+ };
815
+ if (itemList.length > 1) {
816
+ uni.showActionSheet({
817
+ itemList: itemList.map((v) => v[1]),
818
+ success: ({ tapIndex: i }) => {
819
+ _uploadImage(itemList[i][0]);
820
+ },
821
+ });
822
+ } else {
823
+ _uploadImage(itemList[0][0]);
824
+ }
825
+ },
826
+ imageReset() {
827
+ const sys = this.sysinfo || uni.getSystemInfoSync();
828
+ this.moveStop();
829
+ this.scale = 1;
830
+ this.angle = 0;
831
+
832
+ this.imageTop = sys.windowHeight / 2;
833
+ this.imageLeft = sys.windowWidth / 2;
834
+ },
835
+ imageLoad(e) {
836
+ this.imageReset();
837
+ uni.hideLoading();
838
+ this.$emit("ready", e.detail);
839
+ },
840
+ rotate(event) {
841
+ if (this.isDisableRotate) return;
842
+ if (!this.image) {
843
+ uni.showToast({
844
+ title: "请选择图片",
845
+ icon: "none",
846
+ });
847
+ return;
848
+ }
849
+ const { rotateAngle } = this;
850
+ const originAngle = this.angle;
851
+ const type = event.currentTarget.dataset.type;
852
+ if (type === "along") {
853
+ this.angle = originAngle + rotateAngle;
854
+ } else {
855
+ this.angle = originAngle - rotateAngle;
856
+ }
857
+ this.$emit("rotate", this.angle);
858
+ },
859
+
860
+ // 判断图片是否生成正常
861
+ validImage(canvasId, width, height,) {
862
+ return new Promise((resolve, reject) => {
863
+ uni.canvasGetImageData({
864
+ canvasId,
865
+ x: 0,
866
+ y: 0,
867
+ width,
868
+ height,
869
+ success: ({ data }) => {
870
+
871
+ const valid = validImgHasEmpty(data, width * height)
872
+ valid ? resolve(valid) : reject("图片有空白部分");
873
+ },
874
+ fail: (err) => {
875
+ reject(err);
876
+ },
877
+ });
878
+ });
879
+ },
880
+ confirm() {
881
+ if (!this.image) {
882
+ uni.showToast({
883
+ title: "请选择图片",
884
+ icon: "none",
885
+ });
886
+ return;
887
+ }
888
+ uni.showLoading({
889
+ title: "加载中",
890
+ });
891
+ const {
892
+ canvasHeight,
893
+ canvasWidth,
894
+ clipHeight,
895
+ clipWidth,
896
+ scale,
897
+ ctx,
898
+ imageLeft,
899
+ imageTop,
900
+ clipX,
901
+ clipY,
902
+ angle,
903
+ scaleRatio: dpr,
904
+ image,
905
+ quality,
906
+ fileType,
907
+ type: imageType,
908
+ canvasId,
909
+ } = this;
910
+ const draw = () => {
911
+ const imageWidth = this.imageWidth * scale * dpr;
912
+ const imageHeight = this.imageHeight * scale * dpr;
913
+ const xpos = imageLeft - clipX;
914
+ const ypos = imageTop - clipY;
915
+ // const ctx = uni.createCanvasContext(canvasId, this);
916
+ ctx.translate(xpos * dpr, ypos * dpr);
917
+ ctx.rotate((angle * Math.PI) / 180);
918
+ ctx.drawImage(
919
+ image,
920
+ -imageWidth / 2,
921
+ -imageHeight / 2,
922
+ imageWidth,
923
+ imageHeight
924
+ );
925
+ ctx.draw(false, () => {
926
+ const width = clipWidth * dpr;
927
+ const height = clipHeight * dpr;
928
+ let params = {
929
+ x: 0,
930
+ y: 0,
931
+ width,
932
+ height,
933
+ destWidth: this.destWidth || width,
934
+ destHeight: this.destHeight || height,
935
+ canvasId,
936
+ fileType,
937
+ quality,
938
+ success: async(res) => {
939
+ // 钉钉小程序
940
+ data.url = res.tempFilePath || res.filePath;
941
+ uni.hideLoading();
942
+
943
+ let valid = true
944
+
945
+ try {
946
+ await this.validImage(canvasId, width, height)
947
+
948
+ valid = true
949
+
950
+ } catch (error) {
951
+
952
+ console.log(`图片校验失败${error}`);
953
+ valid = false
954
+ }
955
+
956
+
957
+
958
+ if (this.beforeConfirm) {
959
+ valid = await this.beforeConfirm(valid)
960
+ }
961
+
962
+ if (!valid) {
963
+ return
964
+ }
965
+
966
+
967
+ this.$emit("success", data);
968
+ this.$emit("input", false);
969
+ },
970
+ fail: (error) => {
971
+ console.error("error", error);
972
+ this.$emit("fail", error);
973
+ this.$emit("input", false);
974
+ },
975
+ };
976
+
977
+ let data = {
978
+ url: "",
979
+ width,
980
+ height,
981
+ };
982
+ uni.canvasToTempFilePath(params, this);
983
+ });
984
+ };
985
+
986
+ if (canvasWidth !== clipWidth || canvasHeight !== clipHeight) {
987
+ this.canvasWidth = clipWidth;
988
+ this.canvasHeight = clipHeight;
989
+ ctx.draw();
990
+ this.$nextTick(() => {
991
+ setTimeout(() => {
992
+ draw();
993
+ }, 100);
994
+ });
995
+ } else {
996
+ draw();
997
+ }
998
+ },
999
+ cancel() {
1000
+ this.$emit("cancel", false);
1001
+ this.$emit("input", false);
1002
+ },
1003
+ },
1004
+ };
1005
+ </script>
1006
+
1007
+ <style lang="scss" scoped>
1008
+ @import "./index";
1009
+ </style>