摘要:2048 游戏的核心在于 Canvas 动画。但在高分辨率(High DPI)的鸿蒙手机上,我们发现游戏画面不仅锯齿严重,而且动画帧率(FPS)极不稳定,玩起来像看幻灯片。本文复盘了如何开启 ArkWeb 的硬件加速,并利用离屏渲染(Offscreen Canvas)将性能提升 3 倍的过程。
🌫️ 1. 模糊与锯齿
现象:
在 Web 浏览器上看起来清晰锐利的数字方块,到了鸿蒙真机上变得模糊不清,边缘有明显的锯齿。
原因:像素比 (Device Pixel Ratio)
现代手机屏幕的物理像素通常是 CSS 逻辑像素的 2 倍甚至 3 倍(DPR = 3)。
如果 <canvas width="300" height="300">,浏览器会拉伸这张 300x300 的位图到 900x900 的物理屏幕上显示,必然模糊。
修复代码:
function setupHiDPICanvas(canvas, width, height) {
const dpr = window.devicePixelRatio || 1;
// 1. 设置物理尺寸 (Buffer Size)
canvas.width = width * dpr;
canvas.height = height * dpr;
// 2. 设置 CSS 显示尺寸
canvas.style.width = width + "px";
canvas.style.height = height + "px";
// 3. 缩放绘图上下文
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
return ctx;
}
🐌 2. 掉帧之谜
解决了模糊,我们又遇到了掉帧。每当 4x4 的方块同时移动时,FPS 会从 60 跌到 20。
Profile 分析:
通过 Chrome Performance 工具分析,发现大量的 CPU 时间消耗在 Composite Layers(图层合成)和 Paint(重绘)上。
因为 2048 的背景网格是静态的,但每次方块移动,我们都在 requestAnimationFrame 里清空整个 Canvas 重绘所有内容。
🚀 3. 离屏渲染优化
我们采用 离屏渲染 (Offscreen Canvas) 技术,将静态背景缓存下来。
3.1 缓存静态层
// 创建离屏 Canvas
const bgCanvas = document.createElement('canvas');
const bgCtx = setupHiDPICanvas(bgCanvas, 300, 300);
// 预先绘制好背景网格(只画一次)
function drawBackground() {
bgCtx.fillStyle = '#bbada0';
bgCtx.fillRect(0, 0, 300, 300);
// ... 绘制格子线条
}
drawBackground();
3.2 主循环复用
在每一帧动画中,直接将离屏 Canvas 当作图片画上去,而不是重绘线条。
function renderLoop() {
// 1. 清空主画布
mainCtx.clearRect(0, 0, width, height);
// 2. 极速绘制背景 (BitBlt 操作,GPU 加速)
mainCtx.drawImage(bgCanvas, 0, 0, width, height);
// 3. 只绘制移动中的方块
drawActiveTiles();
requestAnimationFrame(renderLoop);
}
⚡ 4. 开启硬件加速
在 Webview 层面,我们还可以进一步压榨 GPU 性能。
CSS 开启:
给 Canvas 元素加上 will-change 属性,强制浏览器为其分配独立的 Compositor Layer。
canvas {
will-change: transform; /* 或者 transform: translateZ(0); */
}
ArkWeb 配置:
确保在 Native 侧没有误关闭加速。
Web({ ... })
// 默认通常是开启的,但检查一下更放心
// 注意:某些旧版本可能没有直接的硬件加速开关 API,
// 但可以通过 layer 模式间接控制
📊 5. 最终成绩
| 优化阶段 | 画面清晰度 | 平均 FPS | 功耗 |
|---|---|---|---|
| 原始版本 | 模糊 | 24 | 高 |
| HiDPI 修复 | 清晰 | 18 (更卡了,因为像素点多了4倍) | 极高 |
| 离屏渲染 + 硬件加速 | 清晰 | 58 | 低 |
现在,即使在千元机上,2048 的滑动动画也像丝绸一样顺滑了。
1338

被折叠的 条评论
为什么被折叠?



