uni-app canvas中measureText的问题
uni-app canvas中measureText的问题
| 开发环境 | 版本号 | 项目创建方式 |
|---|---|---|
| Windows | windows10 | HBuilderX |
产品分类:uniapp/App
PC开发环境操作系统:Windows
PC开发环境操作系统版本号:windows10
HBuilderX类型:正式
HBuilderX版本号:4.87
手机系统:Android
手机系统版本号:Android 13
手机厂商:华为
手机机型:nova7
页面类型:vue
vue版本:vue2
打包方式:云端
项目创建方式:HBuilderX
示例代码:
<template>
<view @tap="ceshi">
测试
</view>
</template>
<script>
export default {
data() {
return {
measureContext: null
}
},
created() {
this.download()
},
methods: {
ceshi(){
uni.navigateTo({
url:'/pages/index/tiao'
})
},
async download() {
// 下载字体文件
const cachedPath = uni.getStorageSync(
'https://write.quickoj.com/uploads/font_ttf/8320702d427f13b080819d668effc629.ttf ');
console.log(cachedPath)
if (cachedPath) {
try {
await this.loadFontFace(cachedPath)
return
} catch (e) {
console.log('缓存字体加载失败,重新下载', e);
uni.removeStorageSync(item.font_url); // 移除无效缓存
}
}
console.log('下载')
const tempPath = await this.downloadFontFile(
'https://write.quickoj.com/uploads/font_ttf/8320702d427f13b080819d668effc629.ttf ');
// 保存到本地(仅小程序/部分App支持)
const savedPath = await this.saveFile(tempPath);
uni.setStorageSync('https://write.quickoj.com/uploads/font_ttf/8320702d427f13b080819d668effc629.ttf ',
savedPath);
await this.loadFontFace(savedPath);
},
downloadFontFile(url) {
return new Promise((resolve, reject) => {
uni.downloadFile({
url,
success: (res) => {
if (res.statusCode === 200) {
resolve(res.tempFilePath);
} else {
reject(`下载失败,状态码:${res.statusCode}`);
}
},
fail: reject,
});
});
},
saveFile(tempFilePath) {
return new Promise((resolve, reject) => {
uni.saveFile({
tempFilePath,
success: (res) => {
resolve(res.savedFilePath); // 返回持久化路径
},
fail: reject,
});
});
},
loadFontFace(filePath, index, item) {
const fontFamily = `customFont_1`;
uni.loadFontFace({
family: fontFamily,
source: `url("${filePath}")`,
success: () => {
this.fontFamily = fontFamily
this.createMeasureContext()
// console.log(this.fontCache)
},
fail: (err) => {
console.error(`字体加载失败,尝试回退网络地址:${item.font_url}`, err);
}
});
},
// 创建测量上下文的独立方法
createMeasureContext() {
try {
// 创建测量上下文
this.measureContext = uni.createCanvasContext('measureCanvas2', this);
// 设置字体
console.log('fontFamily', this.fontFamily)
// const fontFamily = '"Noto Sans Thai", "Thonburi", "Tahoma", sans-serif';
this.measureContext.font = `30px ${this.fontFamily || 'sans-serif'}`;
console.log(this.measureContext)
// 验证字体设置是否生效
const testText = 'สวัสดีครับ';
const metrics = this.measureContext.measureText(testText);
console.log('测量上下文创建完成,测试文本宽度:', metrics.width);
} catch (error) {
console.error('创建测量上下文失败:', error);
}
},
}
</script>
<style lang="scss">
</style>
操作步骤:
我这段代码放在底部导航栏首页index里可以获取正常metrics.width,但放在底部导航栏我的页面同样代码获取metrics.width就异常,在页面也可以正常渲染该字体
预期结果:
metrics.width为78.62992858886719
实际结果:
metrics.width为125.1123046875
bug描述:
canvas中的measureText相同的代码在不同的页面运行结果不一样
更多关于uni-app canvas中measureText的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html
该bug反馈内容基本完整但存在关键缺失:BUG描述未明确区分App与小程序平台特性,复现步骤过于简略(未说明两个页面的具体差异,如是否均为nvue页面、DPI设置等),代码示例中loadFontFace方法存在未使用参数且未处理字体加载完成到测量的时间差问题。预期结果78.63与实际结果125.11差异显著,符合异常特征。
经核查知识库:
CanvasContext.measureText文档明确说明"App端2.8.12+支持同步接口",但用户HBuilderX 4.87版本低于最新修复版本(4.36.2024112612-alpha中修复了canvas相关渲染问题)
uni.loadFontFace平台差异指出Android平台需注意字体加载时序问题
用户使用Vue2+nvue场景,而知识库显示4.36版本修复了App-Android平台keepAlive组件切换问题,可能影响canvas上下文状态
问题核心在于:不同页面中字体加载完成时机与canvas上下文初始化存在竞争条件。当页面快速切换时,"我的"页面可能在字体完全生效前执行measureText,导致测量值异常(125.11接近系统默认字体宽度)。建议用户:
升级至HBuilderX 4.36+版本
在loadFontFace的success回调中添加setTimeout确保字体渲染完成
检查两个页面是否均为nvue页面(nvue对canvas有特殊限制)
参考font加载最佳实践使用字体加载完成检测机制
内容为 AI 生成,仅供参考
更多关于uni-app canvas中measureText的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html
你这个页面不包含 canvas ,如果你认为这个 bug 和 loadfontface 有关系请改成简单代码加载,如果没有关系请移除相关逻辑。
这是一个典型的字体加载时序问题。在uni-app中,measureText在不同页面返回不同宽度,通常是由于字体加载状态不一致导致的。
从你的代码分析:
-
字体加载异步性:
uni.loadFontFace是异步操作,但你在success回调中直接调用createMeasureContext()。如果页面切换时字体尚未完全加载完成,measureText会使用默认字体计算。 -
页面生命周期差异:首页可能已经完成字体加载,而“我的页面”在切换时字体可能还在加载中或未加载。
-
Canvas上下文创建时机:你在
loadFontFace的success回调中创建测量上下文,但字体实际渲染到Canvas可能需要额外时间。
解决方案:
确保在测量文本前字体已完全加载并生效:
// 修改loadFontFace方法
loadFontFace(filePath) {
return new Promise((resolve, reject) => {
const fontFamily = `customFont_1`;
uni.loadFontFace({
family: fontFamily,
source: `url("${filePath}")`,
success: async () => {
this.fontFamily = fontFamily;
// 关键:等待字体真正生效
await this.waitForFontLoad(fontFamily);
this.createMeasureContext();
resolve();
},
fail: reject
});
});
},
// 添加字体加载验证方法
waitForFontLoad(fontFamily) {
return new Promise((resolve) => {
const checkFont = () => {
// 创建临时canvas检查字体
const ctx = uni.createCanvasContext('tempCanvas', this);
ctx.font = `30px ${fontFamily}`;
const metrics1 = ctx.measureText('สวัสดีครับ');
ctx.font = '30px sans-serif';
const metrics2 = ctx.measureText('สวัสดีครับ');
// 如果宽度不同,说明自定义字体已生效
if (Math.abs(metrics1.width - metrics2.width) > 0.1) {
resolve();
} else {
setTimeout(checkFont, 50);
}
};
checkFont();
});
}

