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

3 回复

该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在不同页面返回不同宽度,通常是由于字体加载状态不一致导致的。

从你的代码分析:

  1. 字体加载异步性uni.loadFontFace是异步操作,但你在success回调中直接调用createMeasureContext()。如果页面切换时字体尚未完全加载完成,measureText会使用默认字体计算。

  2. 页面生命周期差异:首页可能已经完成字体加载,而“我的页面”在切换时字体可能还在加载中或未加载。

  3. Canvas上下文创建时机:你在loadFontFacesuccess回调中创建测量上下文,但字体实际渲染到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();
  });
}
回到顶部