uview-plus 3.4.84 → 3.4.86
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.
package/changelog.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<template
|
|
2
|
-
<view>
|
|
3
|
-
<image :src="imgSrc.imgSrc" @click="select" :style="[ imgStyle ]" class="my-avatar"></image>
|
|
4
|
-
<canvas canvas-id="avatar-canvas" id="avatar-canvas" class="my-canvas"
|
|
1
|
+
<template>
|
|
2
|
+
<view class="u-cropper">
|
|
3
|
+
<!-- <image :src="imgSrc.imgSrc" @click="select" :style="[ imgStyle ]" class="my-avatar"></image> -->
|
|
4
|
+
<canvas :canvas-id="'avatar-canvas-' + instanceId" :id="'avatar-canvas-' + instanceId" class="my-canvas"
|
|
5
5
|
:style="{top: styleTop, height: cvsStyleHeight}" disable-scroll="false"></canvas>
|
|
6
|
-
<canvas canvas-id="oper-canvas" id="oper-canvas" class="oper-canvas"
|
|
6
|
+
<canvas :canvas-id="'oper-canvas-' + instanceId" :id="'oper-canvas-' + instanceId" class="oper-canvas"
|
|
7
7
|
:style="{top: styleTop, height: cvsStyleHeight}"
|
|
8
8
|
disable-scroll="false" @touchstart="start" @touchmove="move" @touchend="end"></canvas>
|
|
9
|
-
<canvas canvas-id="prv-canvas" id="prv-canvas" class="prv-canvas"disable-scroll="false"
|
|
9
|
+
<canvas :canvas-id="'prv-canvas-' + instanceId" :id="'prv-canvas-' + instanceId" class="prv-canvas"disable-scroll="false"
|
|
10
10
|
@touchstart="hideImg":style="{ height: cvsStyleHeight, top: prvTop }"></canvas>
|
|
11
11
|
<view class="oper-wrapper" :style="{display: styleDisplay}">
|
|
12
12
|
<view class="oper">
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
</view>
|
|
25
25
|
</view>
|
|
26
26
|
</view>
|
|
27
|
+
<view @click="chooseImage(0, {})" v-if="styleDisplay == 'none'">
|
|
28
|
+
<slot>
|
|
29
|
+
|
|
30
|
+
</slot>
|
|
31
|
+
</view>
|
|
27
32
|
</view>
|
|
28
33
|
</template>
|
|
29
34
|
|
|
@@ -33,6 +38,8 @@
|
|
|
33
38
|
name: "u-cropper",
|
|
34
39
|
data() {
|
|
35
40
|
return {
|
|
41
|
+
// 添加实例ID用于区分不同实例
|
|
42
|
+
instanceId: Date.now() + '-' + Math.random().toString(36).substr(2, 9),
|
|
36
43
|
cvsStyleHeight: '0px',
|
|
37
44
|
styleDisplay: 'none',
|
|
38
45
|
styleTop: '-10000px',
|
|
@@ -45,6 +52,17 @@
|
|
|
45
52
|
},
|
|
46
53
|
btnWidth: '19%',
|
|
47
54
|
btnDsp: 'flex',
|
|
55
|
+
// 裁剪区域宽度,用于设置选择区域的宽度
|
|
56
|
+
arWidth: '',
|
|
57
|
+
// 裁剪区域高度,用于设置选择区域的高度
|
|
58
|
+
arHeight: '',
|
|
59
|
+
// 导出图片宽度,用于设置最终导出图片的宽度
|
|
60
|
+
expWidth: '',
|
|
61
|
+
// 导出图片高度,用于设置最终导出图片的高度
|
|
62
|
+
expHeight: '',
|
|
63
|
+
// 是否允许调整裁剪框大小
|
|
64
|
+
letChangeSize: false,
|
|
65
|
+
|
|
48
66
|
};
|
|
49
67
|
},
|
|
50
68
|
watch: {
|
|
@@ -54,16 +72,6 @@
|
|
|
54
72
|
},
|
|
55
73
|
emits: ['avtinit', 'confirm'],
|
|
56
74
|
props:{
|
|
57
|
-
// 裁剪区域宽度,用于设置选择区域的宽度
|
|
58
|
-
areaWidth: '',
|
|
59
|
-
// 裁剪区域高度,用于设置选择区域的高度
|
|
60
|
-
areaHeight: '',
|
|
61
|
-
// 导出图片宽度,用于设置最终导出图片的宽度
|
|
62
|
-
exportWidth: '',
|
|
63
|
-
// 导出图片高度,用于设置最终导出图片的高度
|
|
64
|
-
exportHeight: '',
|
|
65
|
-
// 是否允许调整裁剪框大小
|
|
66
|
-
canChangeSize: false,
|
|
67
75
|
minScale: '',
|
|
68
76
|
maxScale: '',
|
|
69
77
|
canScale: true,
|
|
@@ -76,17 +84,25 @@
|
|
|
76
84
|
inner: false,
|
|
77
85
|
quality: '',
|
|
78
86
|
index: '',
|
|
87
|
+
canChangeSize: false,
|
|
88
|
+
areaWidth: '300rpx',
|
|
89
|
+
// 裁剪区域高度,用于设置选择区域的高度
|
|
90
|
+
areaHeight: '300rpx',
|
|
91
|
+
// 导出图片宽度,用于设置最终导出图片的宽度
|
|
92
|
+
exportWidth: '260rpx',
|
|
93
|
+
// 导出图片高度,用于设置最终导出图片的高度
|
|
94
|
+
exportHeight: '260rpx',
|
|
79
95
|
},
|
|
80
96
|
created() {
|
|
81
|
-
this.ctxCanvas = uni.createCanvasContext('avatar-canvas', this);
|
|
82
|
-
this.ctxCanvasOper = uni.createCanvasContext('oper-canvas', this);
|
|
83
|
-
this.ctxCanvasPrv = uni.createCanvasContext('prv-canvas', this);
|
|
97
|
+
this.ctxCanvas = uni.createCanvasContext('avatar-canvas-' + this.instanceId, this);
|
|
98
|
+
this.ctxCanvasOper = uni.createCanvasContext('oper-canvas-' + this.instanceId, this);
|
|
99
|
+
this.ctxCanvasPrv = uni.createCanvasContext('prv-canvas-' + this.instanceId, this);
|
|
84
100
|
this.qlty = parseInt(this.quality) || 0.9;
|
|
85
|
-
this.imgSrc.imgSrc = this.
|
|
101
|
+
this.imgSrc.imgSrc = this.imageSrc;
|
|
86
102
|
this.letRotate = (this.canRotate === false || this.inner === true) ? 0 : 1;
|
|
87
103
|
this.letScale = this.canScale === false ? 0 : 1;
|
|
88
104
|
// 是否允许调整裁剪框大小,false表示不允许,其他值表示允许
|
|
89
|
-
this.letChangeSize = this.canChangeSize
|
|
105
|
+
this.letChangeSize = this.canChangeSize;
|
|
90
106
|
this.isin = this.inner === true ? 1 : 0;
|
|
91
107
|
this.indx = this.index || undefined;
|
|
92
108
|
this.mnScale = this.minScale || 0.3;
|
|
@@ -162,8 +178,8 @@
|
|
|
162
178
|
this.imgStyle = obj;
|
|
163
179
|
}
|
|
164
180
|
|
|
165
|
-
this.
|
|
166
|
-
this.
|
|
181
|
+
this.expWidth && (this.expWidth = this.expWidth.indexOf('rpx') >= 0 ? parseInt(this.expWidth)*this.pxRatio : parseInt(this.expWidth));
|
|
182
|
+
this.expHeight && (this.expHeight = this.expHeight.indexOf('rpx') >= 0 ? parseInt(this.expHeight)*this.pxRatio : parseInt(this.expHeight));
|
|
167
183
|
|
|
168
184
|
if(this.styleDisplay === 'flex') {
|
|
169
185
|
this.drawInit(true);
|
|
@@ -190,9 +206,9 @@
|
|
|
190
206
|
this.path = path;
|
|
191
207
|
if( !this.hasSel ) {
|
|
192
208
|
let style = this.selStyle || {};
|
|
193
|
-
if( this.
|
|
194
|
-
let areaWidth = this.
|
|
195
|
-
areaHeight = this.
|
|
209
|
+
if( this.arWidth && this.arHeight ) {
|
|
210
|
+
let areaWidth = this.arWidth.indexOf('rpx') >= 0 ? parseInt(this.arWidth) * this.pxRatio: parseInt(this.arWidth),
|
|
211
|
+
areaHeight = this.arHeight.indexOf('rpx') >= 0 ? parseInt(this.arHeight) * this.pxRatio: parseInt(this.arHeight);
|
|
196
212
|
style.width = areaWidth + 'px';
|
|
197
213
|
style.height = areaHeight + 'px';
|
|
198
214
|
style.top = (this.windowHeight - areaHeight - tabHeight)/2 + 'px';
|
|
@@ -240,12 +256,12 @@
|
|
|
240
256
|
y = parseInt(style.top),
|
|
241
257
|
width = parseInt(style.width),
|
|
242
258
|
height = parseInt(style.height),
|
|
243
|
-
expWidth = this.
|
|
244
|
-
expHeight = this.
|
|
259
|
+
expWidth = this.expWidth || width,
|
|
260
|
+
expHeight = this.expHeight || height;
|
|
245
261
|
|
|
246
262
|
// #ifdef H5
|
|
247
|
-
x *= this.pixelRatio;
|
|
248
|
-
y *= this.pixelRatio;
|
|
263
|
+
// x *= this.pixelRatio;
|
|
264
|
+
// y *= this.pixelRatio;
|
|
249
265
|
expWidth = width;
|
|
250
266
|
expHeight = height;
|
|
251
267
|
// #endif
|
|
@@ -262,18 +278,17 @@
|
|
|
262
278
|
height: height,
|
|
263
279
|
destWidth: expWidth,
|
|
264
280
|
destHeight: expHeight,
|
|
265
|
-
canvasId: 'avatar-canvas',
|
|
281
|
+
canvasId: 'avatar-canvas-' + this.instanceId,
|
|
266
282
|
fileType: 'png',
|
|
267
283
|
quality: this.qlty,
|
|
268
284
|
success: (r)=>{
|
|
269
285
|
r = r.tempFilePath;
|
|
270
286
|
// #ifdef H5
|
|
271
|
-
alert()
|
|
272
287
|
this.btop(r).then((r)=> {
|
|
273
|
-
if(this.
|
|
288
|
+
if(this.expWidth && this.expHeight) {
|
|
274
289
|
let ctxCanvas = this.ctxCanvas;
|
|
275
|
-
expWidth = this.
|
|
276
|
-
expHeight = this.
|
|
290
|
+
expWidth = this.expWidth,
|
|
291
|
+
expHeight = this.expHeight;
|
|
277
292
|
|
|
278
293
|
ctxCanvas.drawImage(r, 0, 0, expWidth, expHeight);
|
|
279
294
|
ctxCanvas.draw(false,()=>{
|
|
@@ -284,7 +299,7 @@
|
|
|
284
299
|
height: expHeight,
|
|
285
300
|
destWidth: expWidth,
|
|
286
301
|
destHeight: expHeight,
|
|
287
|
-
canvasId: 'avatar-canvas',
|
|
302
|
+
canvasId: 'avatar-canvas-' + this.instanceId,
|
|
288
303
|
fileType: 'png',
|
|
289
304
|
quality: this.qlty,
|
|
290
305
|
success: (r)=>{
|
|
@@ -335,12 +350,12 @@
|
|
|
335
350
|
prvY = this.prvY,
|
|
336
351
|
prvWidth = this.prvWidth,
|
|
337
352
|
prvHeight = this.prvHeight,
|
|
338
|
-
expWidth = this.
|
|
339
|
-
expHeight = this.
|
|
353
|
+
expWidth = this.expWidth || prvWidth,
|
|
354
|
+
expHeight = this.expHeight || prvHeight;
|
|
340
355
|
|
|
341
356
|
// #ifdef H5
|
|
342
|
-
prvX *= this.pixelRatio;
|
|
343
|
-
prvY *= this.pixelRatio;
|
|
357
|
+
// prvX *= this.pixelRatio;
|
|
358
|
+
// prvY *= this.pixelRatio;
|
|
344
359
|
expWidth = prvWidth;
|
|
345
360
|
expHeight = prvHeight;
|
|
346
361
|
// #endif
|
|
@@ -358,17 +373,17 @@
|
|
|
358
373
|
height: prvHeight,
|
|
359
374
|
destWidth: expWidth,
|
|
360
375
|
destHeight: expHeight,
|
|
361
|
-
canvasId: 'prv-canvas',
|
|
376
|
+
canvasId: 'prv-canvas-' + this.instanceId,
|
|
362
377
|
fileType: 'png',
|
|
363
378
|
quality: this.qlty,
|
|
364
379
|
success: (r)=>{
|
|
365
380
|
r = r.tempFilePath;
|
|
366
381
|
// #ifdef H5
|
|
367
382
|
this.btop(r).then((r)=> {
|
|
368
|
-
if(this.
|
|
383
|
+
if(this.expWidth && this.expHeight) {
|
|
369
384
|
let ctxCanvas = this.ctxCanvas;
|
|
370
|
-
expWidth = this.
|
|
371
|
-
expHeight = this.
|
|
385
|
+
expWidth = this.expWidth,
|
|
386
|
+
expHeight = this.expHeight;
|
|
372
387
|
|
|
373
388
|
ctxCanvas.drawImage(r, 0, 0, expWidth, expHeight);
|
|
374
389
|
ctxCanvas.draw(false, ()=>{
|
|
@@ -379,7 +394,7 @@
|
|
|
379
394
|
height: expHeight,
|
|
380
395
|
destWidth: expWidth,
|
|
381
396
|
destHeight: expHeight,
|
|
382
|
-
canvasId: 'avatar-canvas',
|
|
397
|
+
canvasId: 'avatar-canvas-' + this.instanceId,
|
|
383
398
|
fileType: 'png',
|
|
384
399
|
quality: this.qlty,
|
|
385
400
|
success: (r)=>{
|
|
@@ -592,30 +607,24 @@
|
|
|
592
607
|
if(this.fPreviewing) return;
|
|
593
608
|
this.fPreviewing = true;
|
|
594
609
|
setTimeout(()=>{ this.fPreviewing = false; }, 1000);
|
|
595
|
-
|
|
596
610
|
let style = this.selStyle,
|
|
597
611
|
x = parseInt(style.left),
|
|
598
612
|
y = parseInt(style.top),
|
|
599
613
|
width = parseInt(style.width),
|
|
600
614
|
height = parseInt(style.height);
|
|
601
615
|
|
|
602
|
-
// #ifdef H5
|
|
603
|
-
x *= this.pixelRatio;
|
|
604
|
-
y *= this.pixelRatio;
|
|
605
|
-
// #endif
|
|
606
|
-
|
|
607
616
|
uni.showLoading({ mask: true });
|
|
608
|
-
console.log('size', x, y, width, height)
|
|
617
|
+
// console.log('size', x, y, width, height)
|
|
609
618
|
uni.canvasToTempFilePath({
|
|
610
619
|
x: x,
|
|
611
620
|
y: y,
|
|
612
621
|
width: width,
|
|
613
622
|
height: height,
|
|
614
|
-
canvasId: 'avatar-canvas',
|
|
623
|
+
canvasId: 'avatar-canvas-' + this.instanceId,
|
|
615
624
|
fileType: 'png',
|
|
616
625
|
quality: this.qlty,
|
|
617
626
|
success: (r)=>{
|
|
618
|
-
console.log(r)
|
|
627
|
+
// console.log(r)
|
|
619
628
|
this.prvImgTmp = r = r.tempFilePath;
|
|
620
629
|
|
|
621
630
|
let ctxCanvasPrv = this.ctxCanvasPrv,
|
|
@@ -635,7 +644,7 @@
|
|
|
635
644
|
prvWidth *= radio;
|
|
636
645
|
prvHeight = useHeight;
|
|
637
646
|
}
|
|
638
|
-
|
|
647
|
+
ctxCanvasPrv.setFillStyle('black');
|
|
639
648
|
ctxCanvasPrv.fillRect(0, 0, prvX, prvY);
|
|
640
649
|
ctxCanvasPrv.fillRect(x, y, width, height);
|
|
641
650
|
this.prvX = prvX = (prvX-prvWidth)/2;
|
|
@@ -643,7 +652,7 @@
|
|
|
643
652
|
this.prvWidth = prvWidth;
|
|
644
653
|
this.prvHeight = prvHeight;
|
|
645
654
|
ctxCanvasPrv.drawImage(r, prvX, prvY, prvWidth, prvHeight);
|
|
646
|
-
ctxCanvasPrv.draw(false, () => {
|
|
655
|
+
ctxCanvasPrv.draw(false, () => {
|
|
647
656
|
// #ifdef H5
|
|
648
657
|
this.btop(this.prvImgTmp).then((r)=> {
|
|
649
658
|
this.showOper = false;
|
|
@@ -671,26 +680,28 @@
|
|
|
671
680
|
},
|
|
672
681
|
chooseImage(index=undefined, params=undefined, data=undefined) {
|
|
673
682
|
if(params) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
683
|
+
console.log(params)
|
|
684
|
+
let areaWidth = params.areaWidth || this.areaWidth,
|
|
685
|
+
areaHeight = params.areaHeight || this.areaHeight,
|
|
686
|
+
expWidth = params.exportWidth || this.exportWidth,
|
|
687
|
+
expHeight = params.exportHeight || this.exportHeight,
|
|
678
688
|
quality = params.quality,
|
|
679
689
|
canRotate = params.canRotate,
|
|
680
690
|
canScale = params.canScale,
|
|
681
|
-
canChangeSize = params.canChangeSize
|
|
691
|
+
canChangeSize = params.canChangeSize,
|
|
682
692
|
minScale = params.minScale,
|
|
683
693
|
maxScale = params.maxScale,
|
|
684
694
|
stretch = params.stretch,
|
|
685
695
|
inner = params.inner,
|
|
686
696
|
lock = params.lock;
|
|
697
|
+
console.log('areaWidth', this.areaWidth)
|
|
687
698
|
|
|
688
|
-
expWidth && (this.
|
|
689
|
-
expHeight && (this.
|
|
699
|
+
expWidth && (this.expWidth = expWidth.indexOf('rpx') >= 0 ? parseInt(expWidth)*this.pxRatio : parseInt(expWidth));
|
|
700
|
+
expHeight && (this.expHeight = expHeight.indexOf('rpx') >= 0 ? parseInt(expHeight)*this.pxRatio : parseInt(expHeight));
|
|
690
701
|
this.letRotate = canRotate === false ? 0 : 1;
|
|
691
702
|
this.letScale = canScale === false ? 0 : 1;
|
|
692
703
|
// 设置是否允许调整裁剪框大小
|
|
693
|
-
this.letChangeSize = canChangeSize
|
|
704
|
+
this.letChangeSize = canChangeSize || false;
|
|
694
705
|
this.qlty = parseInt(quality) || 0.9;
|
|
695
706
|
this.mnScale = minScale || 0.3;
|
|
696
707
|
this.mxScale = maxScale || 4;
|
|
@@ -712,6 +723,7 @@
|
|
|
712
723
|
this.selStyle.height = areaHeight + 'px';
|
|
713
724
|
this.selStyle.top = (this.windowHeight - areaHeight - tabHeight)/2 + 'px';
|
|
714
725
|
this.selStyle.left = (this.windowWidth - areaWidth)/2 + 'px';
|
|
726
|
+
// console.log(this.selStyle);
|
|
715
727
|
this.hasSel = true;
|
|
716
728
|
}
|
|
717
729
|
}
|
|
@@ -936,7 +948,7 @@
|
|
|
936
948
|
prvHeight *= this.pixelRatio;
|
|
937
949
|
// #endif
|
|
938
950
|
uni.canvasGetImageData({
|
|
939
|
-
canvasId: 'prv-canvas',
|
|
951
|
+
canvasId: 'prv-canvas-' + this.instanceId,
|
|
940
952
|
x: prvX,
|
|
941
953
|
y: prvY,
|
|
942
954
|
width: prvWidth,
|
|
@@ -1070,7 +1082,7 @@
|
|
|
1070
1082
|
prvHeight *= this.pixelRatio;
|
|
1071
1083
|
// #endif
|
|
1072
1084
|
uni.canvasPutImageData({
|
|
1073
|
-
canvasId: 'prv-canvas',
|
|
1085
|
+
canvasId: 'prv-canvas-' + this.instanceId,
|
|
1074
1086
|
x: prvX,
|
|
1075
1087
|
y: prvY,
|
|
1076
1088
|
width: prvWidth,
|
|
@@ -1104,7 +1116,8 @@
|
|
|
1104
1116
|
}
|
|
1105
1117
|
</script>
|
|
1106
1118
|
|
|
1107
|
-
<style>
|
|
1119
|
+
<style lang="scss" scoped>
|
|
1120
|
+
.u-cropper {
|
|
1108
1121
|
.my-canvas{
|
|
1109
1122
|
display: flex;
|
|
1110
1123
|
position: fixed !important;
|
|
@@ -1196,4 +1209,5 @@
|
|
|
1196
1209
|
.my-slider {
|
|
1197
1210
|
flex-grow: 1;
|
|
1198
1211
|
}
|
|
1212
|
+
}
|
|
1199
1213
|
</style>
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="u-signature">
|
|
3
|
+
<view class="u-signature__canvas-wrap">
|
|
4
|
+
<canvas
|
|
5
|
+
class="u-signature__canvas"
|
|
6
|
+
:canvas-id="canvasId"
|
|
7
|
+
:disable-scroll="true"
|
|
8
|
+
@touchstart="touchStart"
|
|
9
|
+
@touchmove="touchMove"
|
|
10
|
+
@touchend="touchEnd"
|
|
11
|
+
:style="{
|
|
12
|
+
width: canvasWidth + 'px',
|
|
13
|
+
height: canvasHeight + 'px',
|
|
14
|
+
background: bgColor
|
|
15
|
+
}"
|
|
16
|
+
></canvas>
|
|
17
|
+
</view>
|
|
18
|
+
|
|
19
|
+
<view v-if="showToolbar" class="u-signature__toolbar">
|
|
20
|
+
<view class="u-signature__toolbar-icons u-flex u-flex-x">
|
|
21
|
+
<view class="u-signature__toolbar-icon" @click="undo">
|
|
22
|
+
<u-icon name="arrow-left" size="22" :color="pathStack.length === 0 ? '#ccc' : '#999'"></u-icon>
|
|
23
|
+
</view>
|
|
24
|
+
<view class="u-signature__toolbar-icon" @click="clear">
|
|
25
|
+
<u-icon name="trash" size="25" color="#999"></u-icon>
|
|
26
|
+
</view>
|
|
27
|
+
<view class="u-signature__toolbar-icon" @click="toggleBrushSettings">
|
|
28
|
+
<u-icon name="edit-pen" size="25" color="#999"></u-icon>
|
|
29
|
+
</view>
|
|
30
|
+
<view class="u-signature__toolbar-icon" @click="toggleColorSettings">
|
|
31
|
+
<u-icon name="grid" size="24" color="#999"></u-icon>
|
|
32
|
+
</view>
|
|
33
|
+
<view class="u-signature__toolbar-icon" @click="exportSignature">
|
|
34
|
+
<u-icon name="checkmark" size="25" :color="isEmpty ? '#ccc' : '#999'"></u-icon>
|
|
35
|
+
</view>
|
|
36
|
+
</view>
|
|
37
|
+
|
|
38
|
+
<!-- 笔画设置 -->
|
|
39
|
+
<view v-if="showBrushSettings" class="u-signature__brush-settings">
|
|
40
|
+
<view class="u-signature__progress">
|
|
41
|
+
<text class="u-signature__progress-label">笔画大小:</text>
|
|
42
|
+
<up-slider
|
|
43
|
+
v-model="lineWidth"
|
|
44
|
+
:min="1"
|
|
45
|
+
:max="20"
|
|
46
|
+
:step="1"
|
|
47
|
+
@show-value="true"
|
|
48
|
+
:value-show="(lineWidth)"
|
|
49
|
+
></up-slider>
|
|
50
|
+
</view>
|
|
51
|
+
</view>
|
|
52
|
+
|
|
53
|
+
<!-- 颜色设置 -->
|
|
54
|
+
<view v-if="showColorSettings" class="u-signature__color-settings">
|
|
55
|
+
<view class="u-signature__color-picker">
|
|
56
|
+
<text class="u-signature__color-label">笔画颜色:</text>
|
|
57
|
+
<view class="u-signature__colors">
|
|
58
|
+
<view
|
|
59
|
+
v-for="(color, index) in presetColors"
|
|
60
|
+
:key="index"
|
|
61
|
+
class="u-signature__color-item"
|
|
62
|
+
:class="{'u-signature__color-item--active': lineColor === color}"
|
|
63
|
+
:style="{ backgroundColor: color }"
|
|
64
|
+
@click="selectColor(color)"
|
|
65
|
+
></view>
|
|
66
|
+
</view>
|
|
67
|
+
</view>
|
|
68
|
+
</view>
|
|
69
|
+
</view>
|
|
70
|
+
</view>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script>
|
|
74
|
+
export default {
|
|
75
|
+
name: 'u-signature',
|
|
76
|
+
props: {
|
|
77
|
+
// 画布宽度
|
|
78
|
+
width: {
|
|
79
|
+
type: [String, Number],
|
|
80
|
+
default: 300
|
|
81
|
+
},
|
|
82
|
+
// 画布高度
|
|
83
|
+
height: {
|
|
84
|
+
type: [String, Number],
|
|
85
|
+
default: 200
|
|
86
|
+
},
|
|
87
|
+
// 背景颜色
|
|
88
|
+
bgColor: {
|
|
89
|
+
type: String,
|
|
90
|
+
default: '#ffffff'
|
|
91
|
+
},
|
|
92
|
+
// 默认笔画颜色
|
|
93
|
+
color: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: '#000000'
|
|
96
|
+
},
|
|
97
|
+
// 默认笔画粗细
|
|
98
|
+
thickness: {
|
|
99
|
+
type: [String, Number],
|
|
100
|
+
default: 3
|
|
101
|
+
},
|
|
102
|
+
// 是否显示工具栏
|
|
103
|
+
showToolbar: {
|
|
104
|
+
type: Boolean,
|
|
105
|
+
default: true
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
data() {
|
|
109
|
+
return {
|
|
110
|
+
canvasId: 'u-signature-' + Math.random().toString(36).substr(2, 9),
|
|
111
|
+
canvasWidth: 300,
|
|
112
|
+
canvasHeight: 200,
|
|
113
|
+
lineColor: '#000000',
|
|
114
|
+
lineWidth: 3,
|
|
115
|
+
isDrawing: false,
|
|
116
|
+
pathStack: [], // 存储绘制路径用于回退
|
|
117
|
+
currentPath: [], // 当前绘制路径
|
|
118
|
+
ctx: null,
|
|
119
|
+
isEmpty: true,
|
|
120
|
+
presetColors: [
|
|
121
|
+
'#000000', // 黑色
|
|
122
|
+
'#ff0000', // 红色
|
|
123
|
+
'#00ff00', // 绿色
|
|
124
|
+
'#0000ff', // 蓝色
|
|
125
|
+
'#ffff00', // 黄色
|
|
126
|
+
'#00ffff', // 青色
|
|
127
|
+
'#ff00ff', // 紫色
|
|
128
|
+
'#ffffff' // 白色
|
|
129
|
+
],
|
|
130
|
+
showBrushSettings: false,
|
|
131
|
+
showColorSettings: false,
|
|
132
|
+
lastPoint: null // 保存上一个点的坐标
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
mounted() {
|
|
136
|
+
this.initCanvas()
|
|
137
|
+
},
|
|
138
|
+
watch: {
|
|
139
|
+
width: {
|
|
140
|
+
handler(newVal) {
|
|
141
|
+
this.canvasWidth = Number(newVal)
|
|
142
|
+
},
|
|
143
|
+
immediate: true
|
|
144
|
+
},
|
|
145
|
+
height: {
|
|
146
|
+
handler(newVal) {
|
|
147
|
+
this.canvasHeight = Number(newVal)
|
|
148
|
+
},
|
|
149
|
+
immediate: true
|
|
150
|
+
},
|
|
151
|
+
color: {
|
|
152
|
+
handler(newVal) {
|
|
153
|
+
this.lineColor = newVal
|
|
154
|
+
},
|
|
155
|
+
immediate: true
|
|
156
|
+
},
|
|
157
|
+
thickness: {
|
|
158
|
+
handler(newVal) {
|
|
159
|
+
this.lineWidth = Number(newVal)
|
|
160
|
+
},
|
|
161
|
+
immediate: true
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
methods: {
|
|
165
|
+
initCanvas() {
|
|
166
|
+
// #ifndef APP-NVUE
|
|
167
|
+
const ctx = uni.createCanvasContext(this.canvasId, this)
|
|
168
|
+
this.ctx = ctx
|
|
169
|
+
this.clearCanvas()
|
|
170
|
+
// #endif
|
|
171
|
+
|
|
172
|
+
// #ifdef APP-NVUE
|
|
173
|
+
// NVUE环境下的处理
|
|
174
|
+
// #endif
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
touchStart(e) {
|
|
178
|
+
if (!this.ctx) return
|
|
179
|
+
|
|
180
|
+
this.isDrawing = true
|
|
181
|
+
this.isEmpty = false
|
|
182
|
+
this.currentPath = []
|
|
183
|
+
|
|
184
|
+
const { x, y } = this.getCanvasPoint(e)
|
|
185
|
+
this.ctx.beginPath()
|
|
186
|
+
this.ctx.moveTo(x, y)
|
|
187
|
+
this.ctx.setLineCap('round')
|
|
188
|
+
this.ctx.setLineJoin('round')
|
|
189
|
+
this.ctx.setStrokeStyle(this.lineColor)
|
|
190
|
+
this.ctx.setLineWidth(this.lineWidth)
|
|
191
|
+
|
|
192
|
+
// 记录起始点
|
|
193
|
+
this.currentPath.push({
|
|
194
|
+
x,
|
|
195
|
+
y,
|
|
196
|
+
type: 'start',
|
|
197
|
+
color: this.lineColor,
|
|
198
|
+
width: this.lineWidth
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// 保存上一个点
|
|
202
|
+
this.lastPoint = { x, y }
|
|
203
|
+
|
|
204
|
+
// 阻止默认事件以提高性能
|
|
205
|
+
e.preventDefault()
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
touchMove(e) {
|
|
209
|
+
if (!this.isDrawing || !this.ctx) return
|
|
210
|
+
|
|
211
|
+
// 阻止默认事件以提高性能
|
|
212
|
+
e.preventDefault()
|
|
213
|
+
|
|
214
|
+
const { x, y } = this.getCanvasPoint(e)
|
|
215
|
+
|
|
216
|
+
// 使用更密集的点采样确保线条连贯性
|
|
217
|
+
if (this.lastPoint) {
|
|
218
|
+
// 计算两点间距离
|
|
219
|
+
const distance = Math.sqrt(Math.pow(x - this.lastPoint.x, 2) + Math.pow(y - this.lastPoint.y, 2))
|
|
220
|
+
// 根据距离确定插值点数量,确保点间距不超过1像素以获得更平滑的线条
|
|
221
|
+
const steps = Math.max(1, Math.floor(distance / 1))
|
|
222
|
+
|
|
223
|
+
// 在两点间插入插值点
|
|
224
|
+
for (let i = 1; i <= steps; i++) {
|
|
225
|
+
const t = i / steps
|
|
226
|
+
const midX = this.lastPoint.x + (x - this.lastPoint.x) * t
|
|
227
|
+
const midY = this.lastPoint.y + (y - this.lastPoint.y) * t
|
|
228
|
+
|
|
229
|
+
this.ctx.lineTo(midX, midY)
|
|
230
|
+
this.ctx.stroke()
|
|
231
|
+
this.currentPath.push({
|
|
232
|
+
x: midX,
|
|
233
|
+
y: midY,
|
|
234
|
+
type: 'move'
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
this.ctx.lineTo(x, y)
|
|
239
|
+
this.ctx.stroke()
|
|
240
|
+
this.currentPath.push({
|
|
241
|
+
x,
|
|
242
|
+
y,
|
|
243
|
+
type: 'move'
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.ctx.draw(true)
|
|
248
|
+
|
|
249
|
+
// 更新上一个点
|
|
250
|
+
this.lastPoint = { x, y }
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
touchEnd(e) {
|
|
254
|
+
if (!this.isDrawing || !this.ctx) return
|
|
255
|
+
|
|
256
|
+
this.isDrawing = false
|
|
257
|
+
this.ctx.closePath()
|
|
258
|
+
this.lastPoint = null
|
|
259
|
+
|
|
260
|
+
// 将当前路径加入栈中用于回退
|
|
261
|
+
if (this.currentPath.length > 0) {
|
|
262
|
+
this.pathStack.push([...this.currentPath])
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
// 获取相对于canvas的坐标点
|
|
267
|
+
getCanvasPoint(e) {
|
|
268
|
+
const touch = e.touches[0]
|
|
269
|
+
const rect = uni.createSelectorQuery().in(this).select('.u-signature__canvas').boundingClientRect()
|
|
270
|
+
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
rect.boundingClientRect(data => {
|
|
273
|
+
const x = touch.x - data.left
|
|
274
|
+
const y = touch.y - data.top
|
|
275
|
+
resolve({ x, y })
|
|
276
|
+
}).exec()
|
|
277
|
+
})
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// 同步获取canvas坐标点(兼容处理)
|
|
281
|
+
getCanvasPoint(e) {
|
|
282
|
+
const touch = e.touches[0]
|
|
283
|
+
const canvas = uni.createSelectorQuery().in(this).select('.u-signature__canvas')
|
|
284
|
+
|
|
285
|
+
// 返回一个包含坐标的对象
|
|
286
|
+
return {
|
|
287
|
+
x: touch.x,
|
|
288
|
+
y: touch.y
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
// 选择颜色
|
|
293
|
+
selectColor(color) {
|
|
294
|
+
this.lineColor = color
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
// 回退操作
|
|
298
|
+
undo() {
|
|
299
|
+
if (this.pathStack.length === 0) return
|
|
300
|
+
|
|
301
|
+
// 弹出最后一个路径
|
|
302
|
+
this.pathStack.pop()
|
|
303
|
+
|
|
304
|
+
// 重新绘制
|
|
305
|
+
this.redraw()
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
// 重新绘制所有路径
|
|
309
|
+
redraw() {
|
|
310
|
+
this.clearCanvas()
|
|
311
|
+
|
|
312
|
+
if (this.pathStack.length === 0) {
|
|
313
|
+
this.isEmpty = true
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this.isEmpty = false
|
|
318
|
+
|
|
319
|
+
// #ifndef APP-NVUE
|
|
320
|
+
this.pathStack.forEach(path => {
|
|
321
|
+
if (path.length === 0) return
|
|
322
|
+
|
|
323
|
+
this.ctx.beginPath()
|
|
324
|
+
this.ctx.setLineCap('round')
|
|
325
|
+
this.ctx.setLineJoin('round')
|
|
326
|
+
|
|
327
|
+
let lastPoint = null
|
|
328
|
+
path.forEach((point, index) => {
|
|
329
|
+
if (index === 0 && point.type === 'start') {
|
|
330
|
+
// 设置起始点样式
|
|
331
|
+
this.ctx.setStrokeStyle(point.color)
|
|
332
|
+
this.ctx.setLineWidth(point.width)
|
|
333
|
+
this.ctx.moveTo(point.x, point.y)
|
|
334
|
+
lastPoint = { x: point.x, y: point.y }
|
|
335
|
+
} else if (point.type === 'move') {
|
|
336
|
+
this.ctx.lineTo(point.x, point.y)
|
|
337
|
+
lastPoint = { x: point.x, y: point.y }
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
this.ctx.stroke()
|
|
342
|
+
this.ctx.draw(true)
|
|
343
|
+
})
|
|
344
|
+
// #endif
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
// 清空画布
|
|
348
|
+
clear() {
|
|
349
|
+
this.pathStack = []
|
|
350
|
+
this.currentPath = []
|
|
351
|
+
this.isEmpty = true
|
|
352
|
+
this.lastPoint = null
|
|
353
|
+
this.clearCanvas()
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
// 清空画布内容
|
|
357
|
+
clearCanvas() {
|
|
358
|
+
if (!this.ctx) return
|
|
359
|
+
|
|
360
|
+
// #ifndef APP-NVUE
|
|
361
|
+
this.ctx.setFillStyle(this.bgColor)
|
|
362
|
+
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
|
|
363
|
+
this.ctx.draw()
|
|
364
|
+
// #endif
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
// 导出签名图片
|
|
368
|
+
exportSignature() {
|
|
369
|
+
if (this.isEmpty) return
|
|
370
|
+
|
|
371
|
+
// #ifndef APP-NVUE
|
|
372
|
+
uni.canvasToTempFilePath({
|
|
373
|
+
canvasId: this.canvasId,
|
|
374
|
+
fileType: 'png',
|
|
375
|
+
quality: 1,
|
|
376
|
+
success: (res) => {
|
|
377
|
+
this.$emit('confirm', res.tempFilePath)
|
|
378
|
+
},
|
|
379
|
+
fail: (err) => {
|
|
380
|
+
this.$emit('error', err)
|
|
381
|
+
}
|
|
382
|
+
}, this)
|
|
383
|
+
// #endif
|
|
384
|
+
|
|
385
|
+
// #ifdef APP-NVUE
|
|
386
|
+
// NVUE环境下可能需要特殊处理
|
|
387
|
+
// #endif
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
// 切换笔画设置显示
|
|
391
|
+
toggleBrushSettings() {
|
|
392
|
+
this.showBrushSettings = !this.showBrushSettings;
|
|
393
|
+
if (this.showBrushSettings) {
|
|
394
|
+
this.showColorSettings = false;
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
// 切换颜色设置显示
|
|
399
|
+
toggleColorSettings() {
|
|
400
|
+
this.showColorSettings = !this.showColorSettings;
|
|
401
|
+
if (this.showColorSettings) {
|
|
402
|
+
this.showBrushSettings = false;
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
</script>
|
|
408
|
+
|
|
409
|
+
<style lang="scss" scoped>
|
|
410
|
+
.u-signature {
|
|
411
|
+
display: flex;
|
|
412
|
+
flex-direction: column;
|
|
413
|
+
|
|
414
|
+
&__canvas-wrap {
|
|
415
|
+
border: 1px solid #e0e0e0;
|
|
416
|
+
border-radius: 4px;
|
|
417
|
+
overflow: hidden;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
&__canvas {
|
|
421
|
+
width: 100%;
|
|
422
|
+
height: 100%;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
&__toolbar {
|
|
426
|
+
margin-top: 5px;
|
|
427
|
+
background-color: #fff;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
&__toolbar-icons {
|
|
431
|
+
display: flex;
|
|
432
|
+
justify-content: space-between;
|
|
433
|
+
align-items: center;
|
|
434
|
+
padding: 1px 0;
|
|
435
|
+
// border: 1px solid #e0e0e0;
|
|
436
|
+
border-radius: 4px;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
&__toolbar-icon {
|
|
440
|
+
padding: 5px;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
&__brush-settings,
|
|
444
|
+
&__color-settings {
|
|
445
|
+
margin-top: 15px;
|
|
446
|
+
padding: 1px;
|
|
447
|
+
// border: 1px solid #e0e0e0;
|
|
448
|
+
border-radius: 4px;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
&__progress {
|
|
452
|
+
&-label {
|
|
453
|
+
display: block;
|
|
454
|
+
margin-bottom: 10px;
|
|
455
|
+
font-size: 14px;
|
|
456
|
+
color: #999;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
&__color-picker {
|
|
461
|
+
margin-bottom: 10px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
&__color-label {
|
|
465
|
+
display: block;
|
|
466
|
+
margin-bottom: 10px;
|
|
467
|
+
font-size: 14px;
|
|
468
|
+
color: #999;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
&__colors {
|
|
472
|
+
display: flex;
|
|
473
|
+
flex-direction: row;
|
|
474
|
+
flex-wrap: wrap;
|
|
475
|
+
gap: 10px;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
&__color-item {
|
|
479
|
+
width: 30px;
|
|
480
|
+
height: 30px;
|
|
481
|
+
border-radius: 50%;
|
|
482
|
+
border: 2px solid #f0f0f0;
|
|
483
|
+
cursor: pointer;
|
|
484
|
+
|
|
485
|
+
&--active {
|
|
486
|
+
border-color: #2979ff;
|
|
487
|
+
transform: scale(1.1);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
&__actions {
|
|
492
|
+
display: flex;
|
|
493
|
+
flex-direction: row;
|
|
494
|
+
gap: 10px;
|
|
495
|
+
justify-content: center;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
</style>
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"id": "uview-plus",
|
|
3
3
|
"name": "uview-plus",
|
|
4
4
|
"displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙跨端移动组件库,组件丰富维护更新稳定。",
|
|
5
|
-
"version": "3.4.
|
|
6
|
-
"description": "零云®uview-plus已兼容vue3,100
|
|
5
|
+
"version": "3.4.86",
|
|
6
|
+
"description": "零云®uview-plus已兼容vue3,100+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名等。",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"uview",
|
|
9
9
|
"uview-plus",
|