uview-plus 3.8.49 → 3.8.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.
Files changed (170) hide show
  1. package/changelog.md +2043 -1827
  2. package/components/u-action-sheet/props.js +3 -1
  3. package/components/u-action-sheet/u-action-sheet.vue +1 -0
  4. package/components/u-album/props.js +3 -1
  5. package/components/u-alert/props.js +4 -2
  6. package/components/u-avatar/props.js +4 -1
  7. package/components/u-avatar-group/props.js +4 -1
  8. package/components/u-back-top/props.js +4 -1
  9. package/components/u-badge/props.js +4 -1
  10. package/components/u-barcode/u-barcode.vue +46 -15
  11. package/components/u-box/props.js +5 -2
  12. package/components/u-button/props.js +4 -1
  13. package/components/u-button/vue.scss +3 -3
  14. package/components/u-calendar/calendar.js +14 -2
  15. package/components/u-calendar/month.vue +41 -6
  16. package/components/u-calendar/props.js +63 -1
  17. package/components/u-calendar/u-calendar.vue +311 -2
  18. package/components/u-calendar-strip/props.js +3 -1
  19. package/components/u-canvas/u-canvas.vue +734 -423
  20. package/components/u-car-keyboard/props.js +3 -1
  21. package/components/u-card/props.js +3 -1
  22. package/components/u-cell/props.js +4 -1
  23. package/components/u-cell/u-cell.vue +1 -1
  24. package/components/u-cell-group/props.js +4 -1
  25. package/components/u-checkbox/props.js +4 -1
  26. package/components/u-checkbox/u-checkbox.vue +7 -3
  27. package/components/u-checkbox-group/props.js +3 -1
  28. package/components/u-circle-progress/props.js +4 -1
  29. package/components/u-code/props.js +4 -1
  30. package/components/u-code-input/props.js +4 -1
  31. package/components/u-col/props.js +4 -1
  32. package/components/u-collapse/props.js +4 -1
  33. package/components/u-collapse-item/props.js +4 -1
  34. package/components/u-color-picker/u-color-picker.vue +1 -0
  35. package/components/u-column-notice/props.js +4 -1
  36. package/components/u-count-down/props.js +4 -1
  37. package/components/u-count-to/props.js +4 -1
  38. package/components/u-cropper/u-cropper.vue +95 -51
  39. package/components/u-datetime-picker/props.js +4 -1
  40. package/components/u-divider/props.js +4 -1
  41. package/components/u-dragsort/u-dragsort.vue +2 -1
  42. package/components/u-dropdown/props.js +3 -1
  43. package/components/u-dropdown-item/props.js +3 -1
  44. package/components/u-empty/props.js +4 -1
  45. package/components/u-form/props.js +4 -1
  46. package/components/u-form-item/props.js +4 -1
  47. package/components/u-gap/props.js +4 -1
  48. package/components/u-grid/props.js +4 -1
  49. package/components/u-grid-item/props.js +4 -1
  50. package/components/u-guide/props.js +3 -1
  51. package/components/u-icon/props.js +4 -1
  52. package/components/u-icon/u-icon.vue +2 -2
  53. package/components/u-icon/upicon.ttf +0 -0
  54. package/components/u-icon/util.js +29 -7
  55. package/components/u-image/props.js +4 -1
  56. package/components/u-image/u-image.vue +280 -267
  57. package/components/u-index-anchor/props.js +4 -1
  58. package/components/u-index-item/props.js +2 -1
  59. package/components/u-index-list/props.js +4 -1
  60. package/components/u-input/props.js +3 -1
  61. package/components/u-input/u-input.vue +10 -1
  62. package/components/u-keyboard/props.js +4 -1
  63. package/components/u-line/props.js +4 -1
  64. package/components/u-line-progress/props.js +6 -3
  65. package/components/u-link/props.js +4 -1
  66. package/components/u-list/props.js +4 -1
  67. package/components/u-list-item/props.js +4 -1
  68. package/components/u-loading-icon/props.js +4 -1
  69. package/components/u-loading-page/props.js +3 -1
  70. package/components/u-loadmore/props.js +4 -1
  71. package/components/u-markdown/u-markdown.vue +45 -3
  72. package/components/u-modal/props.js +4 -1
  73. package/components/u-navbar/props.js +4 -2
  74. package/components/u-navbar/u-navbar.vue +15 -9
  75. package/components/u-no-network/props.js +4 -1
  76. package/components/u-notice-bar/props.js +4 -1
  77. package/components/u-notify/props.js +4 -1
  78. package/components/u-number-box/props.js +4 -1
  79. package/components/u-number-box/u-number-box.vue +2 -2
  80. package/components/u-number-keyboard/props.js +4 -1
  81. package/components/u-overlay/props.js +4 -1
  82. package/components/u-pagination/u-pagination.vue +60 -39
  83. package/components/u-parse/props.js +4 -1
  84. package/components/u-picker/props.js +4 -1
  85. package/components/u-picker/u-picker.vue +15 -0
  86. package/components/u-picker-column/props.js +3 -1
  87. package/components/u-popover/props.js +2 -1
  88. package/components/u-popup/props.js +4 -1
  89. package/components/u-poster/u-poster.vue +29 -14
  90. package/components/u-qrcode/qrcode.js +88 -87
  91. package/components/u-qrcode/u-qrcode.vue +138 -256
  92. package/components/u-radio/props.js +4 -1
  93. package/components/u-radio/u-radio.vue +7 -3
  94. package/components/u-radio-group/props.js +4 -1
  95. package/components/u-rate/props.js +4 -1
  96. package/components/u-read-more/props.js +4 -1
  97. package/components/u-row/props.js +4 -1
  98. package/components/u-row-notice/props.js +4 -1
  99. package/components/u-safe-bottom/props.js +2 -1
  100. package/components/u-scroll-list/props.js +4 -1
  101. package/components/u-search/props.js +3 -1
  102. package/components/u-skeleton/props.js +4 -1
  103. package/components/u-slider/props.js +3 -1
  104. package/components/u-slider/u-slider.vue +70 -28
  105. package/components/u-status-bar/props.js +4 -1
  106. package/components/u-steps/props.js +4 -1
  107. package/components/u-steps-item/props.js +4 -1
  108. package/components/u-sticky/props.js +4 -1
  109. package/components/u-sticky/u-sticky.vue +11 -0
  110. package/components/u-subsection/props.js +3 -1
  111. package/components/u-swipe-action/props.js +4 -1
  112. package/components/u-swipe-action-item/props.js +4 -1
  113. package/components/u-swipe-action-item/u-swipe-action-item.vue +14 -0
  114. package/components/u-swiper/props.js +9 -1
  115. package/components/u-swiper/swiper.js +1 -0
  116. package/components/u-swiper/u-swiper.vue +2 -0
  117. package/components/u-swiper-indicator/props.js +4 -1
  118. package/components/u-switch/props.js +4 -1
  119. package/components/u-tabbar/props.js +3 -1
  120. package/components/u-tabbar-item/props.js +4 -1
  121. package/components/u-table/props.js +3 -1
  122. package/components/u-table2/tableRow.vue +155 -69
  123. package/components/u-table2/u-table2.vue +328 -140
  124. package/components/u-tabs/props.js +4 -1
  125. package/components/u-tabs-item/props.js +3 -1
  126. package/components/u-tag/props.js +4 -1
  127. package/components/u-td/props.js +3 -1
  128. package/components/u-text/props.js +4 -1
  129. package/components/u-textarea/props.js +3 -1
  130. package/components/u-textarea/u-textarea.vue +16 -5
  131. package/components/u-th/props.js +3 -1
  132. package/components/u-toolbar/props.js +4 -1
  133. package/components/u-tooltip/props.js +10 -2
  134. package/components/u-tooltip/tooltip.js +2 -1
  135. package/components/u-tooltip/u-tooltip.vue +31 -3
  136. package/components/u-tr/props.js +3 -1
  137. package/components/u-transition/nvue-ani-map.js +75 -68
  138. package/components/u-transition/props.js +4 -1
  139. package/components/u-transition/transitionMixin.js +164 -163
  140. package/components/u-upload/props.js +4 -1
  141. package/components/u-upload/u-upload.vue +47 -40
  142. package/components/u-waterfall/u-waterfall.vue +50 -51
  143. package/index.js +65 -8
  144. package/libs/config/props.js +179 -204
  145. package/libs/css/components.scss +3 -0
  146. package/libs/function/uni-api-shims.js +29 -0
  147. package/libs/i18n/index.js +71 -36
  148. package/libs/i18n/locale-packs.js +11 -0
  149. package/libs/i18n/locales/all.js +23 -0
  150. package/libs/i18n/locales/{de.json → de.js} +1 -1
  151. package/libs/i18n/locales/{en.json → en.js} +2 -2
  152. package/libs/i18n/locales/{es.json → es.js} +1 -1
  153. package/libs/i18n/locales/{fr.json → fr.js} +2 -2
  154. package/libs/i18n/locales/{ja.json → ja.js} +1 -1
  155. package/libs/i18n/locales/{ko.json → ko.js} +1 -1
  156. package/libs/i18n/locales/{ru.json → ru.js} +1 -1
  157. package/libs/i18n/locales/{th.json → th.js} +1 -1
  158. package/libs/i18n/locales/{zh-Hans.json → zh-Hans.js} +1 -1
  159. package/libs/i18n/locales/{zh-Hant.json → zh-Hant.js} +1 -1
  160. package/libs/mixin/mixin.js +14 -4
  161. package/libs/util/gcanvas/bridge/bridge-weex.js +10 -5
  162. package/package.json +4 -5
  163. package/tsconfig.json +18 -0
  164. package/types/comps/calendar.d.ts +57 -0
  165. package/types/comps/checkbox.d.ts +12 -0
  166. package/types/comps/lineProgress.d.ts +5 -0
  167. package/types/comps/navbar.d.ts +3 -3
  168. package/types/comps/swiper.d.ts +5 -0
  169. package/types/comps/tooltip.d.ts +36 -1
  170. /package/components/u-markdown/{marked.esm.js → marked.esm.mjs} +0 -0
@@ -1,423 +1,734 @@
1
- <template>
2
- <view class="u-canvas"
3
- :id="rootId"
4
- :style="{
5
- width: useRootHeightAndWidth ? '100%' : 'auto',
6
- height: useRootHeightAndWidth ? '100%' : 'auto',
7
- }">
8
- <!-- #ifdef MP || H5 -->
9
- <canvas
10
- class="u-canvas__canvas"
11
- :id="canvasId"
12
- :canvas-id="canvasId"
13
- type="2d"
14
- :style="{ width: width + unit, height: height + unit }"
15
- @touchstart="onTouchStart"
16
- @touchmove="onTouchMove"
17
- @touchend="onTouchEnd"/>
18
- <!-- #endif -->
19
-
20
- <!-- #ifdef APP-PLUS -->
21
- <canvas
22
- class="u-canvas__canvas"
23
- :id="canvasId"
24
- :canvas-id="canvasId"
25
- :style="{ width: width + unit, height: height + unit }"
26
- @touchstart="onTouchStart"
27
- @touchmove="onTouchMove"
28
- @touchend="onTouchEnd"/>
29
- <!-- #endif -->
30
-
31
- <!-- #ifdef APP-NVUE -->
32
- <gcanvas class="u-canvas__canvas" ref="gcanvess"
33
- :style="{ width: width + unit, height: height + unit }"
34
- @touchstart="onTouchStart"
35
- @touchmove="onTouchMove"
36
- @touchend="onTouchEnd">
37
- </gcanvas>
38
- <!-- #endif -->
39
- </view>
40
- </template>
41
-
42
- <script>
43
- // #ifdef APP-NVUE
44
- // https://github.com/dcloudio/NvueCanvasDemo/blob/master/README.md
45
- import {
46
- enable,
47
- WeexBridge,
48
- Image as GImage
49
- } from '../../libs/util/gcanvas/index.js';
50
- // #endif
51
- let canvasNode = null;
52
- export default {
53
- name: "u-canvas",
54
- props: {
55
- canvasId: {
56
- type: String,
57
- default: () => {
58
- return `u-canvas${Math.floor(Math.random() * 1000000)}`
59
- }
60
- },
61
- width: {
62
- type: [String, Number],
63
- default: 300
64
- },
65
- height: {
66
- type: [String, Number],
67
- default: 300
68
- },
69
- unit: {
70
- type: String,
71
- default: 'px'
72
- },
73
- useRootHeightAndWidth: {
74
- type: Boolean,
75
- default: false
76
- },
77
- // 背景色
78
- bgColor: {
79
- type: String,
80
- default: '#ffffff'
81
- }
82
- },
83
- data() {
84
- return {
85
- rootId: `rootId${Number(Math.random() * 100).toFixed(0)}`,
86
- ganvas: null,
87
- canvasContext: null,
88
- widthLocal: this.width,
89
- heightLocal: this.height,
90
- ctx: null
91
- };
92
- },
93
- computed: {
94
- // 计算实际画布尺寸
95
- actualWidth() {
96
- return this.useRootHeightAndWidth ? this.widthLocal : Number(this.width);
97
- },
98
- actualHeight() {
99
- return this.useRootHeightAndWidth ? this.heightLocal : Number(this.height);
100
- }
101
- },
102
- methods: {
103
- // 添加触摸事件处理方法
104
- onTouchStart(e) {
105
- this.$emit('touchstart', e);
106
- },
107
- onTouchMove(e) {
108
- this.$emit('touchmove', e);
109
- },
110
- onTouchEnd(e) {
111
- this.$emit('touchend', e);
112
- },
113
-
114
- /**
115
- * 获取节点
116
- * @param id 节点id
117
- * @param isCanvas 是否为Canvas节点
118
- * @return {Promise<unknown>}
119
- */
120
- async getCanvasNode(id, isCanvas = true) {
121
- let that = this
122
- return new Promise((resolve, reject) => {
123
- try {
124
- // #ifdef APP-NVUE
125
- setTimeout(() => {
126
- /*获取元素引用*/
127
- this.ganvas = this.$refs["gcanvess"]
128
- /*通过元素引用获取canvas对象*/
129
- let canvasNode = enable(this.ganvas, {
130
- bridge: WeexBridge
131
- })
132
- resolve(canvasNode)
133
- }, 200)
134
- // #endif
135
- // #ifndef APP-PLUS-NVUE
136
- const query = uni.createSelectorQuery().in(that).select(`#${id}`);
137
- query.fields({
138
- node: true,
139
- size: true
140
- })
141
- .exec((res) => {
142
- if (isCanvas) {
143
- if (res[0]?.node) {
144
- resolve(res[0].node)
145
- } else {
146
- resolve(false)
147
- console.error("获取节点出错", res)
148
- }
149
- } else {
150
- resolve(res[0])
151
- }
152
- })
153
- // #endif
154
- } catch (e) {
155
- console.error("获取节点失败", e)
156
- }
157
- })
158
- },
159
-
160
- /**
161
- * 获取Canvas上下文
162
- */
163
- getCanvasContext() {
164
- // #ifdef APP-PLUS
165
- return uni.createCanvasContext(this.canvasId, this);
166
- // #endif
167
- // #ifdef APP-PLUS-NVUE || MP || H5
168
- return canvasNode.getContext('2d');
169
- // #endif
170
- },
171
-
172
- /**
173
- * 初始化Canvas
174
- */
175
- async initCanvas() {
176
- try {
177
- canvasNode = await this.getCanvasNode(this.canvasId);
178
-
179
- // #ifdef MP-WEIXIN
180
- // 在微信小程序中,为了提高清晰度,需要考虑设备像素比
181
- const dpr = uni.getSystemInfoSync().pixelRatio;
182
- if(canvasNode) {
183
- // 设置canvas实际绘制尺寸为显示尺寸的dpr倍
184
- canvasNode.width = this.actualWidth * dpr;
185
- canvasNode.height = this.actualHeight * dpr;
186
- }
187
- // #endif
188
-
189
- this.ctx = this.getCanvasContext();
190
-
191
- // #ifdef MP-WEIXIN
192
- if(this.ctx) {
193
- this.ctx.scale(dpr, dpr);
194
- }
195
- // #endif
196
-
197
- // 初始化背景,但不在微信小程序中调用draw
198
- this.clearCanvas();
199
- } catch (error) {
200
- console.error("初始化Canvas失败:", error);
201
- }
202
- },
203
-
204
- /**
205
- * 清空画布
206
- */
207
- clearCanvas() {
208
- if (!this.ctx) return;
209
-
210
- this.clearRect(0, 0, this.actualWidth, this.actualHeight);
211
-
212
- // 填充背景色
213
- this.beginPath();
214
- this.rect(0, 0, this.actualWidth, this.actualHeight);
215
-
216
- this.setFillStyle(this.bgColor);
217
- this.fill();
218
-
219
- this.draw();
220
- },
221
- rect(x, y, width, height) {
222
- if (!this.ctx) return;
223
- this.ctx.rect(x, y, width, height);
224
- },
225
- clearRect(x, y, width, height) {
226
- if (!this.ctx) return;
227
- this.ctx.clearRect(x, y, width, height);
228
- },
229
- fill() {
230
- if (!this.ctx) return;
231
- this.ctx.fill();
232
- },
233
- setFillStyle(color) {
234
- if (!this.ctx) return;
235
-
236
- // #ifndef APP-PLUS-NVUE
237
- if (this.ctx.setFillStyle) {
238
- this.ctx.setFillStyle(color);
239
- } else {
240
- this.ctx.fillStyle = color;
241
- }
242
- // #endif
243
- // #ifdef APP-PLUS-NVUE
244
- this.ctx.setFillStyle(color);
245
- // #endif
246
- },
247
-
248
- /**
249
- * 设置线条样式
250
- */
251
- setLineStyle(lineColor, lineWidth) {
252
- if (!this.ctx) return;
253
- this.setLineCap('round');
254
- this.setLineJoin('round');
255
- this.setStrokeStyle(lineColor);
256
- this.setLineWidth(lineWidth);
257
- },
258
- setLineCap(lineCap = 'round') {
259
- if (!this.ctx) return;
260
- if (this.ctx.setLineCap) {
261
- this.ctx.setLineCap(lineCap);
262
- } else {
263
- this.ctx.lineCap = lineCap;
264
- }
265
- },
266
- setLineJoin(lineJoin = 'round') {
267
- if (!this.ctx) return;
268
- if (this.ctx.setLineJoin) {
269
- this.ctx.setLineJoin(lineJoin);
270
- } else {
271
- this.ctx.lineJoin = lineJoin;
272
- }
273
- },
274
- setStrokeStyle(color) {
275
- if (!this.ctx) return;
276
- if (this.ctx.setStrokeStyle) {
277
- this.ctx.setStrokeStyle(color);
278
- } else {
279
- this.ctx.strokeStyle = color;
280
- }
281
- },
282
- setLineWidth(width) {
283
- if (!this.ctx) return;
284
- if (this.ctx.setLineWidth) {
285
- this.ctx.setLineWidth(width);
286
- } else {
287
- this.ctx.lineWidth = width;
288
- }
289
- },
290
-
291
- /**
292
- * 开始路径
293
- */
294
- beginPath() {
295
- if (!this.ctx) return;
296
- this.ctx.beginPath();
297
- },
298
-
299
- /**
300
- * 移动到某点
301
- */
302
- moveTo(x, y) {
303
- if (!this.ctx) return;
304
- this.ctx.moveTo(x, y);
305
- },
306
-
307
- /**
308
- * 画线到某点
309
- */
310
- lineTo(x, y) {
311
- if (!this.ctx) return;
312
- this.ctx.lineTo(x, y);
313
- },
314
-
315
- /**
316
- * 描边
317
- */
318
- stroke() {
319
- if (!this.ctx) return;
320
- this.ctx.stroke();
321
- },
322
-
323
- /**
324
- * 关闭路径
325
- */
326
- closePath() {
327
- if (!this.ctx) return;
328
- this.ctx.closePath();
329
- },
330
-
331
- /**
332
- * 绘制操作
333
- */
334
- draw(isLastDraw = false) {
335
- // #ifndef MP-WEIXIN
336
- if (this.ctx && typeof this.ctx.draw === 'function') {
337
- this.ctx.draw(isLastDraw);
338
- }
339
- // #endif
340
- },
341
-
342
- /**
343
- * 导出图片
344
- */
345
- exportImage(fileType = 'png', quality = 1) {
346
- return new Promise((resolve, reject) => {
347
- // #ifdef MP-WEIXIN
348
- // 微信小程序中需要先完成绘制,然后导出图片
349
- setTimeout(() => {
350
- uni.canvasToTempFilePath({
351
- x: 0,
352
- y: 0,
353
- width: this.actualWidth,
354
- height: this.actualHeight,
355
- destWidth: this.actualWidth * 2, // 使用双倍尺寸以提高清晰度
356
- destHeight: this.actualHeight * 2,
357
- canvas: canvasNode, // 2d必须
358
- canvasId: this.canvasId,
359
- fileType: fileType,
360
- quality: quality,
361
- success: (res) => {
362
- resolve(res.tempFilePath);
363
- },
364
- fail: (err) => {
365
- console.error('导出图片失败:', err);
366
- reject(err);
367
- }
368
- }, this);
369
- }, 50); // 等待50毫秒确保绘制完成
370
- // #endif
371
-
372
- // #ifndef MP-WEIXIN
373
- uni.canvasToTempFilePath({
374
- canvas: canvasNode, // 2d必须
375
- canvasId: this.canvasId,
376
- fileType: fileType,
377
- quality: quality,
378
- success: (res) => {
379
- resolve(res.tempFilePath);
380
- },
381
- fail: (err) => {
382
- console.error('导出图片失败:', err);
383
- reject(err);
384
- }
385
- }, this);
386
- // #endif
387
- });
388
- },
389
-
390
- /**
391
- * 使用根节点宽高 设置新的size
392
- * @return {Promise<void>}
393
- */
394
- async setNewSize(){
395
- const rootNode = await this.getCanvasNode(this.rootId, false);
396
- const { width , height } = rootNode;
397
- this.widthLocal = height;
398
- this.heightLocal = width;
399
- }
400
- },
401
-
402
- async mounted() {
403
- // 如果使用根节点的宽高 则 重新设置 size
404
- if(this.useRootHeightAndWidth){
405
- await this.setNewSize();
406
- }
407
-
408
- // 初始化Canvas
409
- await this.initCanvas();
410
- }
411
- };
412
- </script>
413
-
414
- <style lang="scss" scoped>
415
- .u-canvas {
416
- position: relative;
417
- overflow: hidden;
418
- }
419
-
420
- .u-canvas__canvas {
421
- display: block;
422
- }
423
- </style>
1
+ <template>
2
+ <view
3
+ class="u-canvas"
4
+ :id="rootId"
5
+ :style="{
6
+ width: useRootHeightAndWidth ? '100%' : actualWidth + unit,
7
+ height: useRootHeightAndWidth ? '100%' : actualHeight + unit
8
+ }"
9
+ >
10
+ <!-- #ifdef MP || H5 -->
11
+ <canvas
12
+ class="u-canvas__canvas"
13
+ :id="canvasId"
14
+ :canvas-id="canvasId"
15
+ type="2d"
16
+ :disable-scroll="disableScroll"
17
+ :style="{ width: actualWidth + unit, height: actualHeight + unit }"
18
+ @touchstart="onTouchStart"
19
+ @touchmove="onTouchMove"
20
+ @touchend="onTouchEnd"
21
+ />
22
+ <!-- #endif -->
23
+
24
+ <!-- #ifdef APP-PLUS -->
25
+ <canvas
26
+ class="u-canvas__canvas"
27
+ :id="canvasId"
28
+ :canvas-id="canvasId"
29
+ :disable-scroll="disableScroll"
30
+ :style="{ width: actualWidth + unit, height: actualHeight + unit }"
31
+ @touchstart="onTouchStart"
32
+ @touchmove="onTouchMove"
33
+ @touchend="onTouchEnd"
34
+ />
35
+ <!-- #endif -->
36
+
37
+ <!-- #ifdef APP-NVUE -->
38
+ <gcanvas
39
+ class="u-canvas__canvas"
40
+ ref="gcanvas"
41
+ :style="{ width: actualWidth + unit, height: actualHeight + unit }"
42
+ @touchstart="onTouchStart"
43
+ @touchmove="onTouchMove"
44
+ @touchend="onTouchEnd"
45
+ />
46
+ <!-- #endif -->
47
+ </view>
48
+ </template>
49
+
50
+ <script>
51
+ // #ifdef APP-NVUE
52
+ import {
53
+ enable,
54
+ WeexBridge,
55
+ Image as GImage
56
+ } from '../../libs/util/gcanvas/index.js';
57
+ // #endif
58
+
59
+ export default {
60
+ name: 'u-canvas',
61
+ emits: ['ready', 'touchstart', 'touchmove', 'touchend'],
62
+ props: {
63
+ canvasId: {
64
+ type: String,
65
+ default: () => `u-canvas${Math.floor(Math.random() * 1000000)}`
66
+ },
67
+ width: {
68
+ type: [String, Number],
69
+ default: 300
70
+ },
71
+ height: {
72
+ type: [String, Number],
73
+ default: 300
74
+ },
75
+ unit: {
76
+ type: String,
77
+ default: 'px'
78
+ },
79
+ useRootHeightAndWidth: {
80
+ type: Boolean,
81
+ default: false
82
+ },
83
+ bgColor: {
84
+ type: String,
85
+ default: '#ffffff'
86
+ },
87
+ disableScroll: {
88
+ type: Boolean,
89
+ default: false
90
+ }
91
+ },
92
+ data() {
93
+ return {
94
+ rootId: `u-canvas-root-${Math.floor(Math.random() * 1000000)}`,
95
+ ctx: null,
96
+ widthLocal: this.parseSize(this.width),
97
+ heightLocal: this.parseSize(this.height),
98
+ fontSize: 12,
99
+ fontFamily: 'sans-serif',
100
+ fontWeight: 'normal',
101
+ dpr: 1
102
+ };
103
+ },
104
+ computed: {
105
+ actualWidth() {
106
+ return this.useRootHeightAndWidth ? this.widthLocal : this.parseSize(this.width);
107
+ },
108
+ actualHeight() {
109
+ return this.useRootHeightAndWidth ? this.heightLocal : this.parseSize(this.height);
110
+ }
111
+ },
112
+ watch: {
113
+ width() {
114
+ this.refresh();
115
+ },
116
+ height() {
117
+ this.refresh();
118
+ },
119
+ bgColor() {
120
+ this.clearCanvas();
121
+ }
122
+ },
123
+ created() {
124
+ this._canvasNode = null;
125
+ this._selectorResult = null;
126
+ this._canvasElement = null;
127
+ this._imageCache = Object.create(null);
128
+ this._isNvue = false;
129
+ },
130
+ mounted() {
131
+ this.$nextTick(() => {
132
+ this.initCanvas();
133
+ });
134
+ },
135
+ methods: {
136
+ parseSize(value) {
137
+ if (typeof value === 'number') {
138
+ return value;
139
+ }
140
+ if (typeof value !== 'string') {
141
+ return 0;
142
+ }
143
+ if (value.endsWith('rpx') || value.endsWith('upx')) {
144
+ return uni.upx2px(parseFloat(value));
145
+ }
146
+ if (value.endsWith('px')) {
147
+ return parseFloat(value) || 0;
148
+ }
149
+ return parseFloat(value) || 0;
150
+ },
151
+ onTouchStart(event) {
152
+ this.$emit('touchstart', event);
153
+ },
154
+ onTouchMove(event) {
155
+ this.$emit('touchmove', event);
156
+ },
157
+ onTouchEnd(event) {
158
+ this.$emit('touchend', event);
159
+ },
160
+ async getCanvasNode(id = this.canvasId, isCanvas = true) {
161
+ return new Promise((resolve) => {
162
+ try {
163
+ // #ifdef APP-NVUE
164
+ setTimeout(() => {
165
+ const gcanvas = this.$refs.gcanvas;
166
+ if (!gcanvas) {
167
+ resolve(false);
168
+ return;
169
+ }
170
+ this._isNvue = true;
171
+ resolve(enable(gcanvas, { bridge: WeexBridge }));
172
+ }, 100);
173
+ // #endif
174
+
175
+ // #ifndef APP-NVUE
176
+ uni.createSelectorQuery()
177
+ .in(this)
178
+ .select(`#${id}`)
179
+ .fields(
180
+ {
181
+ node: isCanvas,
182
+ size: true
183
+ },
184
+ (res) => {
185
+ resolve(res || false);
186
+ }
187
+ )
188
+ .exec();
189
+ // #endif
190
+ } catch (error) {
191
+ console.error('获取画布节点失败:', error);
192
+ resolve(false);
193
+ }
194
+ });
195
+ },
196
+ getCanvasElement() {
197
+ return this._canvasElement || null;
198
+ },
199
+ getRawContext() {
200
+ return this.ctx;
201
+ },
202
+ getCanvasContext() {
203
+ // #ifdef APP-PLUS
204
+ return uni.createCanvasContext(this.canvasId, this);
205
+ // #endif
206
+
207
+ // #ifdef APP-NVUE
208
+ return this._canvasElement && typeof this._canvasElement.getContext === 'function'
209
+ ? this._canvasElement.getContext('2d')
210
+ : null;
211
+ // #endif
212
+
213
+ // #ifdef MP || H5
214
+ return this._canvasElement && typeof this._canvasElement.getContext === 'function'
215
+ ? this._canvasElement.getContext('2d')
216
+ : null;
217
+ // #endif
218
+ },
219
+ async setNewSize() {
220
+ const rootNode = await this.getCanvasNode(this.rootId, false);
221
+ if (!rootNode) {
222
+ return;
223
+ }
224
+ if (rootNode.width) {
225
+ this.widthLocal = rootNode.width;
226
+ }
227
+ if (rootNode.height) {
228
+ this.heightLocal = rootNode.height;
229
+ }
230
+ },
231
+ async initCanvas(force = false) {
232
+ try {
233
+ if (this.useRootHeightAndWidth) {
234
+ await this.setNewSize();
235
+ }
236
+ if (this.ctx && !force) {
237
+ this.$emit('ready', {
238
+ width: this.actualWidth,
239
+ height: this.actualHeight
240
+ });
241
+ return true;
242
+ }
243
+
244
+ this._canvasNode = await this.getCanvasNode(this.canvasId);
245
+ if (!this._canvasNode) {
246
+ return false;
247
+ }
248
+
249
+ this._selectorResult = this._canvasNode;
250
+ this._canvasElement = this._canvasNode.node || this._canvasNode;
251
+ this.dpr = uni.getSystemInfoSync().pixelRatio || 1;
252
+
253
+ // #ifdef MP || H5
254
+ if (this._canvasElement) {
255
+ this._canvasElement.width = Math.ceil(this.actualWidth * this.dpr);
256
+ this._canvasElement.height = Math.ceil(this.actualHeight * this.dpr);
257
+ }
258
+ // #endif
259
+
260
+ this.ctx = this.getCanvasContext();
261
+ if (!this.ctx) {
262
+ return false;
263
+ }
264
+
265
+ // #ifdef MP || H5
266
+ if (typeof this.ctx.setTransform === 'function') {
267
+ this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
268
+ } else if (typeof this.ctx.scale === 'function') {
269
+ this.ctx.scale(this.dpr, this.dpr);
270
+ }
271
+ // #endif
272
+
273
+ this.applyFont();
274
+ this.clearCanvas();
275
+ this.$emit('ready', {
276
+ width: this.actualWidth,
277
+ height: this.actualHeight
278
+ });
279
+ return true;
280
+ } catch (error) {
281
+ console.error('初始化Canvas失败:', error);
282
+ return false;
283
+ }
284
+ },
285
+ refresh() {
286
+ return this.initCanvas(true);
287
+ },
288
+ getWidth() {
289
+ return this.actualWidth;
290
+ },
291
+ getHeight() {
292
+ return this.actualHeight;
293
+ },
294
+ clearCanvas() {
295
+ if (!this.ctx) return;
296
+ this.clearRect(0, 0, this.actualWidth, this.actualHeight);
297
+ if (this.bgColor && this.bgColor !== 'transparent') {
298
+ this.beginPath();
299
+ this.rect(0, 0, this.actualWidth, this.actualHeight);
300
+ this.setFillStyle(this.bgColor);
301
+ this.fill();
302
+ }
303
+ this.draw();
304
+ },
305
+ callContext(method, ...args) {
306
+ if (this.ctx && typeof this.ctx[method] === 'function') {
307
+ return this.ctx[method](...args);
308
+ }
309
+ return undefined;
310
+ },
311
+ rect(x, y, width, height) {
312
+ return this.callContext('rect', x, y, width, height);
313
+ },
314
+ clearRect(x, y, width, height) {
315
+ return this.callContext('clearRect', x, y, width, height);
316
+ },
317
+ fillRect(x, y, width, height) {
318
+ return this.callContext('fillRect', x, y, width, height);
319
+ },
320
+ strokeRect(x, y, width, height) {
321
+ return this.callContext('strokeRect', x, y, width, height);
322
+ },
323
+ fill() {
324
+ return this.callContext('fill');
325
+ },
326
+ stroke() {
327
+ return this.callContext('stroke');
328
+ },
329
+ beginPath() {
330
+ return this.callContext('beginPath');
331
+ },
332
+ closePath() {
333
+ return this.callContext('closePath');
334
+ },
335
+ moveTo(x, y) {
336
+ return this.callContext('moveTo', x, y);
337
+ },
338
+ lineTo(x, y) {
339
+ return this.callContext('lineTo', x, y);
340
+ },
341
+ arc(x, y, radius, startAngle, endAngle, anticlockwise = false) {
342
+ return this.callContext('arc', x, y, radius, startAngle, endAngle, anticlockwise);
343
+ },
344
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
345
+ return this.callContext('bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y);
346
+ },
347
+ quadraticCurveTo(cpx, cpy, x, y) {
348
+ return this.callContext('quadraticCurveTo', cpx, cpy, x, y);
349
+ },
350
+ clip() {
351
+ return this.callContext('clip');
352
+ },
353
+ save() {
354
+ return this.callContext('save');
355
+ },
356
+ restore() {
357
+ return this.callContext('restore');
358
+ },
359
+ translate(x, y) {
360
+ return this.callContext('translate', x, y);
361
+ },
362
+ rotate(angle) {
363
+ return this.callContext('rotate', angle);
364
+ },
365
+ scale(x, y) {
366
+ return this.callContext('scale', x, y);
367
+ },
368
+ setFillStyle(color) {
369
+ if (!this.ctx) return;
370
+ if (typeof this.ctx.setFillStyle === 'function') {
371
+ this.ctx.setFillStyle(color);
372
+ } else {
373
+ this.ctx.fillStyle = color;
374
+ }
375
+ },
376
+ setStrokeStyle(color) {
377
+ if (!this.ctx) return;
378
+ if (typeof this.ctx.setStrokeStyle === 'function') {
379
+ this.ctx.setStrokeStyle(color);
380
+ } else {
381
+ this.ctx.strokeStyle = color;
382
+ }
383
+ },
384
+ setLineWidth(width) {
385
+ if (!this.ctx) return;
386
+ if (typeof this.ctx.setLineWidth === 'function') {
387
+ this.ctx.setLineWidth(width);
388
+ } else {
389
+ this.ctx.lineWidth = width;
390
+ }
391
+ },
392
+ setLineCap(lineCap = 'round') {
393
+ if (!this.ctx) return;
394
+ if (typeof this.ctx.setLineCap === 'function') {
395
+ this.ctx.setLineCap(lineCap);
396
+ } else {
397
+ this.ctx.lineCap = lineCap;
398
+ }
399
+ },
400
+ setLineJoin(lineJoin = 'round') {
401
+ if (!this.ctx) return;
402
+ if (typeof this.ctx.setLineJoin === 'function') {
403
+ this.ctx.setLineJoin(lineJoin);
404
+ } else {
405
+ this.ctx.lineJoin = lineJoin;
406
+ }
407
+ },
408
+ setTextAlign(align = 'left') {
409
+ if (!this.ctx) return;
410
+ if (typeof this.ctx.setTextAlign === 'function') {
411
+ this.ctx.setTextAlign(align);
412
+ } else {
413
+ this.ctx.textAlign = align;
414
+ }
415
+ },
416
+ setTextBaseline(baseline = 'alphabetic') {
417
+ if (!this.ctx) return;
418
+ if (typeof this.ctx.setTextBaseline === 'function') {
419
+ this.ctx.setTextBaseline(baseline);
420
+ } else {
421
+ this.ctx.textBaseline = baseline;
422
+ }
423
+ },
424
+ setFontSize(fontSize) {
425
+ this.fontSize = Number(fontSize) || this.fontSize;
426
+ if (this.ctx && typeof this.ctx.setFontSize === 'function') {
427
+ this.ctx.setFontSize(this.fontSize);
428
+ }
429
+ this.applyFont();
430
+ },
431
+ setFont(font) {
432
+ if (!this.ctx) return;
433
+ if ('font' in this.ctx) {
434
+ this.ctx.font = font;
435
+ return;
436
+ }
437
+ const matched = String(font).match(/(\d+(?:\.\d+)?)px/);
438
+ if (matched) {
439
+ this.fontSize = Number(matched[1]);
440
+ }
441
+ this.applyFont();
442
+ },
443
+ setGlobalAlpha(alpha) {
444
+ if (!this.ctx) return;
445
+ if (typeof this.ctx.setGlobalAlpha === 'function') {
446
+ this.ctx.setGlobalAlpha(alpha);
447
+ } else {
448
+ this.ctx.globalAlpha = alpha;
449
+ }
450
+ },
451
+ setShadow(offsetX = 0, offsetY = 0, blur = 0, color = 'rgba(0,0,0,0)') {
452
+ if (!this.ctx) return;
453
+ if (typeof this.ctx.setShadow === 'function') {
454
+ this.ctx.setShadow(offsetX, offsetY, blur, color);
455
+ return;
456
+ }
457
+ this.ctx.shadowOffsetX = offsetX;
458
+ this.ctx.shadowOffsetY = offsetY;
459
+ this.ctx.shadowBlur = blur;
460
+ this.ctx.shadowColor = color;
461
+ },
462
+ setLineStyle(lineColor, lineWidth) {
463
+ this.setLineCap('round');
464
+ this.setLineJoin('round');
465
+ this.setStrokeStyle(lineColor);
466
+ this.setLineWidth(lineWidth);
467
+ },
468
+ applyFont() {
469
+ if (!this.ctx) return;
470
+ const font = `${this.fontWeight === 'normal' ? '' : `${this.fontWeight} `}${this.fontSize}px ${this.fontFamily}`.trim();
471
+ if ('font' in this.ctx) {
472
+ this.ctx.font = font;
473
+ } else if (typeof this.ctx.setFont === 'function') {
474
+ this.ctx.setFont(font);
475
+ } else if (typeof this.ctx.setFontSize === 'function') {
476
+ this.ctx.setFontSize(this.fontSize);
477
+ }
478
+ },
479
+ fillText(text, x, y) {
480
+ return this.callContext('fillText', String(text), x, y);
481
+ },
482
+ measureText(text) {
483
+ if (this.ctx && typeof this.ctx.measureText === 'function') {
484
+ return this.ctx.measureText(String(text));
485
+ }
486
+ return {
487
+ width: String(text).length * this.fontSize * 0.6
488
+ };
489
+ },
490
+ createLinearGradient(x0, y0, x1, y1) {
491
+ if (this.ctx && typeof this.ctx.createLinearGradient === 'function') {
492
+ return this.ctx.createLinearGradient(x0, y0, x1, y1);
493
+ }
494
+ return null;
495
+ },
496
+ createRadialGradient(x0, y0, r0, x1, y1, r1) {
497
+ if (this.ctx && typeof this.ctx.createRadialGradient === 'function') {
498
+ return this.ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
499
+ }
500
+ return null;
501
+ },
502
+ loadImage(src) {
503
+ if (this._imageCache[src]) {
504
+ return Promise.resolve(this._imageCache[src]);
505
+ }
506
+ return new Promise((resolve, reject) => {
507
+ let image = null;
508
+
509
+ // #ifdef APP-NVUE
510
+ image = new GImage();
511
+ // #endif
512
+
513
+ // #ifdef MP
514
+ const canvas = this.getCanvasElement();
515
+ if (canvas && typeof canvas.createImage === 'function') {
516
+ image = canvas.createImage();
517
+ }
518
+ // #endif
519
+
520
+ // #ifdef H5
521
+ image = new Image();
522
+ image.crossOrigin = 'anonymous';
523
+ // #endif
524
+
525
+ if (!image) {
526
+ resolve(src);
527
+ return;
528
+ }
529
+
530
+ image.onload = () => {
531
+ this._imageCache[src] = image;
532
+ resolve(image);
533
+ };
534
+ image.onerror = reject;
535
+ image.src = src;
536
+ });
537
+ },
538
+ async drawImage(source, ...args) {
539
+ if (!this.ctx || typeof this.ctx.drawImage !== 'function') {
540
+ return false;
541
+ }
542
+ if (typeof source !== 'string' || (typeof this.ctx.setFillStyle === 'function' && !this._isNvue)) {
543
+ this.ctx.drawImage(source, ...args);
544
+ return true;
545
+ }
546
+ const image = await this.loadImage(source);
547
+ this.ctx.drawImage(image, ...args);
548
+ return true;
549
+ },
550
+ draw(isLastDraw = false, callback) {
551
+ if (this.ctx && typeof this.ctx.draw === 'function') {
552
+ return this.ctx.draw(isLastDraw, callback);
553
+ }
554
+ if (typeof callback === 'function') {
555
+ setTimeout(callback, 0);
556
+ }
557
+ return undefined;
558
+ },
559
+ toTempFilePath(options = {}) {
560
+ return new Promise((resolve, reject) => {
561
+ const width = options.width || this.actualWidth;
562
+ const height = options.height || this.actualHeight;
563
+ const request = {
564
+ x: options.x || 0,
565
+ y: options.y || 0,
566
+ width,
567
+ height,
568
+ destWidth: options.destWidth || width,
569
+ destHeight: options.destHeight || height,
570
+ fileType: options.fileType || 'png',
571
+ quality: options.quality === undefined ? 1 : options.quality
572
+ };
573
+ const success = (res) => {
574
+ if (typeof options.success === 'function') options.success(res);
575
+ resolve(res);
576
+ };
577
+ const fail = (err) => {
578
+ if (typeof options.fail === 'function') options.fail(err);
579
+ reject(err);
580
+ };
581
+ const complete = (res) => {
582
+ if (typeof options.complete === 'function') options.complete(res);
583
+ };
584
+
585
+ // #ifdef H5
586
+ const canvas = this.getCanvasElement() || (this.ctx && this.ctx.canvas);
587
+ if (canvas && typeof canvas.toDataURL === 'function') {
588
+ try {
589
+ let exportCanvas = canvas;
590
+ if (
591
+ request.x !== 0 ||
592
+ request.y !== 0 ||
593
+ request.width !== this.actualWidth ||
594
+ request.height !== this.actualHeight ||
595
+ request.destWidth !== request.width ||
596
+ request.destHeight !== request.height
597
+ ) {
598
+ exportCanvas = document.createElement('canvas');
599
+ exportCanvas.width = request.destWidth;
600
+ exportCanvas.height = request.destHeight;
601
+ const exportCtx = exportCanvas.getContext('2d');
602
+ exportCtx.drawImage(
603
+ canvas,
604
+ request.x * this.dpr,
605
+ request.y * this.dpr,
606
+ request.width * this.dpr,
607
+ request.height * this.dpr,
608
+ 0,
609
+ 0,
610
+ request.destWidth,
611
+ request.destHeight
612
+ );
613
+ }
614
+ const mime = request.fileType === 'jpg' || request.fileType === 'jpeg'
615
+ ? 'image/jpeg'
616
+ : 'image/png';
617
+ const res = {
618
+ tempFilePath: exportCanvas.toDataURL(mime, request.quality)
619
+ };
620
+ success(res);
621
+ complete(res);
622
+ return;
623
+ } catch (error) {
624
+ fail(error);
625
+ complete(error);
626
+ return;
627
+ }
628
+ }
629
+ // #endif
630
+
631
+ // #ifdef APP-NVUE
632
+ if (this.ctx && typeof this.ctx.toTempFilePath === 'function') {
633
+ this.ctx.toTempFilePath(
634
+ request.x,
635
+ request.y,
636
+ request.width,
637
+ request.height,
638
+ request.destWidth,
639
+ request.destHeight,
640
+ request.fileType,
641
+ request.quality,
642
+ (res) => {
643
+ success(res);
644
+ complete(res);
645
+ }
646
+ );
647
+ return;
648
+ }
649
+ // #endif
650
+
651
+ const canvasNode = this.getCanvasElement();
652
+ const uniOptions = {
653
+ ...request,
654
+ canvasId: this.canvasId,
655
+ success,
656
+ fail,
657
+ complete
658
+ };
659
+ if (canvasNode) {
660
+ uniOptions.canvas = canvasNode;
661
+ }
662
+ uni.canvasToTempFilePath(uniOptions, this);
663
+ });
664
+ },
665
+ async exportImage(fileType = 'png', quality = 1) {
666
+ const res = await this.toTempFilePath({
667
+ fileType,
668
+ quality,
669
+ width: this.actualWidth,
670
+ height: this.actualHeight,
671
+ destWidth: this.actualWidth,
672
+ destHeight: this.actualHeight
673
+ });
674
+ return res.tempFilePath || res.apFilePath;
675
+ },
676
+ getImageData(options = {}) {
677
+ return new Promise((resolve, reject) => {
678
+ const request = {
679
+ canvasId: this.canvasId,
680
+ x: options.x || 0,
681
+ y: options.y || 0,
682
+ width: options.width || this.actualWidth,
683
+ height: options.height || this.actualHeight,
684
+ success: (res) => {
685
+ if (typeof options.success === 'function') options.success(res);
686
+ resolve(res);
687
+ },
688
+ fail: (err) => {
689
+ if (typeof options.fail === 'function') options.fail(err);
690
+ reject(err);
691
+ },
692
+ complete: options.complete
693
+ };
694
+ uni.canvasGetImageData(request, this);
695
+ });
696
+ },
697
+ putImageData(options = {}) {
698
+ return new Promise((resolve, reject) => {
699
+ const request = {
700
+ canvasId: this.canvasId,
701
+ x: options.x || 0,
702
+ y: options.y || 0,
703
+ width: options.width || this.actualWidth,
704
+ height: options.height || this.actualHeight,
705
+ data: options.data,
706
+ success: (res) => {
707
+ if (typeof options.success === 'function') options.success(res);
708
+ resolve(res);
709
+ },
710
+ fail: (err) => {
711
+ if (typeof options.fail === 'function') options.fail(err);
712
+ reject(err);
713
+ },
714
+ complete: options.complete
715
+ };
716
+ uni.canvasPutImageData(request, this);
717
+ });
718
+ }
719
+ }
720
+ };
721
+ </script>
722
+
723
+ <style lang="scss" scoped>
724
+ .u-canvas {
725
+ position: relative;
726
+ overflow: hidden;
727
+ }
728
+
729
+ .u-canvas__canvas {
730
+ display: block;
731
+ width: 100%;
732
+ height: 100%;
733
+ }
734
+ </style>