预渲染文字纹理可显著提升canvas文字渲染性能。其核心是将运行时动态绘制转为启动时批量生成base64图片,通过哈希缓存、统一字体、billboardentity贴图等方式,绕过labelentity高频getimagedata和重复绘图开销,使500标签加载时间从4.7秒降至600ms内,内存降65%。

Canvas文字渲染性能问题,核心不在“画得慢”,而在“反复画”。尤其在Mars3D、Cesium等三维引擎中,每个LabelEntity背后都隐含一次完整的文本→Canvas→纹理→GPU上传流程。当标签数量达数百上千时,主线程被大量同步的getImageData、字体测量、样式计算拖垮,卡顿就不可避免。预渲染文字纹理,本质是把“运行时动态生成”变成“启动时批量生成”,用空间换时间,用静态图替代实时绘制。
为什么原生Label会卡?
不是Canvas本身慢,而是引擎调用方式放大了开销:
- 每个LabelEntity都触发独立的Canvas绘图上下文创建与销毁
- 即使样式相同,Mars3D仍为每个实体重复执行font加载、measureText、背景绘制、透明度合成
- getImageData被高频调用——这是为了读取临时Canvas像素数据并转成WebGL纹理,而该操作是同步且阻塞主线程的
- DOM或虚拟DOM层叠加(如label属性依赖div浮层)进一步加剧重排重绘压力
预渲染纹理的关键步骤
不依赖运行时Canvas绘图,而是提前生成一张张带文字的图片资源:
- 统一字体与样式:限定使用已知可用字体(如'PingFang SC'、'Microsoft YaHei'),避免回退导致基线偏移;禁用font-weight变化,减少字体实例数量
-
离线生成Base64图片:用
document.createElement('canvas')创建离屏Canvas,一次性绘制文字+背景+边框,调用toDataURL('image/png')导出 -
按需缓存纹理:对相同文字+样式的组合做哈希键(如
`${text}-${fontSize}-${bgColor}`),避免重复绘制 -
替换为BillboardEntity:将生成的Base64图片作为
style.image传入BillboardEntity,由WebGL直接贴图渲染,完全绕过LabelEntity的文本管线
实际优化效果对比
以加载500个建筑名称标签为例(字体40px,带半透明背景):
- 原方案:平均加载耗时4.7秒,主线程冻结明显,低配设备偶发崩溃
- 预渲染方案:首次生成耗时约800ms(可异步/分片),后续复用零开销;总加载时间降至600ms内,帧率稳定60fps
- 内存占用下降约65%——DOM节点从500+减至1个Billboard图层容器,WebGL纹理合并为少量Atlas或独立图片
注意事项与边界处理
预渲染不是万能银弹,需配合业务场景做取舍:
- 文字内容频繁变动(如实时温度标签)不适合全量预渲染,可考虑“静态前缀+动态后缀”拆分策略
- 多语言混合文本要注意字体fallback一致性,建议预加载关键字体并通过
FontFace.load()确认就绪后再生成 - 高DPI屏幕需按
window.devicePixelRatio缩放Canvas尺寸,再用CSS scale还原,否则文字模糊 - 避免在循环中直接调用
toDataURL——它会触发同步编码,应批量生成后统一提交











