@didaoktv/didaoui-uniapp 1.2.1 → 1.2.2

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.
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <view class="dd-cropper">
3
- <!-- <image :src="imgSrc.imgSrc" @click="select" :style="[ imgStyle ]" class="my-avatar"></image> -->
4
3
  <dd-canvas ref="avatarCanvas" :canvas-id="'avatar-canvas-' + instanceId" class="my-canvas" :width="windowWidth || 0" :height="windowHeight || 0" :style="{ top: styleTop, height: cvsStyleHeight }" bg-color="transparent"></dd-canvas>
5
4
  <dd-canvas ref="operCanvas" :canvas-id="'oper-canvas-' + instanceId" class="oper-canvas" :width="windowWidth || 0" :height="windowHeight || 0" :style="{ top: styleTop, height: cvsStyleHeight }" bg-color="transparent" @touchstart="start" @touchmove="move" @touchend="end"></dd-canvas>
6
5
  <dd-canvas ref="prvCanvas" :canvas-id="'prv-canvas-' + instanceId" class="prv-canvas" :width="windowWidth || 0" :height="windowHeight || 0" bg-color="transparent" @touchstart="hideImg" :style="{ height: cvsStyleHeight, top: prvTop }"></dd-canvas>
@@ -8,65 +7,154 @@
8
7
  <view class="oper">
9
8
  <view class="btn-wrapper" v-if="showOper">
10
9
  <view @click="select" hover-class="hover" :style="{ width: btnWidth }">
11
- <text>{{ t("dd.common.re-select") }}</text>
10
+ <text>重选</text>
12
11
  </view>
13
12
  <view @click="close" hover-class="hover" :style="{ width: btnWidth }">
14
- <text>{{ t("dd.common.close") }}</text>
13
+ <text>关闭</text>
15
14
  </view>
16
15
  <view @click="rotate" hover-class="hover" :style="{ width: btnWidth, display: btnDsp }">
17
- <text>{{ t("dd.common.rotate") }}</text>
16
+ <text>旋转</text>
18
17
  </view>
19
18
  <view @click="preview" hover-class="hover" :style="{ width: btnWidth }">
20
- <text>{{ t("dd.common.preview") }}</text>
19
+ <text>预览</text>
21
20
  </view>
22
21
  <view @click="confirm" hover-class="hover" :style="{ width: btnWidth }">
23
- <text>{{ t("dd.common.confirm") }}</text>
22
+ <text>确定</text>
24
23
  </view>
25
24
  </view>
26
25
  <view class="clr-wrapper" v-else>
27
26
  <slider class="my-slider" @change="colorChange" block-size="25" value="0" min="-100" max="100" activeColor="red" backgroundColor="green" block-color="grey" show-value></slider>
28
27
  <view @click="prvUpload" hover-class="hover" :style="{ width: btnWidth }">
29
- <text>{{ t("dd.common.confirm") }}</text>
28
+ <text>确定</text>
30
29
  </view>
31
30
  </view>
32
31
  </view>
33
32
  </view>
34
33
  <view @click="chooseImage(0, {})" v-if="styleDisplay == 'none'">
35
- <slot>
36
-
37
- </slot>
34
+ <slot></slot>
38
35
  </view>
39
36
  </view>
40
37
  </template>
41
38
 
42
39
  <script>
43
- import { t } from '../../libs/i18n'
40
+ // 画布系组件(dd-canvas / dd-signature / dd-qrcode 同族)统一采用 Options API,
41
+ // 命令式 canvas 状态挂在 this 上不走响应式,避免触摸高频重绘的代理开销。
42
+
43
+ // 底部操作条高度(px),与样式 .oper-wrapper 的 height 保持一致
44
44
  const tabHeight = 50;
45
+
45
46
  export default {
46
47
  name: "dd-cropper",
48
+ emits: ['avtinit', 'confirm', 'cancel'],
49
+ props: {
50
+ // 最小缩放倍数
51
+ minScale: {
52
+ type: [Number, String],
53
+ default: 0.3
54
+ },
55
+ // 最大缩放倍数
56
+ maxScale: {
57
+ type: [Number, String],
58
+ default: 4
59
+ },
60
+ // 是否允许双指缩放
61
+ canScale: {
62
+ type: Boolean,
63
+ default: true
64
+ },
65
+ // 是否允许旋转(inner 模式下不可用)
66
+ canRotate: {
67
+ type: Boolean,
68
+ default: true
69
+ },
70
+ lockWidth: {
71
+ type: String,
72
+ default: ''
73
+ },
74
+ lockHeight: {
75
+ type: String,
76
+ default: ''
77
+ },
78
+ // 拉伸模式:x / y / long / short / longSel / shortSel
79
+ stretch: {
80
+ type: String,
81
+ default: ''
82
+ },
83
+ // 锁定模式:x / y / long / short / longSel / shortSel
84
+ lock: {
85
+ type: String,
86
+ default: ''
87
+ },
88
+ // 页面无底部 tabbar 时保持 true
89
+ noTab: {
90
+ type: Boolean,
91
+ default: true
92
+ },
93
+ // 内部模式:隐藏旋转按钮、按钮加宽
94
+ inner: {
95
+ type: Boolean,
96
+ default: false
97
+ },
98
+ // 导出图片质量(0-1),仅对压缩格式生效
99
+ quality: {
100
+ type: [Number, String],
101
+ default: 0.9
102
+ },
103
+ // 实例标识,confirm 事件原样回传
104
+ index: {
105
+ type: [Number, String],
106
+ default: ''
107
+ },
108
+ // 是否允许拖拽四角控制点调整裁剪框大小
109
+ canChangeSize: {
110
+ type: Boolean,
111
+ default: false
112
+ },
113
+ // 裁剪区域宽度,如 '300rpx'
114
+ areaWidth: {
115
+ type: String,
116
+ default: '300rpx'
117
+ },
118
+ // 裁剪区域高度,如 '300rpx'
119
+ areaHeight: {
120
+ type: String,
121
+ default: '300rpx'
122
+ },
123
+ // 导出图片宽度,如 '260rpx'
124
+ exportWidth: {
125
+ type: String,
126
+ default: '260rpx'
127
+ },
128
+ // 导出图片高度,如 '260rpx'
129
+ exportHeight: {
130
+ type: String,
131
+ default: '260rpx'
132
+ },
133
+ // 画布填充色,默认透明,可传 'black'、'#ffffff' 等
134
+ fillColor: {
135
+ type: String,
136
+ default: 'transparent'
137
+ },
138
+ },
47
139
  data() {
48
140
  return {
49
- // 添加实例ID用于区分不同实例
50
- instanceId: Date.now() + '-' + Math.random().toString(36).substr(2, 9),
141
+ // 实例ID用于区分不同实例的 canvas-id
142
+ instanceId: Date.now() + '-' + Math.random().toString(36).substring(2, 11),
51
143
  cvsStyleHeight: '0px',
52
144
  styleDisplay: 'none',
53
145
  styleTop: '-10000px',
54
146
  prvTop: '-10000px',
55
- imgStyle: {},
56
147
  selStyle: {},
57
148
  showOper: true,
149
+ // confirm 回载荷(avatar 字段)
58
150
  imgSrc: {
59
151
  imgSrc: ''
60
152
  },
61
153
  btnWidth: '19%',
62
154
  btnDsp: 'flex',
63
- // 裁剪区域宽度,用于设置选择区域的宽度
64
- arWidth: '',
65
- // 裁剪区域高度,用于设置选择区域的高度
66
- arHeight: '',
67
- // 导出图片宽度,用于设置最终导出图片的宽度
155
+ // 导出图片宽度(px,经 rpx 换算)
68
156
  expWidth: '',
69
- // 导出图片高度,用于设置最终导出图片的高度
157
+ // 导出图片高度(px,经 rpx 换算)
70
158
  expHeight: '',
71
159
  // 是否允许调整裁剪框大小
72
160
  letChangeSize: false,
@@ -74,48 +162,13 @@
74
162
  safeAreaInsetsBottom: 0,
75
163
  };
76
164
  },
77
- watch: {
78
- avatarSrc() {
79
- this.imgSrc.imgSrc = this.avatarSrc;
80
- }
81
- },
82
- emits: ['avtinit', 'confirm'],
83
- props: {
84
- minScale: '',
85
- maxScale: '',
86
- canScale: true,
87
- canRotate: true,
88
- lockWidth: '',
89
- lockHeight: '',
90
- stretch: '',
91
- lock: '',
92
- noTab: true,
93
- inner: false,
94
- quality: '',
95
- index: '',
96
- canChangeSize: false,
97
- areaWidth: '300rpx',
98
- // 裁剪区域高度,用于设置选择区域的高度
99
- areaHeight: '300rpx',
100
- // 导出图片宽度,用于设置最终导出图片的宽度
101
- exportWidth: '260rpx',
102
- // 导出图片高度,用于设置最终导出图片的高度
103
- exportHeight: '260rpx',
104
- // 画布填充色,默认透明,可传 'black'、'#ffffff' 等
105
- fillColor: {
106
- type: String,
107
- default: 'transparent'
108
- },
109
- },
110
165
  created() {
111
166
  this.ctxCanvas = null;
112
167
  this.ctxCanvasOper = null;
113
168
  this.ctxCanvasPrv = null;
114
169
  this.qlty = parseInt(this.quality) || 0.9;
115
- this.imgSrc.imgSrc = this.imageSrc;
116
170
  this.letRotate = (this.canRotate === false || this.inner === true) ? 0 : 1;
117
171
  this.letScale = this.canScale === false ? 0 : 1;
118
- // 是否允许调整裁剪框大小,false表示不允许,其他值表示允许
119
172
  this.letChangeSize = this.canChangeSize;
120
173
  this.isin = this.inner === true ? 1 : 0;
121
174
  this.indx = this.index || undefined;
@@ -148,7 +201,6 @@
148
201
  this.initCanvasRefs(true);
149
202
  },
150
203
  methods: {
151
- t,
152
204
  async initCanvasRefs(force = false) {
153
205
  await this.$nextTick();
154
206
  const avatarCanvas = this.$refs.avatarCanvas;
@@ -191,28 +243,6 @@
191
243
  // #endif
192
244
  this.pxRatio = this.windowWidth / 750;
193
245
 
194
- let style = this.avatarStyle;
195
- if (style && style !== true && (style = style.trim())) {
196
- style = style.split(';');
197
- let obj = {};
198
- for (let v of style) {
199
- if (!v) continue;
200
- v = v.trim().split(':');
201
- if (v[1].indexOf('rpx') >= 0) {
202
- let arr = v[1].trim().split(' ');
203
- for (let k in arr) {
204
- if (!arr[k]) continue;
205
- if (arr[k].indexOf('rpx') >= 0) {
206
- arr[k] = parseFloat(arr[k]) * this.pxRatio + 'px';
207
- }
208
- }
209
- v[1] = arr.join(' ');
210
- }
211
- obj[v[0].trim()] = v[1].trim();
212
- }
213
- this.imgStyle = obj;
214
- }
215
-
216
246
  this.expWidth && (this.expWidth = this.expWidth.indexOf('rpx') >= 0 ? parseInt(this.expWidth) * this.pxRatio : parseInt(this.expWidth));
217
247
  this.expHeight && (this.expHeight = this.expHeight.indexOf('rpx') >= 0 ? parseInt(this.expHeight) * this.pxRatio : parseInt(this.expHeight));
218
248
 
@@ -225,7 +255,6 @@
225
255
  if (this.fSelecting) return;
226
256
  this.fSelecting = true;
227
257
  setTimeout(() => { this.fSelecting = false; }, 500);
228
- const self = this
229
258
  uni.chooseImage({
230
259
  count: 1,
231
260
  sizeType: ['original', 'compressed'],
@@ -241,16 +270,16 @@
241
270
  this.path = path;
242
271
  if (!this.hasSel) {
243
272
  let style = this.selStyle || {};
244
- if (this.arWidth && this.arHeight) {
245
- let areaWidth = this.arWidth.indexOf('rpx') >= 0 ? parseInt(this.arWidth) * this.pxRatio : parseInt(this.arWidth),
246
- areaHeight = this.arHeight.indexOf('rpx') >= 0 ? parseInt(this.arHeight) * this.pxRatio : parseInt(this.arHeight);
273
+ if (this.areaWidth && this.areaHeight) {
274
+ let areaWidth = this.areaWidth.indexOf('rpx') >= 0 ? parseInt(this.areaWidth) * this.pxRatio : parseInt(this.areaWidth),
275
+ areaHeight = this.areaHeight.indexOf('rpx') >= 0 ? parseInt(this.areaHeight) * this.pxRatio : parseInt(this.areaHeight);
247
276
  style.width = areaWidth + 'px';
248
277
  style.height = areaHeight + 'px';
249
278
  style.top = (this.windowHeight - areaHeight - tabHeight) / 2 + 'px';
250
279
  style.left = (this.windowWidth - areaWidth) / 2 + 'px';
251
280
  } else {
252
281
  uni.showModal({
253
- title: t("dd.cropper.emptyWidhtOrHeight"),
282
+ title: "裁剪框的宽或高没有设置",
254
283
  showCancel: false
255
284
  })
256
285
  return;
@@ -270,7 +299,7 @@
270
299
  },
271
300
  fail: () => {
272
301
  uni.showToast({
273
- title: "error3",
302
+ title: "读取图片失败",
274
303
  duration: 2000,
275
304
  })
276
305
  },
@@ -278,8 +307,9 @@
278
307
  uni.hideLoading();
279
308
  }
280
309
  });
281
- }, fail(err) {
282
- self.$emit('cancel')
310
+ },
311
+ fail: () => {
312
+ this.$emit('cancel')
283
313
  }
284
314
  })
285
315
  },
@@ -297,8 +327,6 @@
297
327
  expHeight = this.expHeight || height;
298
328
 
299
329
  // #ifdef H5
300
- // x *= this.pixelRatio;
301
- // y *= this.pixelRatio;
302
330
  expWidth = width;
303
331
  expHeight = height;
304
332
  // #endif
@@ -346,7 +374,7 @@
346
374
  },
347
375
  fail: () => {
348
376
  uni.showToast({
349
- title: "error0",
377
+ title: "导出图片失败",
350
378
  duration: 2000,
351
379
  })
352
380
  }
@@ -361,9 +389,9 @@
361
389
  this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
362
390
  // #endif
363
391
  },
364
- fail: (res) => {
392
+ fail: () => {
365
393
  uni.showToast({
366
- title: "error1",
394
+ title: "导出图片失败",
367
395
  duration: 2000,
368
396
  })
369
397
  },
@@ -373,16 +401,13 @@
373
401
  }
374
402
  });
375
403
  },
376
- // 用户点击"预览"模式下的"确认"按钮时被调用,用于将预览的裁剪结果上传
404
+ // 预览模式下点击"确认":将预览画布的裁剪结果导出
377
405
  async prvUpload() {
378
406
  if (this.fPrvUploading) return;
379
407
  this.fPrvUploading = true;
380
408
  setTimeout(() => { this.fPrvUploading = false; }, 1000)
381
409
 
382
- let style = this.selStyle,
383
- destWidth = parseInt(style.width),
384
- destHeight = parseInt(style.height),
385
- prvX = this.prvX,
410
+ let prvX = this.prvX,
386
411
  prvY = this.prvY,
387
412
  prvWidth = this.prvWidth,
388
413
  prvHeight = this.prvHeight,
@@ -390,8 +415,6 @@
390
415
  expHeight = this.expHeight || prvHeight;
391
416
 
392
417
  // #ifdef H5
393
- // prvX *= this.pixelRatio;
394
- // prvY *= this.pixelRatio;
395
418
  expWidth = prvWidth;
396
419
  expHeight = prvHeight;
397
420
  // #endif
@@ -440,7 +463,7 @@
440
463
  },
441
464
  fail: () => {
442
465
  uni.showToast({
443
- title: "error0",
466
+ title: "导出图片失败",
444
467
  duration: 2000,
445
468
  })
446
469
  }
@@ -457,7 +480,7 @@
457
480
  },
458
481
  fail: () => {
459
482
  uni.showToast({
460
- title: "error_prv",
483
+ title: "导出图片失败",
461
484
  duration: 2000,
462
485
  })
463
486
  },
@@ -476,7 +499,6 @@
476
499
  imgRadio = imgWidth / imgHeight,
477
500
  useWidth = allWidth - 40,
478
501
  useHeight = allHeight - tabHeight - 80,
479
- pixelRatio = this.pixelRatio,
480
502
  selWidth = parseInt(this.selStyle.width),
481
503
  selHeight = parseInt(this.selStyle.height);
482
504
 
@@ -492,13 +514,8 @@
492
514
  case 'longSel': if (selWidth > selHeight) this.fixWidth = 1; else this.fixHeight = 1; break;
493
515
  case 'shortSel': if (selWidth > selHeight) this.fixHeight = 1; else this.fixWidth = 1; break;
494
516
  }
495
- // lck 用于控制裁剪框的宽度和高度锁定行为
496
- // 'x': 锁定宽度,不允许水平方向调整
497
- // 'y': 锁定高度,不允许垂直方向调整
498
- // 'long': 根据图片长边锁定,如果图片横向较长则锁定宽度,否则锁定高度
499
- // 'short': 根据图片短边锁定,如果图片横向较长则锁定高度,否则锁定宽度
500
- // 'longSel': 根据选择框的长边锁定,如果选择框宽度大于高度则锁定宽度,否则锁定高度
501
- // 'shortSel': 根据选择框的短边锁定,如果选择框宽度大于高度则锁定高度,否则锁定宽度
517
+ // lck 锁定裁剪框的宽/高调整方向:
518
+ // 'x' 锁宽 / 'y' 锁高 / 'long'/'short' 按图片长短边 / 'longSel'/'shortSel' 按选择框长短边
502
519
  switch (this.lck) {
503
520
  case 'x': this.lckWidth = 1; break;
504
521
  case 'y': this.lckHeight = 1; break;
@@ -557,8 +574,6 @@
557
574
  top = parseInt(style.top),
558
575
  width = parseInt(style.width),
559
576
  height = parseInt(style.height),
560
- canvas = this.canvas,
561
- canvasOper = this.canvasOper,
562
577
  ctxCanvas = this.ctxCanvas,
563
578
  ctxCanvasOper = this.ctxCanvasOper;
564
579
 
@@ -577,21 +592,17 @@
577
592
  ctxCanvasOper.moveTo(left + 20, top + height); ctxCanvasOper.lineTo(left, top + height); ctxCanvasOper.lineTo(left, top + height - 20);
578
593
  ctxCanvasOper.moveTo(left + width - 20, top + height); ctxCanvasOper.lineTo(left + width, top + height); ctxCanvasOper.lineTo(left + width, top + height - 20);
579
594
 
580
- // 绘制控制点(四个角)
595
+ // 四角控制点(canChangeSize 时可拖拽调整裁剪框)
581
596
  const controlPointSize = 10;
582
597
  ctxCanvasOper.setFillStyle('white');
583
598
  ctxCanvasOper.setStrokeStyle('grey');
584
599
  ctxCanvasOper.setLineWidth(1);
585
- // 左上角
586
600
  ctxCanvasOper.fillRect(left - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
587
601
  ctxCanvasOper.strokeRect(left - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
588
- // 右上角
589
602
  ctxCanvasOper.fillRect(left + width - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
590
603
  ctxCanvasOper.strokeRect(left + width - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
591
- // 左下角
592
604
  ctxCanvasOper.fillRect(left - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
593
605
  ctxCanvasOper.strokeRect(left - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
594
- // 右下角
595
606
  ctxCanvasOper.fillRect(left + width - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
596
607
  ctxCanvasOper.strokeRect(left + width - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
597
608
 
@@ -640,8 +651,6 @@
640
651
  this.target = null;
641
652
  },
642
653
  close() {
643
- console.log('up-cropper close');
644
-
645
654
  this.styleDisplay = 'none';
646
655
  this.styleTop = '-10000px';
647
656
  this.hasSel = false;
@@ -660,7 +669,6 @@
660
669
  height = parseInt(style.height);
661
670
 
662
671
  uni.showLoading({ mask: true });
663
- // console.log('size', x, y, width, height)
664
672
  await this.initCanvasRefs();
665
673
  this.ctxCanvas.toTempFilePath({
666
674
  x: x,
@@ -670,7 +678,6 @@
670
678
  fileType: 'png',
671
679
  quality: this.qlty,
672
680
  success: async (r) => {
673
- // console.log(r)
674
681
  this.prvImgTmp = r = r.tempFilePath;
675
682
 
676
683
  let ctxCanvasPrv = this.ctxCanvasPrv,
@@ -702,7 +709,7 @@
702
709
  await ctxCanvasPrv.drawImage(r, prvX, prvY, prvWidth, prvHeight);
703
710
  ctxCanvasPrv.draw(false, () => {
704
711
  // #ifdef H5
705
- this.btop(this.prvImgTmp).then((r) => {
712
+ this.btop(this.prvImgTmp).then(() => {
706
713
  this.showOper = false;
707
714
  this.prvTop = this.drawTop + 'px';
708
715
  })
@@ -717,7 +724,7 @@
717
724
  },
718
725
  fail: () => {
719
726
  uni.showToast({
720
- title: "error2",
727
+ title: "生成预览失败",
721
728
  duration: 2000,
722
729
  })
723
730
  },
@@ -728,7 +735,6 @@
728
735
  },
729
736
  chooseImage(index = undefined, params = undefined, data = undefined) {
730
737
  if (params) {
731
- console.log(params)
732
738
  let areaWidth = params.areaWidth || this.areaWidth,
733
739
  areaHeight = params.areaHeight || this.areaHeight,
734
740
  expWidth = params.exportWidth || this.exportWidth,
@@ -742,14 +748,12 @@
742
748
  stretch = params.stretch,
743
749
  inner = params.inner,
744
750
  lock = params.lock;
745
- console.log('areaWidth', this.areaWidth)
746
751
 
747
752
  expWidth && (this.expWidth = expWidth.indexOf('rpx') >= 0 ? parseInt(expWidth) * this.pxRatio : parseInt(expWidth));
748
753
  expHeight && (this.expHeight = expHeight.indexOf('rpx') >= 0 ? parseInt(expHeight) * this.pxRatio : parseInt(expHeight));
749
754
  this.isin = inner === true ? 1 : 0;
750
755
  this.letRotate = (canRotate === false || this.isin) ? 0 : 1;
751
756
  this.letScale = canScale === false ? 0 : 1;
752
- // 设置是否允许调整裁剪框大小
753
757
  this.letChangeSize = canChangeSize || false;
754
758
  this.qlty = parseInt(quality) || 0.9;
755
759
  this.mnScale = minScale || 0.3;
@@ -771,7 +775,6 @@
771
775
  this.selStyle.height = areaHeight + 'px';
772
776
  this.selStyle.top = (this.windowHeight - areaHeight - tabHeight) / 2 + 'px';
773
777
  this.selStyle.left = (this.windowWidth - areaWidth) / 2 + 'px';
774
- // console.log(this.selStyle);
775
778
  this.hasSel = true;
776
779
  }
777
780
  }
@@ -788,10 +791,8 @@
788
791
  }
789
792
  // #endif
790
793
 
791
- // if(this.letRotate) {
792
794
  this.rotateDeg += 90 - this.rotateDeg % 90;
793
795
  this.drawImage();
794
- // }
795
796
  },
796
797
  start(e) {
797
798
  let touches = e.touches,
@@ -805,34 +806,30 @@
805
806
  let x = touch1.x - touch0.x,
806
807
  y = touch1.y - touch0.y;
807
808
  this.fgDistance = Math.sqrt(x * x + y * y);
808
- } else {
809
- // 只有在允许调整大小时才检查控制点
810
- if (this.letChangeSize) {
811
- // 检查是否点击在控制点上
812
- const controlPointSize = 20;
813
- const x = touch0.x;
814
- const y = touch0.y;
815
- const style = this.selStyle;
816
- const left = parseInt(style.left);
817
- const top = parseInt(style.top);
818
- const width = parseInt(style.width);
819
- const height = parseInt(style.height);
820
-
821
- // 检查四个控制点
822
- if (Math.abs(x - left) < controlPointSize && Math.abs(y - top) < controlPointSize) {
823
- this.resizeHandle = 'top-left';
824
- } else if (Math.abs(x - (left + width)) < controlPointSize && Math.abs(y - top) < controlPointSize) {
825
- this.resizeHandle = 'top-right';
826
- } else if (Math.abs(x - left) < controlPointSize && Math.abs(y - (top + height)) < controlPointSize) {
827
- this.resizeHandle = 'bottom-left';
828
- } else if (Math.abs(x - (left + width)) < controlPointSize && Math.abs(y - (top + height)) < controlPointSize) {
829
- this.resizeHandle = 'bottom-right';
830
- } else {
831
- this.resizeHandle = null;
832
- }
809
+ } else if (this.letChangeSize) {
810
+ // 单指落在四角控制点附近时进入裁剪框调整模式
811
+ const controlPointSize = 20;
812
+ const x = touch0.x;
813
+ const y = touch0.y;
814
+ const style = this.selStyle;
815
+ const left = parseInt(style.left);
816
+ const top = parseInt(style.top);
817
+ const width = parseInt(style.width);
818
+ const height = parseInt(style.height);
819
+
820
+ if (Math.abs(x - left) < controlPointSize && Math.abs(y - top) < controlPointSize) {
821
+ this.resizeHandle = 'top-left';
822
+ } else if (Math.abs(x - (left + width)) < controlPointSize && Math.abs(y - top) < controlPointSize) {
823
+ this.resizeHandle = 'top-right';
824
+ } else if (Math.abs(x - left) < controlPointSize && Math.abs(y - (top + height)) < controlPointSize) {
825
+ this.resizeHandle = 'bottom-left';
826
+ } else if (Math.abs(x - (left + width)) < controlPointSize && Math.abs(y - (top + height)) < controlPointSize) {
827
+ this.resizeHandle = 'bottom-right';
833
828
  } else {
834
829
  this.resizeHandle = null;
835
830
  }
831
+ } else {
832
+ this.resizeHandle = null;
836
833
  }
837
834
  },
838
835
  move(e) {
@@ -881,8 +878,8 @@
881
878
 
882
879
  this.drawImage();
883
880
  } else if (this.touch0) {
884
- // 只有在允许调整大小时才处理裁剪框大小调整
885
881
  if (this.resizeHandle && this.letChangeSize) {
882
+ // 拖拽控制点调整裁剪框
886
883
  const style = { ...this.selStyle };
887
884
  const left = parseInt(style.left);
888
885
  const top = parseInt(style.top);
@@ -920,7 +917,6 @@
920
917
  let nw = parseInt(style.width);
921
918
  let nh = parseInt(style.height);
922
919
 
923
- // 屏幕边界
924
920
  if (nl < 0) nl = 0;
925
921
  if (nt < 0) nt = 0;
926
922
  if (nl + nw > this.windowWidth) nw = this.windowWidth - nl;
@@ -947,11 +943,10 @@
947
943
  style.width = nw + 'px';
948
944
  style.height = nh + 'px';
949
945
  this.selStyle = style;
950
- // 重新绘制操作层
951
946
  this.drawInit();
952
947
  }
953
948
  } else {
954
- // 原有的移动图片逻辑
949
+ // 单指拖动图片
955
950
  let x = touch0.x - this.touch0.x,
956
951
  y = touch0.y - this.touch0.y,
957
952
  beX = this.posWidth + x,
@@ -997,14 +992,13 @@
997
992
  },
998
993
  end(e) {
999
994
  let touches = e.touches,
1000
- touch0 = touches && touches[0],
1001
- touch1 = touches && touches[1];
995
+ touch0 = touches && touches[0];
1002
996
  if (touch0) {
1003
997
  this.touch0 = touch0;
1004
998
  } else {
1005
999
  this.touch0 = null;
1006
1000
  this.touch1 = null;
1007
- this.resizeHandle = null; // 重置调整手柄
1001
+ this.resizeHandle = null;
1008
1002
  }
1009
1003
  },
1010
1004
  async getImgData() {
@@ -1034,6 +1028,7 @@
1034
1028
  });
1035
1029
  });
1036
1030
  },
1031
+ // 预览模式饱和度滑杆:HSL 调整后 putImageData 回写预览画布
1037
1032
  async colorChange(e) {
1038
1033
  let tm_now = Date.now();
1039
1034
  if (tm_now - this.prvTm < 100) return;
@@ -1042,9 +1037,9 @@
1042
1037
  uni.showLoading({ mask: true });
1043
1038
 
1044
1039
  if (!this.prvImgData) {
1045
- if (!(this.prvImgData = await this.getImgData().catch((res) => {
1040
+ if (!(this.prvImgData = await this.getImgData().catch(() => {
1046
1041
  uni.showToast({
1047
- title: "error_read",
1042
+ title: "读取图片失败",
1048
1043
  duration: 2000,
1049
1044
  })
1050
1045
  }))) return;
@@ -1054,7 +1049,7 @@
1054
1049
  let data = this.prvImgData,
1055
1050
  target = this.target,
1056
1051
  i = e.detail.value,
1057
- r, g, b, a, h, s, l, d, p, q, t, min, max, hK, tR, tG, tB;
1052
+ r, g, b, a, h, s, l, d, p, q, min, max, hK, tR, tG, tB;
1058
1053
 
1059
1054
  if (i === 0) {
1060
1055
  target = data;
@@ -1163,7 +1158,7 @@
1163
1158
  data: target,
1164
1159
  fail() {
1165
1160
  uni.showToast({
1166
- title: 'error_put',
1161
+ title: "导出图片失败",
1167
1162
  duration: 2000
1168
1163
  })
1169
1164
  },
@@ -1172,8 +1167,9 @@
1172
1167
  }
1173
1168
  });
1174
1169
  },
1170
+ // H5 专用:base64 转 blob URL,规避部分浏览器 canvas 跨域限制
1175
1171
  btop(base64) {
1176
- return new Promise(function (resolve, reject) {
1172
+ return new Promise(function (resolve) {
1177
1173
  var arr = base64.split(','),
1178
1174
  mime = arr[0].match(/:(.*?);/)[1],
1179
1175
  bstr = atob(arr[1]),
@@ -1190,36 +1186,32 @@
1190
1186
  </script>
1191
1187
 
1192
1188
  <style lang="scss" scoped>
1189
+ @import '../../scss/variables';
1190
+
1193
1191
  .dd-cropper {
1194
1192
  .my-canvas {
1195
1193
  display: flex;
1196
1194
  position: fixed !important;
1197
- background: #000000;
1195
+ background: $dd-color-black;
1198
1196
  left: 0;
1199
- z-index: 100000;
1197
+ z-index: $dd-z-index-cropper;
1200
1198
  width: 100%;
1201
1199
  }
1202
1200
 
1203
- .my-avatar {
1204
- width: 150rpx;
1205
- height: 150rpx;
1206
- border-radius: 100%;
1207
- }
1208
-
1209
1201
  .oper-canvas {
1210
1202
  display: flex;
1211
1203
  position: fixed !important;
1212
1204
  left: 0;
1213
- z-index: 100001;
1205
+ z-index: $dd-z-index-cropper + 1;
1214
1206
  width: 100%;
1215
1207
  }
1216
1208
 
1217
1209
  .prv-canvas {
1218
1210
  display: flex;
1219
1211
  position: fixed !important;
1220
- background: #000000;
1212
+ background: $dd-color-black;
1221
1213
  left: 0;
1222
- z-index: 200000;
1214
+ z-index: $dd-z-index-cropper * 2;
1223
1215
  width: 100%;
1224
1216
  }
1225
1217
 
@@ -1227,12 +1219,12 @@
1227
1219
  height: 50px;
1228
1220
  position: fixed !important;
1229
1221
  box-sizing: border-box;
1230
- border: 1px solid #F1F1F1;
1231
- background: #ffffff;
1222
+ border: 1px solid $dd-neutral-50;
1223
+ background: $dd-color-white;
1232
1224
  width: 100%;
1233
1225
  left: 0;
1234
1226
  bottom: 0;
1235
- z-index: 100009;
1227
+ z-index: $dd-z-index-cropper + 9;
1236
1228
  flex-direction: row;
1237
1229
  }
1238
1230
 
@@ -1263,14 +1255,14 @@
1263
1255
  display: flex;
1264
1256
  align-items: center;
1265
1257
  justify-content: center;
1266
- font-size: 16px;
1267
- color: #333;
1268
- border: 1px solid #f1f1f1;
1258
+ font-size: $dd-font-size-h4;
1259
+ color: $dd-neutral-700;
1260
+ border: 1px solid $dd-neutral-50;
1269
1261
  border-radius: 6%;
1270
1262
  }
1271
1263
 
1272
1264
  .hover {
1273
- background: #f1f1f1;
1265
+ background: $dd-neutral-50;
1274
1266
  border-radius: 6%;
1275
1267
  }
1276
1268
 
@@ -1284,9 +1276,9 @@
1284
1276
  display: flex;
1285
1277
  align-items: center;
1286
1278
  justify-content: center;
1287
- font-size: 16px;
1288
- color: #333;
1289
- border: 1px solid #f1f1f1;
1279
+ font-size: $dd-font-size-h4;
1280
+ color: $dd-neutral-700;
1281
+ border: 1px solid $dd-neutral-50;
1290
1282
  border-radius: 6%;
1291
1283
  }
1292
1284