uview-plus 3.7.0 → 3.7.13

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,19 +1,22 @@
1
1
  <template>
2
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"
3
+ <view class="u-signature__canvas-wrap" :style="{background: bgColor}">
4
+ <up-canvas
5
+ ref="signatureCanvas"
6
+ :canvas-id="canvasId"
7
+ :width="canvasWidth"
8
+ :height="canvasHeight"
9
+ :bg-color="bgColor"
10
+ @touchstart="touchStart"
11
+ @touchmove="touchMove"
10
12
  @touchend="touchEnd"
13
+ :disable-scroll="true"
14
+ class="u-signature__canvas"
11
15
  :style="{
12
16
  width: canvasWidth + 'px',
13
17
  height: canvasHeight + 'px',
14
- background: bgColor
15
- }"
16
- ></canvas>
18
+ }">
19
+ </up-canvas>
17
20
  </view>
18
21
 
19
22
  <view v-if="showToolbar" class="u-signature__toolbar">
@@ -116,7 +119,6 @@
116
119
  isDrawing: false,
117
120
  pathStack: [], // 存储绘制路径用于回退
118
121
  currentPath: [], // 当前绘制路径
119
- ctx: null,
120
122
  isEmpty: true,
121
123
  presetColors: [
122
124
  '#000000', // 黑色
@@ -130,11 +132,16 @@
130
132
  ],
131
133
  showBrushSettings: false,
132
134
  showColorSettings: false,
133
- lastPoint: null // 保存上一个点的坐标
135
+ lastPoint: null, // 保存上一个点的坐标
136
+ canvasInstance: null // 缓存canvas实例
134
137
  }
135
138
  },
136
139
  mounted() {
137
- this.initCanvas()
140
+ // 初始化时获取canvas实例
141
+ this.$nextTick(() => {
142
+ this.getCanvasInstance();
143
+ this.clearCanvas();
144
+ });
138
145
  },
139
146
  watch: {
140
147
  width: {
@@ -164,32 +171,40 @@
164
171
  },
165
172
  methods: {
166
173
  t,
167
- initCanvas() {
168
- // #ifndef APP-NVUE
169
- const ctx = uni.createCanvasContext(this.canvasId, this)
170
- this.ctx = ctx
171
- this.clearCanvas()
172
- // #endif
174
+
175
+ // 获取签名画布实例
176
+ getCanvasInstance() {
177
+ if (this.canvasInstance) {
178
+ return this.canvasInstance;
179
+ }
173
180
 
174
- // #ifdef APP-NVUE
175
- // NVUE环境下的处理
176
- // #endif
181
+ const canvasRef = this.$refs.signatureCanvas;
182
+ if (canvasRef) {
183
+ this.canvasInstance = canvasRef;
184
+ return canvasRef;
185
+ }
186
+ return null;
177
187
  },
178
188
 
179
189
  touchStart(e) {
180
- if (!this.ctx) return
190
+ if (!this.canvasInstance || !this.canvasInstance.ctx) {
191
+ this.getCanvasInstance();
192
+ }
193
+
194
+ if (!this.canvasInstance || !this.canvasInstance.ctx) return;
195
+
196
+ this.isDrawing = true;
197
+ this.isEmpty = false;
198
+ this.currentPath = [];
181
199
 
182
- this.isDrawing = true
183
- this.isEmpty = false
184
- this.currentPath = []
200
+ const { x, y } = this.getCanvasPoint(e);
185
201
 
186
- const { x, y } = this.getCanvasPoint(e)
187
- this.ctx.beginPath()
188
- this.ctx.moveTo(x, y)
189
- this.ctx.setLineCap('round')
190
- this.ctx.setLineJoin('round')
191
- this.ctx.setStrokeStyle(this.lineColor)
192
- this.ctx.setLineWidth(this.lineWidth)
202
+ // 设置线条样式
203
+ this.canvasInstance.setLineStyle(this.lineColor, this.lineWidth);
204
+
205
+ // 开始路径
206
+ this.canvasInstance.beginPath();
207
+ this.canvasInstance.moveTo(x, y);
193
208
 
194
209
  // 记录起始点
195
210
  this.currentPath.push({
@@ -198,83 +213,69 @@
198
213
  type: 'start',
199
214
  color: this.lineColor,
200
215
  width: this.lineWidth
201
- })
216
+ });
202
217
 
203
218
  // 保存上一个点
204
- this.lastPoint = { x, y }
219
+ this.lastPoint = { x, y };
205
220
 
206
221
  // 阻止默认事件以提高性能
207
- e.preventDefault()
222
+ e.preventDefault();
208
223
  },
209
224
 
210
225
  touchMove(e) {
211
- if (!this.isDrawing || !this.ctx) return
226
+ if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
212
227
 
213
228
  // 阻止默认事件以提高性能
214
- e.preventDefault()
229
+ e.preventDefault();
215
230
 
216
- const { x, y } = this.getCanvasPoint(e)
231
+ const { x, y } = this.getCanvasPoint(e);
217
232
 
218
- // 使用更密集的点采样确保线条连贯性
219
- if (this.lastPoint) {
220
- // 计算两点间距离
221
- const distance = Math.sqrt(Math.pow(x - this.lastPoint.x, 2) + Math.pow(y - this.lastPoint.y, 2))
222
- // 根据距离确定插值点数量,确保点间距不超过1像素以获得更平滑的线条
223
- const steps = Math.max(1, Math.floor(distance / 1))
224
-
225
- // 在两点间插入插值点
226
- for (let i = 1; i <= steps; i++) {
227
- const t = i / steps
228
- const midX = this.lastPoint.x + (x - this.lastPoint.x) * t
229
- const midY = this.lastPoint.y + (y - this.lastPoint.y) * t
230
-
231
- this.ctx.lineTo(midX, midY)
232
- this.ctx.stroke()
233
- this.currentPath.push({
234
- x: midX,
235
- y: midY,
236
- type: 'move'
237
- })
238
- }
239
- } else {
240
- this.ctx.lineTo(x, y)
241
- this.ctx.stroke()
242
- this.currentPath.push({
243
- x,
244
- y,
245
- type: 'move'
246
- })
247
- }
248
-
249
- this.ctx.draw(true)
233
+ // 从上一个点画线到当前点
234
+ this.canvasInstance.lineTo(x, y);
235
+ this.canvasInstance.stroke(); // 实时绘制当前线段
236
+ this.currentPath.push({
237
+ x,
238
+ y,
239
+ type: 'move'
240
+ });
241
+ this.canvasInstance.draw(false);
250
242
 
251
243
  // 更新上一个点
252
- this.lastPoint = { x, y }
244
+ this.lastPoint = { x, y };
253
245
  },
254
246
 
255
247
  touchEnd(e) {
256
- if (!this.isDrawing || !this.ctx) return
248
+ if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
257
249
 
258
- this.isDrawing = false
259
- this.ctx.closePath()
260
- this.lastPoint = null
250
+ this.isDrawing = false;
251
+ this.canvasInstance.closePath();
252
+ this.lastPoint = null;
261
253
 
262
254
  // 将当前路径加入栈中用于回退
263
255
  if (this.currentPath.length > 0) {
264
- this.pathStack.push([...this.currentPath])
256
+ this.pathStack.push([...this.currentPath]);
265
257
  }
258
+
259
+ // 最后统一执行一次绘制
260
+ this.canvasInstance.draw(true);
266
261
  },
267
262
 
268
263
  // 同步获取canvas坐标点(兼容处理)
269
264
  getCanvasPoint(e) {
270
- const touch = e.touches[0]
271
- const canvas = uni.createSelectorQuery().in(this).select('.u-signature__canvas')
265
+ // #ifdef MP-WEIXIN
266
+ const touch = e.touches && e.touches[0] ? e.touches[0] : e.mp.touches[0];
267
+ // #endif
268
+ // #ifndef MP-WEIXIN
269
+ const touch = e.touches[0];
270
+ // #endif
272
271
 
273
- // 返回一个包含坐标的对象
272
+ // 计算相对于canvas的坐标
273
+ // 由于无法直接获取canvas位置,这里简化处理
274
+ // 实际应用中可能需要通过uni.createSelectorQuery获取canvas位置
274
275
  return {
275
276
  x: touch.x,
276
277
  y: touch.y
277
- }
278
+ };
278
279
  },
279
280
 
280
281
  // 选择颜色
@@ -295,84 +296,83 @@
295
296
 
296
297
  // 重新绘制所有路径
297
298
  redraw() {
298
- this.clearCanvas()
299
+ if (!this.canvasInstance) {
300
+ this.getCanvasInstance();
301
+ }
302
+
303
+ if (!this.canvasInstance) return;
304
+
305
+ // 先清空画布
306
+ this.canvasInstance.clearCanvas();
299
307
 
300
308
  if (this.pathStack.length === 0) {
301
- this.isEmpty = true
302
- return
309
+ this.isEmpty = true;
310
+ return;
303
311
  }
304
312
 
305
- this.isEmpty = false
313
+ this.isEmpty = false;
306
314
 
307
- // #ifndef APP-NVUE
315
+ // 逐个绘制路径
308
316
  this.pathStack.forEach(path => {
309
- if (path.length === 0) return
317
+ if (path.length === 0) return;
310
318
 
311
- this.ctx.beginPath()
312
- this.ctx.setLineCap('round')
313
- this.ctx.setLineJoin('round')
319
+ this.canvasInstance.beginPath();
314
320
 
315
- let lastPoint = null
321
+ let lastPoint = null;
316
322
  path.forEach((point, index) => {
317
323
  if (index === 0 && point.type === 'start') {
318
- // 设置起始点样式
319
- this.ctx.setStrokeStyle(point.color)
320
- this.ctx.setLineWidth(point.width)
321
- this.ctx.moveTo(point.x, point.y)
322
- lastPoint = { x: point.x, y: point.y }
324
+ // 设置线条样式
325
+ this.canvasInstance.setLineStyle(point.color, point.width);
326
+ this.canvasInstance.moveTo(point.x, point.y);
327
+ lastPoint = { x: point.x, y: point.y };
323
328
  } else if (point.type === 'move') {
324
- this.ctx.lineTo(point.x, point.y)
325
- lastPoint = { x: point.x, y: point.y }
329
+ this.canvasInstance.lineTo(point.x, point.y);
330
+ lastPoint = { x: point.x, y: point.y };
326
331
  }
327
- })
328
-
329
- this.ctx.stroke()
330
- this.ctx.draw(true)
331
- })
332
- // #endif
333
- },
334
-
335
- // 清空画布
336
- clear() {
337
- this.pathStack = []
338
- this.currentPath = []
339
- this.isEmpty = true
340
- this.lastPoint = null
341
- this.clearCanvas()
332
+ });
333
+ this.canvasInstance.stroke();
334
+ this.canvasInstance.draw(true);
335
+ });
342
336
  },
343
337
 
344
338
  // 清空画布内容
345
339
  clearCanvas() {
346
- if (!this.ctx) return
340
+ if (!this.canvasInstance) {
341
+ this.getCanvasInstance();
342
+ }
347
343
 
348
- // #ifndef APP-NVUE
349
- this.ctx.setFillStyle(this.bgColor)
350
- this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
351
- this.ctx.draw()
352
- // #endif
344
+ if (!this.canvasInstance) return;
345
+
346
+ this.canvasInstance.clearCanvas();
353
347
  },
354
348
 
355
349
  // 导出签名图片
356
- exportSignature() {
357
- if (this.isEmpty) return
350
+ async exportSignature() {
351
+ if (this.isEmpty) {
352
+ console.warn('签名为空,无法导出');
353
+ return;
354
+ }
358
355
 
359
- // #ifndef APP-NVUE
360
- uni.canvasToTempFilePath({
361
- canvasId: this.canvasId,
362
- fileType: 'png',
363
- quality: 1,
364
- success: (res) => {
365
- this.$emit('confirm', res.tempFilePath)
366
- },
367
- fail: (err) => {
368
- this.$emit('error', err)
369
- }
370
- }, this)
371
- // #endif
356
+ if (!this.canvasInstance) {
357
+ this.getCanvasInstance();
358
+ }
372
359
 
373
- // #ifdef APP-NVUE
374
- // NVUE环境下可能需要特殊处理
375
- // #endif
360
+ if (!this.canvasInstance) {
361
+ console.error('无法获取画布实例');
362
+ return;
363
+ }
364
+
365
+ try {
366
+ // 先重绘整个签名内容
367
+ this.redraw();
368
+
369
+ // 导出图片
370
+ const imagePath = await this.canvasInstance.exportImage('png', 1);
371
+ this.$emit('confirm', imagePath);
372
+ } catch (error) {
373
+ console.error('导出签名图片失败:', error);
374
+ this.$emit('error', error);
375
+ }
376
376
  },
377
377
 
378
378
  // 切换笔画设置显示
@@ -82,7 +82,7 @@ export const props = defineMixin({
82
82
  type: Boolean,
83
83
  default: () => defProps.slider.useNative
84
84
  },
85
- // 滑块高度
85
+ // 滑块厚度
86
86
  height: {
87
87
  type: String,
88
88
  default: () => defProps.slider.height
@@ -91,5 +91,20 @@ export const props = defineMixin({
91
91
  type: Object,
92
92
  default: () => defProps.slider.innerStyle
93
93
  },
94
+ // 是否垂直方向
95
+ vertical: {
96
+ type: Boolean,
97
+ default: defProps.slider.vertical
98
+ },
99
+ // 滑块粗细,为了支持垂直模式下统一粗细参数,用size代替height。
100
+ size: {
101
+ type: [Number, String],
102
+ default: () => defProps.slider.size
103
+ },
104
+ // 滑块长度,水平和垂直模式的长度。
105
+ length: {
106
+ type: [Number, String],
107
+ default: 'auto'
108
+ }
94
109
  }
95
- })
110
+ })
@@ -22,7 +22,10 @@ export default {
22
22
  disabled:false,
23
23
  blockStyle: {},
24
24
  useNative: false,
25
- height: '2px',
26
- innerStyle: {}
25
+ height: '',
26
+ innerStyle: {},
27
+ vertical: false,
28
+ size: '2px',
29
+ length: 'auto'
27
30
  }
28
- }
31
+ }