hy-app 0.2.1 → 0.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.
Files changed (50) hide show
  1. package/components/hy-button/index.scss +4 -0
  2. package/components/hy-code-input/hy-code-input.vue +223 -0
  3. package/components/hy-code-input/index.scss +108 -0
  4. package/components/hy-code-input/props.ts +21 -0
  5. package/components/hy-code-input/typing.d.ts +65 -0
  6. package/components/hy-config-provider/hy-config-provider.vue +0 -1
  7. package/components/hy-dropdown/props.ts +1 -1
  8. package/components/hy-dropdown-item/hy-dropdown-item.vue +2 -5
  9. package/components/hy-dropdown-item/index.scss +11 -13
  10. package/components/hy-grid/hy-grid.vue +5 -5
  11. package/components/hy-icon/index.scss +1 -0
  12. package/components/hy-modal/hy-modal.vue +5 -5
  13. package/components/hy-modal/index.scss +0 -6
  14. package/components/hy-notify/hy-notify.vue +169 -0
  15. package/components/hy-notify/index.scss +25 -0
  16. package/components/hy-notify/props.ts +14 -0
  17. package/components/hy-notify/typing.d.ts +44 -0
  18. package/components/hy-pagination/hy-pagination.vue +125 -0
  19. package/components/hy-pagination/index.scss +46 -0
  20. package/components/hy-pagination/props.ts +15 -0
  21. package/components/hy-pagination/typing.d.ts +44 -0
  22. package/components/hy-picker/index.scss +4 -0
  23. package/components/hy-scroll-list/index.scss +1 -1
  24. package/components/hy-search/index.scss +1 -2
  25. package/components/hy-signature/canvasHelper.ts +51 -0
  26. package/components/hy-signature/hy-signature.vue +656 -0
  27. package/components/hy-signature/index.scss +31 -0
  28. package/components/hy-signature/props.ts +28 -0
  29. package/components/hy-signature/typing.d.ts +177 -0
  30. package/components/hy-slider/index.scss +5 -1
  31. package/components/hy-swipe-action/hy-swipe-action.vue +288 -248
  32. package/components/hy-swipe-action/index.scss +34 -0
  33. package/components/hy-swipe-action/index.ts +34 -0
  34. package/components/hy-swipe-action/props.ts +15 -9
  35. package/components/hy-swipe-action/typing.d.ts +20 -22
  36. package/components/hy-swiper/index.scss +5 -0
  37. package/components/hy-tabs/index.scss +2 -2
  38. package/components/hy-tag/index.scss +1 -1
  39. package/components/hy-textarea/hy-textarea.vue +5 -5
  40. package/components/hy-textarea/index.scss +5 -6
  41. package/components/hy-tooltip/index.scss +2 -2
  42. package/components/hy-upload/index.scss +1 -1
  43. package/composables/index.ts +1 -0
  44. package/composables/useTouch.ts +48 -0
  45. package/libs/css/mixin.scss +52 -13
  46. package/libs/css/vars.css +7 -1
  47. package/package.json +2 -2
  48. package/theme.scss +23 -46
  49. package/components/hy-swipe-action/index.wxs +0 -235
  50. package/components/hy-swipe-action/wxs.js +0 -15
@@ -0,0 +1,656 @@
1
+ <template>
2
+ <view class="hy-signature" :style="customStyle">
3
+ <view class="hy-signature__content">
4
+ <!-- #ifdef MP-WEIXIN -->
5
+ <canvas
6
+ class="hy-signature__content-canvas"
7
+ :style="canvasStyle"
8
+ :width="canvasState.canvasWidth"
9
+ :height="canvasState.canvasHeight"
10
+ :canvas-id="canvasId"
11
+ :id="canvasId"
12
+ :disable-scroll="disableScroll"
13
+ @touchstart="startDrawing"
14
+ @touchend="stopDrawing"
15
+ @touchmove="draw"
16
+ type="2d"
17
+ />
18
+ <!-- #endif -->
19
+ <!-- #ifndef MP-WEIXIN -->
20
+ <canvas
21
+ class="hy-signature__content-canvas"
22
+ :canvas-id="canvasId"
23
+ :style="canvasStyle"
24
+ :width="canvasState.canvasWidth"
25
+ :height="canvasState.canvasHeight"
26
+ :id="canvasId"
27
+ :disable-scroll="disableScroll"
28
+ @touchstart="startDrawing"
29
+ @touchend="stopDrawing"
30
+ @touchmove="draw"
31
+ />
32
+ <!-- #endif -->
33
+ </view>
34
+ <view class="hy-signature__footer">
35
+ <slot
36
+ name="footer"
37
+ :clear="clear"
38
+ :confirm="confirmSignature"
39
+ :current-step="currentStep"
40
+ :revoke="revoke"
41
+ :restore="restore"
42
+ :can-undo="lines.length > 0"
43
+ :can-redo="redoLines.length > 0"
44
+ :history-list="lines"
45
+ >
46
+ <block v-if="enableHistory">
47
+ <hy-button
48
+ size="small"
49
+ plain
50
+ shape="circle"
51
+ @click="revoke"
52
+ :disabled="lines.length <= 0"
53
+ :text="revokeText"
54
+ >
55
+ </hy-button>
56
+ <hy-button
57
+ size="small"
58
+ plain
59
+ shape="circle"
60
+ @click="restore"
61
+ :disabled="redoLines.length <= 0"
62
+ :text="restoreText"
63
+ ></hy-button>
64
+ </block>
65
+ <hy-button
66
+ size="small"
67
+ plain
68
+ shape="circle"
69
+ @click="clear"
70
+ :text="clearText"
71
+ ></hy-button>
72
+ <hy-button
73
+ size="small"
74
+ shape="circle"
75
+ @click="confirmSignature"
76
+ :text="confirmText"
77
+ ></hy-button>
78
+ </slot>
79
+ </view>
80
+ </view>
81
+ </template>
82
+ <script lang="ts">
83
+ export default {
84
+ name: "hy-signature",
85
+ options: {
86
+ addGlobalClass: true,
87
+ virtualHost: true,
88
+ styleIsolation: "shared",
89
+ },
90
+ };
91
+ </script>
92
+ <script lang="ts" setup>
93
+ import {
94
+ computed,
95
+ getCurrentInstance,
96
+ onBeforeMount,
97
+ onMounted,
98
+ reactive,
99
+ ref,
100
+ watch,
101
+ type CSSProperties,
102
+ } from "vue";
103
+ import { addUnit, getRect, guid } from "../../utils";
104
+ import type { SignatureExpose, SignatureResult, Point, Line } from "./typing";
105
+ import type IProps from "./typing";
106
+ import defaultProps from "./props";
107
+ // #ifdef MP-WEIXIN
108
+ import { canvas2dAdapter } from "./canvasHelper";
109
+ // #endif
110
+
111
+ // 组件
112
+ import HyButton from "@/package/components/hy-button/hy-button.vue";
113
+
114
+ const props = withDefaults(defineProps<IProps>(), defaultProps);
115
+ const emit = defineEmits(["start", "end", "signing", "confirm", "clear"]);
116
+ const { proxy } = getCurrentInstance() as any;
117
+ const canvasId = ref<string>(`signature${guid()}`); // canvas 组件的唯一标识符
118
+ let canvas: null = null; //canvas对象 微信小程序生成图片必须传入
119
+ const drawing = ref<boolean>(false); // 是否正在绘制
120
+ const pixelRatio = ref<number>(1); // 像素比
121
+
122
+ const canvasState = reactive({
123
+ canvasWidth: 0,
124
+ canvasHeight: 0,
125
+ ctx: null as UniApp.CanvasContext | null, // canvas上下文
126
+ });
127
+
128
+ watch(
129
+ () => props.penColor,
130
+ () => {
131
+ setLine();
132
+ },
133
+ );
134
+
135
+ watch(
136
+ () => props.lineWidth,
137
+ () => {
138
+ setLine();
139
+ },
140
+ );
141
+
142
+ const canvasStyle = computed(() => {
143
+ const style: CSSProperties = {};
144
+ if (props.width) {
145
+ style.width = addUnit(props.width);
146
+ }
147
+
148
+ if (props.height) {
149
+ style.height = addUnit(props.height);
150
+ }
151
+
152
+ return `${style}`;
153
+ });
154
+
155
+ const disableScroll = computed(() => props.disableScroll);
156
+ const enableHistory = computed(() => props.enableHistory);
157
+
158
+ const lines = ref<Line[]>([]); // 保存所有线条
159
+ const redoLines = ref<Line[]>([]); // 保存撤销的线条
160
+ const currentLine = ref<Line>(); // 当前正在绘制的线
161
+ const currentStep = ref(0); // 当前步骤
162
+
163
+ // 添加计算笔画宽度的方法
164
+ function calculateLineWidth(speed: number): number {
165
+ if (!props.pressure) return props.lineWidth;
166
+
167
+ const minSpeed = props.minSpeed || 1.5;
168
+ const limitedSpeed = Math.min(minSpeed * 10, Math.max(minSpeed, speed));
169
+ const addWidth =
170
+ ((props.maxWidth - props.minWidth) * (limitedSpeed - minSpeed)) / minSpeed;
171
+ const lineWidth = Math.max(props.maxWidth - addWidth, props.minWidth);
172
+ return Math.min(lineWidth, props.maxWidth);
173
+ }
174
+
175
+ /* 获取默认笔画宽度 */
176
+ const getDefaultLineWidth = () => {
177
+ if (props.pressure) {
178
+ // 在压感模式下,使用最大和最小宽度的平均值作为默认值
179
+ return (props.maxWidth + props.minWidth) / 2;
180
+ }
181
+ return props.lineWidth;
182
+ };
183
+
184
+ /* 开始画线 */
185
+ const startDrawing = (e: any) => {
186
+ e.preventDefault();
187
+ drawing.value = true;
188
+ setLine();
189
+ emit("start", e);
190
+
191
+ // 创建新线条,同时保存当前的所有绘制参数
192
+ const { x, y } = e.touches[0];
193
+ currentLine.value = {
194
+ points: [
195
+ {
196
+ x,
197
+ y,
198
+ t: Date.now(), // 使用 t 替换 width
199
+ },
200
+ ],
201
+ color: props.penColor,
202
+ width: getDefaultLineWidth(),
203
+ backgroundColor: props.backgroundColor,
204
+ isPressure: props.pressure, // 添加笔锋模式标记
205
+ };
206
+
207
+ // 清空重做记录
208
+ redoLines.value = [];
209
+ draw(e);
210
+ };
211
+
212
+ /* 结束画线 */
213
+ const stopDrawing = (e: TouchEvent) => {
214
+ e.preventDefault();
215
+ drawing.value = false;
216
+ if (currentLine.value) {
217
+ // 保存完整的线条信息,包括所有点的参数
218
+ lines.value.push({
219
+ ...currentLine.value,
220
+ points: currentLine.value.points.map((point) => ({
221
+ ...point,
222
+ t: point.t,
223
+ speed: point.speed,
224
+ distance: point.distance,
225
+ lineWidth: point.lineWidth,
226
+ lastX1: point.lastX1,
227
+ lastY1: point.lastY1,
228
+ lastX2: point.lastX2,
229
+ lastY2: point.lastY2,
230
+ isFirstPoint: point.isFirstPoint,
231
+ })),
232
+ });
233
+ currentStep.value = lines.value.length;
234
+ }
235
+ currentLine.value = undefined;
236
+ const { ctx } = canvasState;
237
+ if (ctx) ctx.beginPath();
238
+ emit("end", e);
239
+ };
240
+
241
+ /**
242
+ * 初始化 canvas
243
+ * @param forceUpdate 是否强制更新
244
+ */
245
+ const initCanvas = (forceUpdate: boolean = false) => {
246
+ // 如果不是强制更新,且已经初始化过 canvas,则不再重复初始化
247
+ if (!forceUpdate && canvasState.canvasHeight && canvasState.canvasWidth) {
248
+ return;
249
+ }
250
+ getContext().then(() => {
251
+ const { ctx } = canvasState;
252
+ if (ctx && props.backgroundColor) {
253
+ ctx.setFillStyle(props.backgroundColor);
254
+ ctx.fillRect(0, 0, canvasState.canvasWidth, canvasState.canvasHeight);
255
+ ctx.draw();
256
+ }
257
+ });
258
+ };
259
+
260
+ // 清空 canvas
261
+ const clear = () => {
262
+ lines.value = [];
263
+ redoLines.value = [];
264
+ currentStep.value = 0;
265
+ clearCanvas();
266
+ emit("clear");
267
+ };
268
+
269
+ // 确认签名
270
+ const confirmSignature = () => {
271
+ canvasToImage();
272
+ };
273
+
274
+ //canvas划线
275
+ const draw = (e: any) => {
276
+ e.preventDefault();
277
+ const { ctx } = canvasState;
278
+
279
+ if (!drawing.value || props.disabled || !ctx) return;
280
+ const { x, y } = e.touches[0];
281
+
282
+ const point: Point = {
283
+ x,
284
+ y,
285
+ t: Date.now(),
286
+ };
287
+
288
+ if (currentLine.value) {
289
+ const points = currentLine.value.points;
290
+ const prePoint = points[points.length - 1];
291
+
292
+ if (prePoint.t === point.t || (prePoint.x === x && prePoint.y === y)) {
293
+ return;
294
+ }
295
+
296
+ // 计算点的速度和距离
297
+ point.distance = Math.sqrt(
298
+ Math.pow(point.x - prePoint.x, 2) + Math.pow(point.y - prePoint.y, 2),
299
+ );
300
+ point.speed = point.distance / (point.t - prePoint.t || 0.1);
301
+
302
+ if (props.pressure) {
303
+ point.lineWidth = calculateLineWidth(point.speed);
304
+ // 处理线宽变化率限制
305
+ if (points.length >= 2) {
306
+ const prePoint2 = points[points.length - 2];
307
+ if (prePoint2.lineWidth && prePoint.lineWidth) {
308
+ const rate =
309
+ (point.lineWidth - prePoint.lineWidth) / prePoint.lineWidth;
310
+ const maxRate = 0.2; // 最大变化率20%
311
+ if (Math.abs(rate) > maxRate) {
312
+ const per = rate > 0 ? maxRate : -maxRate;
313
+ point.lineWidth = prePoint.lineWidth * (1 + per);
314
+ }
315
+ }
316
+ }
317
+ }
318
+
319
+ points.push(point);
320
+
321
+ // 非笔锋模式直接使用线段连接
322
+ if (!props.pressure) {
323
+ ctx.beginPath();
324
+ ctx.moveTo(prePoint.x, prePoint.y);
325
+ ctx.lineTo(point.x, point.y);
326
+ ctx.stroke();
327
+ ctx.draw(true);
328
+ } else if (points.length >= 2) {
329
+ // 笔锋模式使用贝塞尔曲线
330
+ drawSmoothLine(prePoint, point);
331
+ }
332
+ }
333
+
334
+ emit("signing", e);
335
+ };
336
+
337
+ /* 重绘整个画布 */
338
+ const redrawCanvas = () => {
339
+ const { ctx } = canvasState;
340
+ if (!ctx) return;
341
+
342
+ // 清除画布并设置背景
343
+ if (props.backgroundColor) {
344
+ ctx.setFillStyle(props.backgroundColor);
345
+ ctx.fillRect(0, 0, canvasState.canvasWidth, canvasState.canvasHeight);
346
+ } else {
347
+ ctx.clearRect(0, 0, canvasState.canvasWidth, canvasState.canvasHeight);
348
+ }
349
+
350
+ // 如果没有线条,只需要清空画布
351
+ if (lines.value.length === 0) {
352
+ ctx.draw();
353
+ return;
354
+ }
355
+
356
+ // 收集所有绘制操作,最后一次性 draw
357
+ lines.value.forEach((line) => {
358
+ if (!line.points.length) return;
359
+
360
+ ctx.setStrokeStyle(line.color);
361
+ ctx.setLineJoin("round");
362
+ ctx.setLineCap("round");
363
+
364
+ if (line.isPressure && props.pressure) {
365
+ // 笔锋模式的重绘
366
+ line.points.forEach((point, index) => {
367
+ if (index === 0) return;
368
+ const prePoint = line.points[index - 1];
369
+ const dis_x = point.x - prePoint.x;
370
+ const dis_y = point.y - prePoint.y;
371
+ const distance = Math.sqrt(dis_x * dis_x + dis_y * dis_y);
372
+
373
+ if (distance <= 2) {
374
+ point.lastX1 = point.lastX2 = prePoint.x + dis_x * 0.5;
375
+ point.lastY1 = point.lastY2 = prePoint.y + dis_y * 0.5;
376
+ } else {
377
+ const speed = point.speed || 0;
378
+ const minSpeed = props.minSpeed || 1.5;
379
+ const speedFactor = Math.max(
380
+ 0.1,
381
+ Math.min(0.9, speed / (minSpeed * 10)),
382
+ );
383
+
384
+ point.lastX1 = prePoint.x + dis_x * (0.2 + speedFactor * 0.3);
385
+ point.lastY1 = prePoint.y + dis_y * (0.2 + speedFactor * 0.3);
386
+ point.lastX2 = prePoint.x + dis_x * (0.8 - speedFactor * 0.3);
387
+ point.lastY2 = prePoint.y + dis_y * (0.8 - speedFactor * 0.3);
388
+ }
389
+
390
+ const lineWidth = point.lineWidth || line.width;
391
+ if (typeof prePoint.lastX1 === "number") {
392
+ ctx.setLineWidth(lineWidth);
393
+ ctx.beginPath();
394
+ ctx.moveTo(prePoint.lastX2!, prePoint.lastY2!);
395
+ ctx.quadraticCurveTo(
396
+ prePoint.x,
397
+ prePoint.y,
398
+ point.lastX1,
399
+ point.lastY1,
400
+ );
401
+ ctx.stroke();
402
+
403
+ if (!prePoint.isFirstPoint) {
404
+ ctx.beginPath();
405
+ ctx.moveTo(prePoint.lastX1!, prePoint.lastY1!);
406
+ ctx.quadraticCurveTo(
407
+ prePoint.x,
408
+ prePoint.y,
409
+ prePoint.lastX2!,
410
+ prePoint.lastY2!,
411
+ );
412
+ ctx.stroke();
413
+ }
414
+ } else {
415
+ point.isFirstPoint = true;
416
+ }
417
+ });
418
+ } else {
419
+ // 非笔锋模式的重绘
420
+ ctx.setLineWidth(line.width);
421
+ line.points.forEach((point, index) => {
422
+ if (index === 0) return;
423
+ const prePoint = line.points[index - 1];
424
+ ctx.beginPath();
425
+ ctx.moveTo(prePoint.x, prePoint.y);
426
+ ctx.lineTo(point.x, point.y);
427
+ ctx.stroke();
428
+ });
429
+ }
430
+ });
431
+
432
+ // 所有线条绘制完成后,一次性更新画布
433
+ ctx.draw();
434
+ };
435
+
436
+ // 修改撤销功能
437
+ const revoke = () => {
438
+ if (!lines.value.length) return;
439
+ const step = Math.min(props.step, lines.value.length);
440
+ const removedLines = lines.value.splice(lines.value.length - step);
441
+ redoLines.value.push(...removedLines);
442
+ currentStep.value = Math.max(0, currentStep.value - step);
443
+ redrawCanvas();
444
+ };
445
+
446
+ // 修改恢复功能
447
+ const restore = () => {
448
+ if (!redoLines.value.length) return;
449
+ const step = Math.min(props.step, redoLines.value.length);
450
+ const restoredLines = redoLines.value.splice(redoLines.value.length - step);
451
+ lines.value.push(...restoredLines);
452
+ currentStep.value = Math.min(lines.value.length, currentStep.value + step);
453
+ redrawCanvas();
454
+ };
455
+
456
+ // 添加平滑线条绘制方法
457
+ function drawSmoothLine(prePoint: Point, point: Point) {
458
+ const { ctx } = canvasState;
459
+ if (!ctx) return;
460
+
461
+ // 计算两点间距离
462
+ const dis_x = point.x - prePoint.x;
463
+ const dis_y = point.y - prePoint.y;
464
+ const distance = Math.sqrt(dis_x * dis_x + dis_y * dis_y);
465
+
466
+ if (distance <= 2) {
467
+ // 对于非常近的点,直接使用中点
468
+ point.lastX1 = point.lastX2 = prePoint.x + dis_x * 0.5;
469
+ point.lastY1 = point.lastY2 = prePoint.y + dis_y * 0.5;
470
+ } else {
471
+ // 根据点的速度计算控制点的偏移程度
472
+ const speed = point.speed || 0;
473
+ const minSpeed = props.minSpeed || 1.5;
474
+ const speedFactor = Math.max(0.1, Math.min(0.9, speed / (minSpeed * 10)));
475
+
476
+ // 计算控制点
477
+ point.lastX1 = prePoint.x + dis_x * (0.2 + speedFactor * 0.3);
478
+ point.lastY1 = prePoint.y + dis_y * (0.2 + speedFactor * 0.3);
479
+ point.lastX2 = prePoint.x + dis_x * (0.8 - speedFactor * 0.3);
480
+ point.lastY2 = prePoint.y + dis_y * (0.8 - speedFactor * 0.3);
481
+ }
482
+
483
+ // 计算线宽
484
+ const lineWidth = point.lineWidth || props.lineWidth;
485
+
486
+ // 绘制贝塞尔曲线
487
+ if (typeof prePoint.lastX1 === "number") {
488
+ // 设置线宽
489
+ ctx.setLineWidth(lineWidth);
490
+ // 绘制第一段曲线
491
+ ctx.beginPath();
492
+ ctx.moveTo(prePoint.lastX2!, prePoint.lastY2!);
493
+ ctx.quadraticCurveTo(prePoint.x, prePoint.y, point.lastX1, point.lastY1);
494
+ ctx.stroke();
495
+
496
+ if (!prePoint.isFirstPoint) {
497
+ // 绘制连接段曲线
498
+ ctx.beginPath();
499
+ ctx.moveTo(prePoint.lastX1!, prePoint.lastY1!);
500
+ ctx.quadraticCurveTo(
501
+ prePoint.x,
502
+ prePoint.y,
503
+ prePoint.lastX2!,
504
+ prePoint.lastY2!,
505
+ );
506
+ ctx.stroke();
507
+ }
508
+
509
+ // 批量更新绘制内容
510
+ ctx.draw(true);
511
+ } else {
512
+ point.isFirstPoint = true;
513
+ }
514
+ }
515
+
516
+ onMounted(() => {
517
+ initCanvas();
518
+ });
519
+
520
+ onBeforeMount(() => {
521
+ // #ifdef MP
522
+ pixelRatio.value = uni.getSystemInfoSync().pixelRatio;
523
+ // #endif
524
+ });
525
+
526
+ /**
527
+ * 获取canvas上下文
528
+ */
529
+ function getContext() {
530
+ return new Promise<UniApp.CanvasContext>((resolve) => {
531
+ const { ctx } = canvasState;
532
+
533
+ if (ctx) {
534
+ return resolve(ctx);
535
+ }
536
+ // #ifndef MP-WEIXIN
537
+ getRect(`#${canvasId.value}`, false, proxy).then((canvasRect) => {
538
+ setcanvasState(canvasRect.width!, canvasRect.height!);
539
+ canvasState.ctx = uni.createCanvasContext(canvasId.value, proxy);
540
+ if (canvasState.ctx) {
541
+ canvasState.ctx.scale(pixelRatio.value, pixelRatio.value);
542
+ }
543
+ resolve(canvasState.ctx);
544
+ });
545
+ // #endif
546
+ // #ifdef MP-WEIXIN
547
+
548
+ getRect(`#${canvasId.value}`, false, proxy, true).then(
549
+ (canvasRect: any) => {
550
+ if (
551
+ canvasRect &&
552
+ canvasRect.node &&
553
+ canvasRect.width &&
554
+ canvasRect.height
555
+ ) {
556
+ const canvasInstance = canvasRect.node;
557
+ canvasState.ctx = canvas2dAdapter(
558
+ canvasInstance.getContext("2d") as CanvasRenderingContext2D,
559
+ );
560
+ canvasInstance.width = canvasRect.width * pixelRatio.value;
561
+ canvasInstance.height = canvasRect.height * pixelRatio.value;
562
+ canvasState.ctx.scale(pixelRatio.value, pixelRatio.value);
563
+ canvas = canvasInstance;
564
+ setcanvasState(canvasRect.width, canvasRect.height);
565
+ resolve(canvasState.ctx);
566
+ }
567
+ },
568
+ );
569
+ // #endif
570
+ });
571
+ }
572
+
573
+ /**
574
+ * 设置 canvasState
575
+ */
576
+ function setcanvasState(width: number, height: number) {
577
+ canvasState.canvasHeight = height * pixelRatio.value;
578
+ canvasState.canvasWidth = width * pixelRatio.value;
579
+ }
580
+
581
+ /* 设置线段 */
582
+ function setLine() {
583
+ const { ctx } = canvasState;
584
+ if (ctx) {
585
+ ctx.setLineWidth(getDefaultLineWidth()); // 使用新的默认宽度
586
+ ctx.setStrokeStyle(props.penColor);
587
+ ctx.setLineJoin("round");
588
+ ctx.setLineCap("round");
589
+ }
590
+ }
591
+
592
+ /**
593
+ * canvas 绘制图片输出成文件类型
594
+ */
595
+ function canvasToImage() {
596
+ const { fileType, quality, exportScale } = props;
597
+ const { canvasWidth, canvasHeight } = canvasState;
598
+ uni.canvasToTempFilePath(
599
+ {
600
+ width: canvasWidth * exportScale,
601
+ height: canvasHeight * exportScale,
602
+ destWidth: canvasWidth * exportScale,
603
+ destHeight: canvasHeight * exportScale,
604
+ fileType,
605
+ quality,
606
+ canvasId: canvasId.value,
607
+ canvas: canvas,
608
+ success: (res) => {
609
+ const result: SignatureResult = {
610
+ tempFilePath: res.tempFilePath,
611
+ width: (canvasWidth * exportScale) / pixelRatio.value,
612
+ height: (canvasHeight * exportScale) / pixelRatio.value,
613
+ success: true,
614
+ };
615
+ // #ifdef MP-DINGTALK
616
+ result.tempFilePath = (res as any).filePath;
617
+ // #endif
618
+ emit("confirm", result);
619
+ },
620
+ fail: () => {
621
+ const result: SignatureResult = {
622
+ tempFilePath: "",
623
+ width: (canvasWidth * exportScale) / pixelRatio.value,
624
+ height: (canvasHeight * exportScale) / pixelRatio.value,
625
+ success: false,
626
+ };
627
+ emit("confirm", result);
628
+ },
629
+ },
630
+ proxy,
631
+ );
632
+ }
633
+
634
+ function clearCanvas() {
635
+ const { canvasWidth, canvasHeight, ctx } = canvasState;
636
+ if (ctx) {
637
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
638
+ if (props.backgroundColor) {
639
+ ctx.setFillStyle(props.backgroundColor);
640
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
641
+ }
642
+ ctx.draw();
643
+ }
644
+ }
645
+
646
+ defineExpose<SignatureExpose>({
647
+ init: initCanvas,
648
+ clear,
649
+ confirm: confirmSignature,
650
+ restore,
651
+ revoke,
652
+ });
653
+ </script>
654
+ <style scoped lang="scss">
655
+ @import "./index.scss";
656
+ </style>
@@ -0,0 +1,31 @@
1
+ @use "../../theme.scss" as *;
2
+ @use "../../libs/css/mixin.scss" as *;
3
+
4
+ @include b(signature) {
5
+ @include e(content) {
6
+ justify-content: center;
7
+ align-items: center;
8
+ display: flex;
9
+ overflow: hidden;
10
+ background: $hy-background--3;
11
+ border-radius: $hy-border-radius-base;
12
+ border: $hy-border-line;
13
+ }
14
+
15
+ @include e(content-canvas) {
16
+ width: 100%;
17
+ }
18
+
19
+
20
+ @include e(footer) {
21
+ margin-top: $hy-border-margin-padding-base;
22
+ justify-content: flex-end;
23
+ display: flex;
24
+
25
+ :deep(){
26
+ @include b(button) {
27
+ margin-left: $hy-border-margin-padding-base;
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,28 @@
1
+ import type IProps from "./typing";
2
+
3
+ const defaultProps: IProps = {
4
+ penColor: "#000",
5
+ lineWidth: 1,
6
+ clearText: "清空",
7
+ revokeText: "撤销",
8
+ restoreText: "恢复",
9
+ confirmText: "确认",
10
+ fileType: "png",
11
+ quality: 1,
12
+ exportScale: 1,
13
+ disabled: false,
14
+ height: 1,
15
+ width: 1,
16
+ backgroundColor: "",
17
+ disableScroll: true,
18
+ enableHistory: false,
19
+ step: 1,
20
+ undoText: "",
21
+ redoText: "",
22
+ pressure: false,
23
+ minWidth: 2,
24
+ maxWidth: 6,
25
+ minSpeed: 1.5,
26
+ };
27
+
28
+ export default defaultProps;