uview-ultra 4.5.71 → 4.5.75

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 (41) hide show
  1. package/changelog.md +1704 -1635
  2. package/components/up-cropper/up-cropper.vue +1426 -1392
  3. package/components/up-form/up-form.uvue +281 -276
  4. package/components/up-form/up-form.vue +14 -6
  5. package/components/up-form-item/up-form-item.uvue +14 -1
  6. package/components/up-form-item/up-form-item.vue +14 -2
  7. package/components/up-input/up-input.uvue +4 -0
  8. package/components/up-input/up-input.vue +6 -1
  9. package/components/up-lottie/libs/LICENSE-lottie-miniprogram +21 -0
  10. package/components/up-lottie/libs/LICENSE-lottie-web +21 -0
  11. package/components/up-lottie/libs/gen-uvue-variant.py +47 -0
  12. package/components/up-lottie/libs/harmony-canvas-adapter.js +394 -0
  13. package/components/up-lottie/libs/lottie-miniprogram.min.js +14 -0
  14. package/components/up-lottie/libs/lottie-miniprogram.uvue.js +32 -0
  15. package/components/up-lottie/libs/lottie_svg.min.js +6 -0
  16. package/components/up-lottie/libs/wx-env.js +15 -0
  17. package/components/up-lottie/lottie.js +14 -0
  18. package/components/up-lottie/lottie.uts +14 -0
  19. package/components/up-lottie/props.js +32 -0
  20. package/components/up-lottie/up-lottie.uvue +500 -0
  21. package/components/up-lottie/up-lottie.vue +652 -0
  22. package/components/up-number-keyboard/up-number-keyboard.uvue +7 -1
  23. package/components/up-number-keyboard/up-number-keyboard.vue +8 -1
  24. package/components/up-root/root.js +1 -1
  25. package/components/up-select/up-select.uvue +328 -314
  26. package/components/up-select/up-select.vue +19 -2
  27. package/components/up-sticky/up-sticky.vue +4 -1
  28. package/components/up-upload/props.js +5 -0
  29. package/components/up-upload/props.uts +5 -0
  30. package/components/up-upload/types.uts +1 -0
  31. package/components/up-upload/up-upload.uvue +9 -0
  32. package/components/up-upload/up-upload.vue +1 -0
  33. package/components/up-upload/upload.js +2 -0
  34. package/components/up-upload/upload.uts +2 -0
  35. package/components/up-upload/utils.js +23 -12
  36. package/components/up-upload/utils.uts +4 -0
  37. package/libs/config/props.js +2 -0
  38. package/libs/vite/app-icon-font.js +9 -5
  39. package/package.json +3 -2
  40. package/types/comps/lottie.d.ts +34 -0
  41. package/types/comps/upload.d.ts +5 -0
@@ -67,7 +67,7 @@
67
67
  >{{ message }}</text>
68
68
  </slot>
69
69
  <up-line
70
- v-if="borderBottom"
70
+ v-if="resolvedBorderBottom"
71
71
  :color="message != '' && parentData['errorType'] == 'border-bottom' ? color['error'] : propsLine['color']"
72
72
  :customStyle="`margin-top: ${message != '' && parentData['errorType'] == 'message' ? '5px' : 0}`"
73
73
  ></up-line>
@@ -93,6 +93,8 @@
93
93
  labelWidth: 45,
94
94
  // 错误提示方式
95
95
  errorType: 'message',
96
+ // 是否显示下划线边框,未在up-form-item上显式设置时跟随up-form的同名属性
97
+ borderBottom: true,
96
98
  originalModel: {}
97
99
  })
98
100
  const instance = getCurrentInstance()!.proxy!
@@ -178,6 +180,17 @@
178
180
  const message = ref('')
179
181
  const itemRules = ref<UTSJSONObject[]|UTSJSONObject>({})
180
182
 
183
+ // 是否显示下划线边框:未在up-form-item上显式设置(默认空串)时,跟随up-form的同名属性。
184
+ // 此前 form-item 只读自己的 borderBottom,导致 up-form 上配的 borderBottom 完全不生效
185
+ const resolvedBorderBottom = computed((): boolean => {
186
+ const own: any = props.borderBottom
187
+ if (own == null || (typeof own == 'string' && (own as string) == '')) {
188
+ const fromParent: any = parentData.value['borderBottom']
189
+ return fromParent == null ? true : fromParent as boolean
190
+ }
191
+ return own as boolean
192
+ })
193
+
181
194
  // 获取父组件的参数
182
195
  const updateParentData = () => {
183
196
  getParentData('up-form', instance, false);
@@ -67,7 +67,7 @@
67
67
  >{{ message }}</text>
68
68
  </slot>
69
69
  <u-line
70
- v-if="borderBottom"
70
+ v-if="resolvedBorderBottom"
71
71
  :color="message && parentData['errorType'] === 'border-bottom' ? color['error'] : propsLine['color']"
72
72
  :customStyle="`margin-top: ${message && parentData['errorType'] === 'message' ? '5px' : 0}`"
73
73
  ></u-line>
@@ -121,7 +121,9 @@
121
121
  // 提示文本的宽度
122
122
  labelWidth: 45,
123
123
  // 错误提示方式
124
- errorType: 'message'
124
+ errorType: 'message',
125
+ // 是否显示下划线边框,未在up-form-item上显式设置时跟随up-form的同名属性
126
+ borderBottom: true
125
127
  })
126
128
  const { parent, getParentData } = useUltraUI(props, parentData)
127
129
  // 错误提示语
@@ -134,6 +136,16 @@
134
136
  return defProps.line
135
137
  })
136
138
 
139
+ // 是否显示下划线边框:未在up-form-item上显式设置(默认空串)时,跟随up-form的同名属性。
140
+ // 此前 form-item 只读自己的 borderBottom,导致 up-form 上配的 borderBottom 完全不生效
141
+ const resolvedBorderBottom = computed(() => {
142
+ const own = props.borderBottom
143
+ if (own === '' || own === undefined || own === null) {
144
+ return parentData.borderBottom
145
+ }
146
+ return own
147
+ })
148
+
137
149
  function init() {
138
150
  // 父组件的实例
139
151
  updateParentData()
@@ -340,6 +340,10 @@ watch((): any => props.modelValue, (newVal: any, oldVal: string|number) => {
340
340
  if ((changeFromInner.value && !isBlurFormattedValue) || innerValue.value == newVal) {
341
341
  changeFromInner.value = false // 重要否则会出现双向绑定失效问题https://github.com/ijry/uview-plus/issues/419
342
342
  blurValue.value = null
343
+ // immediate 首次执行也会走这里,必须把 firstChange 置为 false,
344
+ // 否则首次外部赋值(如弹窗选择回填)会因 firstChange 仍为 true
345
+ // 而跳过 valueChange 与表单验证
346
+ firstChange.value = false
343
347
  return
344
348
  }
345
349
  innerValue.value = newVal
@@ -167,6 +167,10 @@ watch(() => props.modelValue, (newVal) => {
167
167
  if ((changeFromInner.value && !isBlurFormattedValue) || innerValue.value === newVal) {
168
168
  changeFromInner.value = false // 重要否则会出现双向绑定失效问题https://github.com/ijry/uview-plus/issues/419
169
169
  blurValue.value = null
170
+ // immediate 首次执行也会走这里,必须把 firstChange 置为 false,
171
+ // 否则首次外部赋值(如弹窗选择回填)会因 firstChange 仍为 true
172
+ // 而跳过 valueChange 与表单验证
173
+ firstChange.value = false
170
174
  return
171
175
  }
172
176
  innerValue.value = newVal
@@ -282,7 +286,8 @@ function doFocus() {
282
286
 
283
287
  function doBlur() {
284
288
  const inputNative = proxy?.$refs?.['input-native']
285
- inputNative && inputNative.blur()
289
+ // 部分平台(如小程序端)取不到原生节点引用,做防御性调用避免直接抛错
290
+ inputNative && typeof inputNative.blur === 'function' && inputNative.blur()
286
291
  }
287
292
 
288
293
  // 点击完成按钮时触发
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 wechat-miniprogram
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Bodymovin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ """生成 uvue 专用的 lottie-miniprogram 副本。
3
+
4
+ lottie-miniprogram 在**模块加载期**就会执行 `var b = wx.getSystemInfoSync()`,
5
+ 而 uni-app x 没有 wx 全局,直接 import 会在启动时抛 ReferenceError。
6
+ 这里在文件最前面补一个模块作用域的 wx 桩,后续压缩代码里的 `wx` 引用
7
+ 会解析到这个局部变量(比挂 globalThis 更可靠,不依赖运行时全局对象)。
8
+
9
+ 只补 getSystemInfoSync / request 两项:
10
+ - getSystemInfoSync:lottie 只用来取 pixelRatio(dpr 我们显式传 1)
11
+ - request:仅在按 path 加载远程 JSON 时使用,本组件只用 animationData
12
+
13
+ 原始文件保持不动,供 .vue 侧(微信小程序/鸿蒙,有真实 wx)使用。
14
+ """
15
+ import io
16
+
17
+ SRC = 'lottie-miniprogram.min.js'
18
+ DST = 'lottie-miniprogram.uvue.js'
19
+
20
+ SHIM = """/* ------------------------------------------------------------------
21
+ * uni-app x 适配层(本文件由 lottie-miniprogram.min.js 自动生成)
22
+ * lottie-miniprogram 在模块加载期即调用 wx.getSystemInfoSync(),
23
+ * 而 uni-app x 没有 wx 全局,故在此补一个模块作用域的 wx 桩。
24
+ * 下方代码与 lottie-miniprogram.min.js 完全一致,未做任何修改。
25
+ * ------------------------------------------------------------------ */
26
+ var wx = {
27
+ getSystemInfoSync: function () {
28
+ try {
29
+ var info = (typeof uni !== 'undefined' && uni.getSystemInfoSync) ? (uni.getSystemInfoSync() || {}) : {}
30
+ return { pixelRatio: info.pixelRatio || 1, platform: info.platform || '' }
31
+ } catch (e) {
32
+ return { pixelRatio: 1, platform: '' }
33
+ }
34
+ },
35
+ request: function () {}
36
+ };
37
+
38
+ """
39
+
40
+ with io.open(SRC, 'rb') as f:
41
+ body = f.read()
42
+
43
+ with io.open(DST, 'wb') as f:
44
+ f.write(SHIM.encode('utf-8'))
45
+ f.write(body)
46
+
47
+ print('generated %s, size=%d' % (DST, len(SHIM.encode('utf-8')) + len(body)))
@@ -0,0 +1,394 @@
1
+ function parseFontSize(font) {
2
+ const matched = String(font || '').match(/(\d+(?:\.\d+)?)px/)
3
+ return matched ? Number(matched[1]) : 10
4
+ }
5
+
6
+ export function createHarmonyCanvasAdapter(options = {}) {
7
+ const legacy = options.context
8
+ if (!legacy) {
9
+ throw new Error('harmony canvas context is required')
10
+ }
11
+
12
+ const width = Number(options.width) > 0 ? Number(options.width) : 1
13
+ const height = Number(options.height) > 0 ? Number(options.height) : 1
14
+
15
+ const state = {
16
+ fillStyle: '#000000',
17
+ strokeStyle: '#000000',
18
+ lineWidth: 1,
19
+ lineCap: 'butt',
20
+ lineJoin: 'miter',
21
+ miterLimit: 10,
22
+ lineDashOffset: 0,
23
+ lineDash: [],
24
+ globalAlpha: 1,
25
+ globalCompositeOperation: 'source-over',
26
+ font: '10px sans-serif'
27
+ }
28
+ const stack = []
29
+ const rafIds = new Set()
30
+ let dirty = false
31
+ let flushing = false
32
+ let destroyed = false
33
+ let flushTimer = null
34
+
35
+ const flush = () => {
36
+ if (destroyed || !dirty || flushing) return
37
+ dirty = false
38
+ flushing = true
39
+ let released = false
40
+ const release = () => {
41
+ if (released) return
42
+ released = true
43
+ flushing = false
44
+ if (dirty) scheduleFlush()
45
+ }
46
+ try {
47
+ if (typeof legacy.draw === 'function') {
48
+ legacy.draw(false, release)
49
+ setTimeout(release, 80)
50
+ } else {
51
+ release()
52
+ }
53
+ } catch (error) {
54
+ release()
55
+ }
56
+ }
57
+
58
+ const scheduleFlush = () => {
59
+ if (destroyed || flushTimer !== null) return
60
+ const timer = setTimeout(() => {
61
+ rafIds.delete(timer)
62
+ if (flushTimer === timer) flushTimer = null
63
+ flush()
64
+ }, 0)
65
+ flushTimer = timer
66
+ rafIds.add(timer)
67
+ }
68
+
69
+ const markDirty = () => {
70
+ dirty = true
71
+ scheduleFlush()
72
+ }
73
+
74
+ const sync = () => {
75
+ if (typeof legacy.setFillStyle === 'function') {
76
+ legacy.setFillStyle(state.fillStyle)
77
+ }
78
+ if (typeof legacy.setStrokeStyle === 'function') {
79
+ legacy.setStrokeStyle(state.strokeStyle)
80
+ }
81
+ if (typeof legacy.setLineWidth === 'function') {
82
+ legacy.setLineWidth(state.lineWidth)
83
+ }
84
+ if (typeof legacy.setLineCap === 'function') {
85
+ legacy.setLineCap(state.lineCap)
86
+ }
87
+ if (typeof legacy.setLineJoin === 'function') {
88
+ legacy.setLineJoin(state.lineJoin)
89
+ }
90
+ if (typeof legacy.setMiterLimit === 'function') {
91
+ legacy.setMiterLimit(state.miterLimit)
92
+ }
93
+ if (typeof legacy.setGlobalAlpha === 'function') {
94
+ legacy.setGlobalAlpha(state.globalAlpha)
95
+ }
96
+ if (typeof legacy.setGlobalCompositeOperation === 'function') {
97
+ legacy.setGlobalCompositeOperation(state.globalCompositeOperation)
98
+ }
99
+ if (typeof legacy.setLineDash === 'function') {
100
+ legacy.setLineDash(state.lineDash, state.lineDashOffset)
101
+ }
102
+ if (typeof legacy.setFontSize === 'function') {
103
+ legacy.setFontSize(parseFontSize(state.font))
104
+ }
105
+ }
106
+
107
+ const canvas = {
108
+ width,
109
+ height,
110
+ getContext() {
111
+ return context
112
+ },
113
+ requestAnimationFrame(callback) {
114
+ const timer = setTimeout(() => {
115
+ rafIds.delete(timer)
116
+ if (destroyed) return
117
+ try {
118
+ callback(Date.now())
119
+ } finally {
120
+ flush()
121
+ }
122
+ }, 33)
123
+ rafIds.add(timer)
124
+ return timer
125
+ },
126
+ cancelAnimationFrame(timer) {
127
+ if (rafIds.has(timer)) {
128
+ clearTimeout(timer)
129
+ rafIds.delete(timer)
130
+ }
131
+ },
132
+ createImage() {
133
+ const image = {
134
+ width: 0,
135
+ height: 0,
136
+ _src: '',
137
+ _localPath: '',
138
+ onload: null,
139
+ onerror: null,
140
+ addEventListener(type, callback) {
141
+ if (type === 'load') image.onload = callback
142
+ if (type === 'error') image.onerror = callback
143
+ }
144
+ }
145
+ Object.defineProperty(image, 'src', {
146
+ get() {
147
+ return image._src
148
+ },
149
+ set(value) {
150
+ image._src = value
151
+ uni.getImageInfo({
152
+ src: value,
153
+ success(result) {
154
+ image.width = result.width || 0
155
+ image.height = result.height || 0
156
+ image._localPath = result.path || value
157
+ if (typeof image.onload === 'function') image.onload()
158
+ },
159
+ fail() {
160
+ if (typeof image.onerror === 'function') image.onerror()
161
+ }
162
+ })
163
+ }
164
+ })
165
+ return image
166
+ }
167
+ }
168
+
169
+ const context = {
170
+ canvas,
171
+ get fillStyle() {
172
+ return state.fillStyle
173
+ },
174
+ set fillStyle(value) {
175
+ state.fillStyle = value
176
+ },
177
+ get strokeStyle() {
178
+ return state.strokeStyle
179
+ },
180
+ set strokeStyle(value) {
181
+ state.strokeStyle = value
182
+ },
183
+ get lineWidth() {
184
+ return state.lineWidth
185
+ },
186
+ set lineWidth(value) {
187
+ state.lineWidth = value
188
+ },
189
+ get lineCap() {
190
+ return state.lineCap
191
+ },
192
+ set lineCap(value) {
193
+ state.lineCap = value
194
+ },
195
+ get lineJoin() {
196
+ return state.lineJoin
197
+ },
198
+ set lineJoin(value) {
199
+ state.lineJoin = value
200
+ },
201
+ get miterLimit() {
202
+ return state.miterLimit
203
+ },
204
+ set miterLimit(value) {
205
+ state.miterLimit = value
206
+ },
207
+ get lineDashOffset() {
208
+ return state.lineDashOffset
209
+ },
210
+ set lineDashOffset(value) {
211
+ state.lineDashOffset = value
212
+ },
213
+ get globalAlpha() {
214
+ return state.globalAlpha
215
+ },
216
+ set globalAlpha(value) {
217
+ state.globalAlpha = value
218
+ },
219
+ get globalCompositeOperation() {
220
+ return state.globalCompositeOperation
221
+ },
222
+ set globalCompositeOperation(value) {
223
+ state.globalCompositeOperation = value
224
+ },
225
+ get font() {
226
+ return state.font
227
+ },
228
+ set font(value) {
229
+ state.font = value
230
+ },
231
+ save() {
232
+ stack.push(Object.assign({}, state))
233
+ if (typeof legacy.save === 'function') legacy.save()
234
+ },
235
+ restore() {
236
+ if (stack.length) Object.assign(state, stack.pop())
237
+ if (typeof legacy.restore === 'function') legacy.restore()
238
+ sync()
239
+ },
240
+ translate(x, y) {
241
+ legacy.translate(x, y)
242
+ },
243
+ scale(x, y) {
244
+ legacy.scale(x, y)
245
+ },
246
+ rotate(angle) {
247
+ legacy.rotate(angle)
248
+ },
249
+ transform(a, b, c, d, e, f) {
250
+ if (typeof legacy.transform === 'function') {
251
+ legacy.transform(a, b, c, d, e, f)
252
+ }
253
+ },
254
+ setTransform(a, b, c, d, e, f) {
255
+ if (typeof legacy.setTransform === 'function') {
256
+ legacy.setTransform(a, b, c, d, e, f)
257
+ } else if (typeof legacy.transform === 'function') {
258
+ legacy.transform(a, b, c, d, e, f)
259
+ }
260
+ },
261
+ beginPath() {
262
+ legacy.beginPath()
263
+ },
264
+ closePath() {
265
+ legacy.closePath()
266
+ },
267
+ moveTo(x, y) {
268
+ legacy.moveTo(x, y)
269
+ },
270
+ lineTo(x, y) {
271
+ legacy.lineTo(x, y)
272
+ },
273
+ bezierCurveTo(a, b, c, d, e, f) {
274
+ legacy.bezierCurveTo(a, b, c, d, e, f)
275
+ },
276
+ quadraticCurveTo(a, b, c, d) {
277
+ if (typeof legacy.quadraticCurveTo === 'function') {
278
+ legacy.quadraticCurveTo(a, b, c, d)
279
+ }
280
+ },
281
+ rect(x, y, w, h) {
282
+ legacy.rect(x, y, w, h)
283
+ },
284
+ arc(x, y, radius, start, end, anticlockwise) {
285
+ if (typeof legacy.arc === 'function') {
286
+ legacy.arc(x, y, radius, start, end, anticlockwise)
287
+ }
288
+ },
289
+ fill() {
290
+ sync()
291
+ legacy.fill()
292
+ markDirty()
293
+ },
294
+ stroke() {
295
+ sync()
296
+ legacy.stroke()
297
+ markDirty()
298
+ },
299
+ clip() {
300
+ if (typeof legacy.clip === 'function') {
301
+ legacy.clip()
302
+ }
303
+ },
304
+ clearRect(x, y, w, h) {
305
+ if (typeof legacy.clearRect === 'function') {
306
+ legacy.clearRect(x, y, w, h)
307
+ }
308
+ markDirty()
309
+ },
310
+ fillRect(x, y, w, h) {
311
+ sync()
312
+ if (typeof legacy.fillRect === 'function') {
313
+ legacy.fillRect(x, y, w, h)
314
+ }
315
+ markDirty()
316
+ },
317
+ strokeRect(x, y, w, h) {
318
+ sync()
319
+ if (typeof legacy.strokeRect === 'function') {
320
+ legacy.strokeRect(x, y, w, h)
321
+ }
322
+ markDirty()
323
+ },
324
+ drawImage() {
325
+ const image = arguments[0]
326
+ const source = image && (image._localPath || image._src || image.src)
327
+ if (!source) return
328
+ sync()
329
+ const args = Array.prototype.slice.call(arguments, 1)
330
+ try {
331
+ if (args.length >= 8) {
332
+ legacy.drawImage(source, args[4], args[5], args[6], args[7])
333
+ } else if (args.length >= 4) {
334
+ legacy.drawImage(source, args[0], args[1], args[2], args[3])
335
+ } else {
336
+ legacy.drawImage(
337
+ source,
338
+ args[0] || 0,
339
+ args[1] || 0,
340
+ image.width || 0,
341
+ image.height || 0
342
+ )
343
+ }
344
+ markDirty()
345
+ } catch (error) {
346
+ // 单张图片绘制失败时跳过,避免中断整个动画
347
+ }
348
+ },
349
+ createLinearGradient(x0, y0, x1, y1) {
350
+ return legacy.createLinearGradient(x0, y0, x1, y1)
351
+ },
352
+ createRadialGradient(x0, y0, r0, x1, y1, r1) {
353
+ return legacy.createRadialGradient(x0, y0, r0, x1, y1, r1)
354
+ },
355
+ setLineDash(segments, offset) {
356
+ state.lineDash = Array.isArray(segments) ? segments.slice() : []
357
+ if (typeof offset === 'number') {
358
+ state.lineDashOffset = offset
359
+ }
360
+ },
361
+ fillText(text, x, y) {
362
+ sync()
363
+ if (typeof legacy.fillText === 'function') {
364
+ legacy.fillText(text, x, y)
365
+ }
366
+ markDirty()
367
+ },
368
+ strokeText(text, x, y) {
369
+ sync()
370
+ if (typeof legacy.strokeText === 'function') {
371
+ legacy.strokeText(text, x, y)
372
+ }
373
+ markDirty()
374
+ },
375
+ measureText(text) {
376
+ const fontSize = parseFontSize(state.font)
377
+ return { width: String(text == null ? '' : text).length * fontSize * 0.6 }
378
+ }
379
+ }
380
+
381
+ return {
382
+ canvas,
383
+ context,
384
+ flush,
385
+ destroy() {
386
+ destroyed = true
387
+ rafIds.forEach((timer) => clearTimeout(timer))
388
+ rafIds.clear()
389
+ flushTimer = null
390
+ dirty = false
391
+ stack.length = 0
392
+ }
393
+ }
394
+ }