在这里插入图片描述

Canvas 页面在开发机上看着顺眼,不代表上线后一直顺眼。第一次把演示页放到不同窗口尺寸上,我遇到的不是崩溃,而是一种更尴尬的现象:顶部面板被压扁,右侧比较区挤出边缘,底部放大格被挡住。最初代码用了 1280 和 720 作为初始宽高,本意是给首次渲染一个可用值;后来我才意识到,初始值不是布局依据。只要页面发生旋转、分屏、窗口拖拽或容器重新测量,Canvas 里的绝对坐标就需要重新计算。

这一篇不讨论抗锯齿效果,也不复述“状态变了要重画”的问题。我关注的是页面已经能画之后,面积变化如何不把已有布局画乱。Canvas 不像普通声明式组件那样自动替我排版;我在 fillRectfillText 里写进去的每一个坐标都要面对新的宽高。因此上线后维护的第一件事不是问“横屏和竖屏支不支持”,而是确认新的 Area 是否已经成为这一次绘制的真实输入。
在这里插入图片描述

问题不是窗口变了,而是旧尺寸还在指导绘制

我曾经在一个较窄窗口里复测。视觉上 Canvas 已经铺满容器,可代码内部仍按上一轮较宽尺寸算双栏,面板宽度加上左右边距后超过实际画布。由于 Canvas 不会替我裁掉逻辑错误,结果就是边框一部分不见、文字位置显得奇怪。看上去像字体问题,根因却是布局计算的输入没有更新。

现有页面的思路是先把 onAreaChange 给出的宽高落到 canvasWidthcanvasHeight,然后再调用 renderCanvas()。有两个细节我会保留。其一,只接受大于零的宽高,避免测量暂态把合法尺寸写成零;其二,即使这次面积没有通过条件,也会执行绘制入口,让页面按当前已知有效尺寸保持一致。这个取舍不是说零尺寸有意义,而是避免把重绘逻辑分叉得太复杂。

Canvas(this.context)
  .width('100%')
  .height('100%')
  .onAreaChange((_oldArea: Area, newArea: Area) => {
    const width = Math.floor(Number(newArea.width));
    const height = Math.floor(Number(newArea.height));
    if (width > 0 && height > 0) {
      this.canvasWidth = width;
      this.canvasHeight = height;
    }
    this.renderCanvas();
  })
  .onReady(() => { this.canvasReady = true; this.renderCanvas(); });

这里 onReadyonAreaChange 的职责不一样。前者保证绘制上下文准备好以后至少有一次画面;后者处理后续面积变动。把它们混成“哪个先来就画哪个”,很容易在页面初始阶段拿未就绪上下文绘制,或者在尺寸变化后没有刷新。维护时我会检查这两个入口,而不是只看某一台设备是否碰巧画出了首屏。

面积进入绘制后,布局要全部从它推导

真正避免错位的关键,不是多写几个 if,而是不要在绘制函数里偷藏旧常量。现有 renderCanvas() 先从新的高度减去安全区,再以宽度比例算边距、间距和双栏面板宽度。它还给内容高度和边距设置下限,避免小窗口把数值压到不可读的程度。这样每轮布局都来自同一组尺寸输入,面板、下方放大区和边框会一起移动。

private renderCanvas(): void {
  if (!this.canvasReady) {
    return;
  }
  const safeTop = 104;
  const safeBottom = 128;
  const contentHeight = Math.max(360, this.canvasHeight - safeTop - safeBottom);
  const margin = Math.max(36, Math.floor(this.canvasWidth * 0.035));
  const gap = Math.max(16, Math.floor(this.canvasWidth * 0.014));
  const topPanelHeight = Math.floor(contentHeight * 0.58);
  const panelWidth = Math.floor((this.canvasWidth - margin * 2 - gap) / 2);
  this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  // 后续面板和放大区均使用这一轮计算出的坐标。
}

我以前会只盯着 panelWidth,后来发现下方区域也同样重要。lowerYlowerHeight 必须从同一轮 contentHeight 推导,不然顶部压缩了,放大区还停在旧 y 坐标,就会发生重叠。Canvas 的布局错乱常常不是一个元素算错,而是一组相互关联的坐标来自不同时间的尺寸。

容器面积变化

onAreaChange 接收 newArea

取整并过滤非正宽高

更新 canvasWidth 与 canvasHeight

renderCanvas

计算安全区 内容高度 边距 间距

计算双栏面板与放大区坐标

clearRect 清掉旧尺寸画面

按新尺寸重画所有区域

这张流程图里,clearRect 不是装饰。若只按新尺寸再画一次,旧窗口更大时留下的像素可能仍在画布边缘;用户会把残影误以为布局错误。清空范围也必须使用当前画布尺寸。这里没有宣称系统会在任意窗口形态下给出一样的排版,它只是保证这个 Demo 按当前可用面积重新计算并绘制。

在这里插入图片描述

我上线后采用的尺寸复测方式

我会先把测试分成三组:首次进入、变大、变小。首次进入验证 onReady 和首个有效面积协作;变大验证原先的元素不会漂在左上角;变小验证文本、双栏和放大区不会互相盖住。每一组都保留同一组样本和参数,避免字号变化掩盖布局变化。

对于变小场景,我不把“所有内容都完整展示”写成不切实际的目标。当前代码用最小内容高度与最小边距维持基础可读性,但极端窄小面积下仍需要产品决定是缩放、滚动、折叠比较区,还是限制使用形态。现有页面只是一个实验页,维护记录必须如实写下这个边界。

固定样本 字号 字重和 AA 状态

记录初始窗口与截图

扩大容器面积

检查双栏边距和底部放大区

缩小容器面积

检查文本是否越界或重叠

切换一次样本并确认仍按新面积绘制

返回初始面积

核对无旧尺寸残影

复测时我还会看两个容易漏掉的动作。第一个是连续拖拽窗口,不只测一次跳变。这样可以观察面积事件快速到来时,页面是否始终使用最近尺寸。第二个是先切换参数、再改尺寸,确认快照面板和当前面板都跟随新的布局规则;不能只让“当前输出”适配,而把上次快照留在旧坐标。这个页面两块面板的比较价值很高,任何一块错位都会让维护判断失真。

给后来维护者的检查清单

我会在交接记录里写四个问题。每一次绘制是否都只从 canvasWidthcanvasHeight 推导坐标?安全区常量是否与外层覆盖控件的实际高度保持同步?发生面积变化时是否完整清空旧画面?极小面积的产品策略是否被明确,而不是依赖偶然的裁剪?这些问题听着像排版细节,但它们决定了同一个功能在运行环境变化后还能不能被人正常使用。

也别把 Math.floor 当成无关紧要的处理。Canvas 坐标带小数时,边框和文字在不同缩放下可能出现难解释的轻微模糊。现有页面将面积和若干尺寸取整,是为了让计算路径更稳定。它不保证所有图形都绝对锐利,却减少了“同一个尺寸计算出半像素坐标”的不确定性。若后续需要高精度绘图,应单独评估设备像素比和渲染策略,不应在这里凭感觉加补丁。

下一版可以优化什么

以下内容是设计设想,不是现有页面能力。下一版可以把双栏布局抽成明确的宽度阈值策略:宽度不足时改为上下排列,并在页面上提示当前比较模式;还可以记录最近一次有效面积,方便维护人员复现问题窗口。若要支持更复杂的分屏场景,也可以增加专门的尺寸测试面板,把安全区、实际画布尺寸和每个区域的计算值显示出来。它们需要新的产品决定和实现工作,不能当作当前代码已经提供的保证。

我现在复盘这次问题,会把“画布铺满了容器”与“内容按容器正确重排”分开看。前者只是组件尺寸,后者才是绘制逻辑。只要所有坐标都回到当前面积重新计算,Canvas 的维护就不会被第一次调好的 1280×720 绑住。

必要条件|故障前置排查

前置故障 检查位置 处理动作
SDK/API缺失 DevEco Studio SDK Manager、build-profile.json5 安装 API 24 并重新同步工程
Kit未引入或版本不匹配 当前页面 import 和编译错误 按 API 24 文档修正对应 Kit/API
页面或模块未配置 main_pages.jsonmodule.json5 登记页面并补齐 Stage/权限配置
权限被拒绝 系统应用权限、abilityAccessCtrl 返回值 重新授权;拒绝时保持未运行状态
系统能力不可用 Camera、MapKit、SpeechKit、VisionKit 或文件运行状态 切换支持设备或走页面声明的降级路径

排查图片固定覆盖 SDK 配置、设备授权和运行版本;复杂技术再增加权限、能力和回调状态图。01要能看到 API 24/构建工具,02要能看到真实授权状态,03要能看到设备版本和能力状态。

SDK/API 故障排查:

在这里插入图片描述
在这里插入图片描述

权限故障排查后:

在这里插入图片描述

MapKit文章在地图服务故障排查后补充 AppGallery Connect 检查:项目、包名、签名、MapKit 服务开通状态和应用服务凭据/授权配置必须一致;不要用含密钥的控制台截图。 在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐