uview-plus 3.4.85 → 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,3 +1,6 @@
1
+ ## 3.4.86(2025-08-12)
2
+ feat: 新增signature签名签字组件
3
+
1
4
  ## 3.4.85(2025-08-11)
2
5
  feat: 完善图片裁剪功能支持props及JS两种方式
3
6
 
@@ -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.85",
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",