uview-plus 3.8.106 → 3.8.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +23 -0
- package/components/u-canvas/u-canvas.vue +67 -8
- package/components/u-poster/u-poster.vue +37 -4
- package/libs/util/gcanvas/bridge/bridge-weex.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStyleLinearGradient.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStylePattern.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStyleRadialGradient.js +0 -0
- package/libs/util/gcanvas/context-2d/RenderingContext.js +0 -0
- package/libs/util/gcanvas/context-webgl/ActiveInfo.js +0 -0
- package/libs/util/gcanvas/context-webgl/Buffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/Framebuffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLenum.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLmethod.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLtype.js +0 -0
- package/libs/util/gcanvas/context-webgl/Program.js +0 -0
- package/libs/util/gcanvas/context-webgl/Renderbuffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/RenderingContext.js +0 -0
- package/libs/util/gcanvas/context-webgl/Shader.js +0 -0
- package/libs/util/gcanvas/context-webgl/ShaderPrecisionFormat.js +0 -0
- package/libs/util/gcanvas/context-webgl/Texture.js +0 -0
- package/libs/util/gcanvas/context-webgl/UniformLocation.js +0 -0
- package/libs/util/gcanvas/context-webgl/classUtils.js +0 -0
- package/libs/util/gcanvas/env/canvas.js +0 -0
- package/libs/util/gcanvas/env/image.js +0 -0
- package/libs/util/gcanvas/env/tool.js +0 -0
- package/libs/util/gcanvas/index.js +0 -0
- package/package.json +2 -2
package/changelog.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## 3.8.108(2026-08-18)
|
|
2
|
+
fix: 修复鸿蒙二维码不显示、海报文字不换行等绘图问题
|
|
3
|
+
|
|
4
|
+
鸿蒙 CanvasContext.measureText 同步返回值恒为 0,真实宽度只通过可选 callback 经 evalJSAsync 异步回传;
|
|
5
|
+
而 uni-app-plus 走 evalJSSync 同步就能拿到真值。这是同样代码在安卓
|
|
6
|
+
正常、鸿蒙异常的根因。
|
|
7
|
+
|
|
8
|
+
宽度 0 会让 drawTextWithLineClamp 里 `0 <= maxWidth` 恒成立,整段
|
|
9
|
+
文本被当成一行放得下直接输出,标题横穿并压到二维码上。
|
|
10
|
+
|
|
11
|
+
- u-canvas: 新增 #ifdef APP-HARMONY 的 callback 测量分支;measureText
|
|
12
|
+
不再把 0 当有效值外传;新增 estimateTextWidth 兜底
|
|
13
|
+
- u-poster: measureTextWidth 拿到非正数宽度时改用估算,不再 `|| 0` 静默吞掉
|
|
14
|
+
- 兜底估算区分全角/半角(全角 1.0、半角 0.56、空白 0.28)。原先的
|
|
15
|
+
length * fontSize * 0.6 会让中文短算约 40%,仍然不换行
|
|
16
|
+
- callback 超时从 1000ms 降到 300ms,并用 _harmonyMeasureUnavailable
|
|
17
|
+
锁存状态:一次换行要二分测量十几次,逐次干等会把导出拖成十几秒
|
|
18
|
+
- 新增 verify:harmony-poster-text-wrap 校验脚本
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## 3.8.107(2026-08-18)
|
|
22
|
+
fix: 修复empty示例页面
|
|
23
|
+
|
|
1
24
|
## 3.8.106
|
|
2
25
|
feat: 让 App 原生 nvue 页面完整复用 App.up.vue 根容器
|
|
3
26
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
/>
|
|
22
22
|
<!-- #endif -->
|
|
23
23
|
|
|
24
|
-
<!-- #ifdef APP-PLUS -->
|
|
24
|
+
<!-- #ifdef APP-PLUS || APP-HARMONY -->
|
|
25
25
|
<!-- #ifndef APP-NVUE -->
|
|
26
26
|
<canvas
|
|
27
27
|
class="u-canvas__canvas"
|
|
@@ -203,7 +203,7 @@ export default {
|
|
|
203
203
|
size: true
|
|
204
204
|
},
|
|
205
205
|
(res) => {
|
|
206
|
-
// #ifdef APP-PLUS
|
|
206
|
+
// #ifdef APP-PLUS || APP-HARMONY
|
|
207
207
|
resolve(res || {
|
|
208
208
|
width: this.actualWidth,
|
|
209
209
|
height: this.actualHeight
|
|
@@ -232,7 +232,7 @@ export default {
|
|
|
232
232
|
return this._webViewContext;
|
|
233
233
|
// #endif
|
|
234
234
|
|
|
235
|
-
// #ifdef APP-PLUS
|
|
235
|
+
// #ifdef APP-PLUS || APP-HARMONY
|
|
236
236
|
return uni.createCanvasContext(this.canvasId, this);
|
|
237
237
|
// #endif
|
|
238
238
|
|
|
@@ -364,7 +364,7 @@ export default {
|
|
|
364
364
|
measureText: (text, state) => {
|
|
365
365
|
const matched = String(state.font || '').match(/(\d+(?:\.\d+)?)px/);
|
|
366
366
|
const fontSize = matched ? Number(matched[1]) : this.fontSize;
|
|
367
|
-
return { width:
|
|
367
|
+
return { width: this.estimateTextWidth(text, fontSize) };
|
|
368
368
|
}
|
|
369
369
|
});
|
|
370
370
|
this.ctx = this._webViewContext;
|
|
@@ -646,18 +646,77 @@ export default {
|
|
|
646
646
|
}
|
|
647
647
|
return this.callContext('strokeText', String(text), x, y, maxWidth);
|
|
648
648
|
},
|
|
649
|
+
/**
|
|
650
|
+
* 按字形宽度估算文本宽度
|
|
651
|
+
* @description 当平台无法给出真实测量值时的兜底。必须区分全角/半角:
|
|
652
|
+
* 汉字、假名、全角标点约占一个字号,按半角的 0.6 估算会短 40%,
|
|
653
|
+
* 中文海报因此算出"整段都放得下"而不换行。
|
|
654
|
+
* @param {String} text 待测量文本
|
|
655
|
+
* @param {Number} [fontSize] 字号,默认取组件当前字号
|
|
656
|
+
* @returns {Number} 估算宽度(px)
|
|
657
|
+
* @author jry ijry@qq.com
|
|
658
|
+
*/
|
|
659
|
+
estimateTextWidth(text, fontSize) {
|
|
660
|
+
const size = Number(fontSize) || Number(this.fontSize) || 12;
|
|
661
|
+
const FULL_WIDTH = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-가-힣豈-︰--⦆¢-₩]/;
|
|
662
|
+
return Array.from(String(text)).reduce((width, char) => {
|
|
663
|
+
if (FULL_WIDTH.test(char)) return width + size;
|
|
664
|
+
if (/\s/.test(char)) return width + size * 0.28;
|
|
665
|
+
return width + size * 0.56;
|
|
666
|
+
}, 0);
|
|
667
|
+
},
|
|
649
668
|
measureText(text) {
|
|
650
669
|
if (this.ctx && typeof this.ctx.measureText === 'function') {
|
|
651
|
-
|
|
670
|
+
const metrics = this.ctx.measureText(String(text));
|
|
671
|
+
const width = Number(metrics && metrics.width);
|
|
672
|
+
// 鸿蒙同步测量恒为 0(真实值只走 callback),上下文未就绪时其他
|
|
673
|
+
// 平台也可能返回 0。绝不能把 0 交给调用方——换行逻辑会把它当成
|
|
674
|
+
// "宽度足够",于是整段文本挤成一行。
|
|
675
|
+
if (width > 0) return metrics;
|
|
652
676
|
}
|
|
653
677
|
return {
|
|
654
|
-
width:
|
|
678
|
+
width: this.estimateTextWidth(text)
|
|
655
679
|
};
|
|
656
680
|
},
|
|
657
681
|
measureTextAsync(text) {
|
|
658
682
|
if (this.ctx && typeof this.ctx.measureTextAsync === 'function') {
|
|
659
683
|
return this.ctx.measureTextAsync(String(text));
|
|
660
684
|
}
|
|
685
|
+
|
|
686
|
+
// #ifdef APP-HARMONY
|
|
687
|
+
// 鸿蒙的 CanvasContext.measureText 同步返回值恒为 0,真实宽度只通过
|
|
688
|
+
// callback(webview.evalJSAsync) 回传,因此必须走回调形式。
|
|
689
|
+
if (this.ctx && typeof this.ctx.measureText === 'function' && !this._harmonyMeasureUnavailable) {
|
|
690
|
+
return new Promise((resolve) => {
|
|
691
|
+
let settled = false;
|
|
692
|
+
// evalJSAsync 并非所有鸿蒙版本都提供,回调可能永远不来,
|
|
693
|
+
// 必须兜底避免上层 await 永久挂起。且一旦超时就标记为不可用:
|
|
694
|
+
// 单次换行要二分测量十几次,逐次干等会把导出拖成十几秒。
|
|
695
|
+
const timer = setTimeout(() => {
|
|
696
|
+
if (settled) return;
|
|
697
|
+
settled = true;
|
|
698
|
+
this._harmonyMeasureUnavailable = true;
|
|
699
|
+
resolve({ width: this.estimateTextWidth(text) });
|
|
700
|
+
}, 300);
|
|
701
|
+
const done = (metrics) => {
|
|
702
|
+
if (settled) return;
|
|
703
|
+
settled = true;
|
|
704
|
+
clearTimeout(timer);
|
|
705
|
+
const width = Number(metrics && metrics.width);
|
|
706
|
+
resolve({ width: width > 0 ? width : this.estimateTextWidth(text) });
|
|
707
|
+
};
|
|
708
|
+
try {
|
|
709
|
+
const sync = this.ctx.measureText(String(text), done);
|
|
710
|
+
// 若该平台其实同步返回了有效值,直接采用
|
|
711
|
+
const syncWidth = Number(sync && sync.width);
|
|
712
|
+
if (syncWidth > 0) done(sync);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
done(null);
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
// #endif
|
|
719
|
+
|
|
661
720
|
return Promise.resolve(this.measureText(text));
|
|
662
721
|
},
|
|
663
722
|
createLinearGradient(x0, y0, x1, y1) {
|
|
@@ -800,8 +859,8 @@ export default {
|
|
|
800
859
|
let destWidth = hasDestWidth ? options.destWidth : width;
|
|
801
860
|
let destHeight = hasDestHeight ? options.destHeight : height;
|
|
802
861
|
|
|
803
|
-
// MP、H5 和 APP-
|
|
804
|
-
// #ifdef MP || H5 || APP-PLUS
|
|
862
|
+
// MP、H5、APP-PLUS 和 APP-HARMONY 保持逻辑绘制坐标,只提升默认导出像素。
|
|
863
|
+
// #ifdef MP || H5 || APP-PLUS || APP-HARMONY
|
|
805
864
|
if (!hasDestWidth) {
|
|
806
865
|
destWidth = Math.round(width * this.dpr);
|
|
807
866
|
}
|
|
@@ -360,6 +360,16 @@ export default {
|
|
|
360
360
|
const lines = [];
|
|
361
361
|
const ellipsis = '...';
|
|
362
362
|
const measurementCache = new Map();
|
|
363
|
+
const fontSize = css.fontSize ? this.convertRpxToPx(css.fontSize) : 20;
|
|
364
|
+
// 兜底估算:全角字符约占一个字号,半角约 0.56。测量失败时若按半角估算,
|
|
365
|
+
// 中文会短算 40% 而不换行,所以必须区分宽度。
|
|
366
|
+
const estimateWidth = (value) => Array.from(String(value)).reduce((width, char) => {
|
|
367
|
+
if (/[ᄀ-ᅟ⺀-〾-㏿㐀-䶿一-鿿ꀀ-가-힣豈-︰--⦆¢-₩]/.test(char)) {
|
|
368
|
+
return width + fontSize;
|
|
369
|
+
}
|
|
370
|
+
if (/\s/.test(char)) return width + fontSize * 0.28;
|
|
371
|
+
return width + fontSize * 0.56;
|
|
372
|
+
}, 0);
|
|
363
373
|
const measureTextWidth = async (value) => {
|
|
364
374
|
if (measurementCache.has(value)) {
|
|
365
375
|
return measurementCache.get(value);
|
|
@@ -370,7 +380,12 @@ export default {
|
|
|
370
380
|
} else {
|
|
371
381
|
metrics = ctx.measureText(value);
|
|
372
382
|
}
|
|
373
|
-
|
|
383
|
+
let measuredWidth = Number(metrics && metrics.width) || 0;
|
|
384
|
+
// 测量不可用时(鸿蒙同步 measureText 恒返回 0)必须改用估算:
|
|
385
|
+
// 把 0 当作真实宽度会让下面的 <= maxWidth 恒成立,整段文本挤成一行。
|
|
386
|
+
if (!(measuredWidth > 0) && value) {
|
|
387
|
+
measuredWidth = estimateWidth(value);
|
|
388
|
+
}
|
|
374
389
|
measurementCache.set(value, measuredWidth);
|
|
375
390
|
return measuredWidth;
|
|
376
391
|
};
|
|
@@ -475,15 +490,33 @@ export default {
|
|
|
475
490
|
*/
|
|
476
491
|
convertRpxToPx(rpxValue) {
|
|
477
492
|
if (typeof rpxValue === 'number') return rpxValue;
|
|
478
|
-
|
|
493
|
+
|
|
479
494
|
// 使用rpx2px方法
|
|
480
495
|
if (typeof rpxValue === 'string' && rpxValue.endsWith('rpx')) {
|
|
481
496
|
const value = parseFloat(rpxValue);
|
|
482
|
-
return
|
|
497
|
+
return value * this.getRpxRatio();
|
|
483
498
|
}
|
|
484
|
-
|
|
499
|
+
|
|
485
500
|
return parseFloat(rpxValue) || 0;
|
|
486
501
|
},
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* 获取 rpx -> px 的统一换算比例
|
|
505
|
+
* @description uni.upx2px 对每个值单独判断设备宽度:当 windowWidth 超过
|
|
506
|
+
* rpxCalcMaxDeviceWidth(默认960)时,只有恰好等于 750 的值使用真实设备宽度,
|
|
507
|
+
* 其余值回退到 rpxCalcBaseDeviceWidth(默认375)。海报需要所有尺寸共用同一
|
|
508
|
+
* 比例,逐值换算会让宽高用上不同基准而破坏宽高比:鸿蒙 windowWidth 上报为
|
|
509
|
+
* 物理像素1084,750rpx算得1084、1114rpx算得557,海报因此变成横向。
|
|
510
|
+
* 这里只取一次比例,且刻意用非 750 的值探测,避开上面的特例分支,
|
|
511
|
+
* 使超宽设备统一走 baseWidth,避免画布被放大到数十MB。
|
|
512
|
+
* @returns {Number} 每 1rpx 对应的 px 值
|
|
513
|
+
* @author jry ijry@qq.com
|
|
514
|
+
*/
|
|
515
|
+
getRpxRatio() {
|
|
516
|
+
const PROBE = 1000; // 不能用 750,750 是 upx2px 的特例值
|
|
517
|
+
const ratio = rpx2px(PROBE) / PROBE;
|
|
518
|
+
return ratio > 0 ? ratio : 1;
|
|
519
|
+
},
|
|
487
520
|
|
|
488
521
|
/**
|
|
489
522
|
* 绘制渐变背景
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "uview-plus",
|
|
3
3
|
"name": "uview-plus",
|
|
4
4
|
"displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙跨端移动组件库,组件丰富维护更新稳定。",
|
|
5
|
-
"version": "3.8.
|
|
5
|
+
"version": "3.8.108",
|
|
6
6
|
"description": "零云®uview-plus已兼容vue3支持多语言,120+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名、Markdown等。",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"uview",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"echarts",
|
|
12
12
|
"ui",
|
|
13
13
|
"可视化"
|
|
14
|
-
|
|
14
|
+
],
|
|
15
15
|
"main": "index.js",
|
|
16
16
|
"repository": "https://github.com/ijry/uview-plus",
|
|
17
17
|
"engines": {
|