ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Harmony os 技术实战|拼豆制图26:16 色图例如何撑开 70×70 导出画布

Harmony os 技术实战|拼豆制图26:16 色图例如何撑开 70×70 导出画布 标签Harmony os、ArkTS、离屏渲染、动态布局、PNG 导出编号施工图最容易被忽略的区域往往不是 70×70 主网格而是网格下面的色号图例。主网格尺寸固定图例却会随着颜色数量增加4 种颜色只占一行5 种颜色必须换到第二行16 种颜色正好铺满四行。若画布仍使用固定高度最下面一行色号或页脚会直接落到缓冲区外若一开始预留很大的空白又会让低色数图纸带着一截无意义的尾部。本篇不从“让布局看起来差不多”出发而是拆解工程里的整数几何calculateSize()先计算 RGBA 缓冲区drawLegend()再用同一组行列常量落点。以当前 70×70、16 色图纸为样本可以把最终尺寸精确推导为1632×1868并解释为什么每增加一行图例内存会多出221,952 字节。一、先把画布拆成六段而不是写一个经验高度PatternExportService把尺寸相关常量集中在同一个服务中privatestaticreadonlycellSize:number22;privatestaticreadonlymarginLeft:number64;privatestaticreadonlymarginTop:number72;privatestaticreadonlymarginRight:number28;privatestaticreadonlyaxisBottom:number38;privatestaticreadonlylegendTop:number34;privatestaticreadonlylegendRowHeight:number34;privatestaticreadonlylegendColumns:number4;privatestaticreadonlywatermarkHeight:number48;画布宽度由左边距、网格宽度和右边距组成高度则由顶部、网格、下坐标轴、图例间隔、图例行和页脚组成width marginLeft pattern.width × cellSize marginRight height marginTop pattern.height × cellSize axisBottom legendTop legendRows × legendRowHeight watermarkHeight这比一个固定的canvasHeight更重要因为每一段都能在导出结果中找到对应区域。出现裁切时可以直接判断是网格尺寸算错、图例行数算少还是页脚没有纳入总高度。二、图例行数只有一条公式但边界值决定成败四列布局下行数等于颜色数除以 4 后向上取整。工程中还用Math.max(1, ...)保证至少留出一行constlegendRowsMath.max(1,Math.ceil(pattern.colorStats.length/PatternExportService.legendColumns));几个关键输入如下colorStats.length计算过程图例行数0max(1, ceil(0 / 4))14max(1, ceil(4 / 4))15max(1, ceil(5 / 4))215max(1, ceil(15 / 4))416max(1, ceil(16 / 4))4这里最危险的不是 16而是 5。只要实现误用了向下取整4 色图仍然正常5 色图的第五项却会占用未预留的第二行。测试样本因此不能只选整除值至少要覆盖4、5、16。三、70×70、16 色画布为何恰好是 1632×1868主网格每格 22 像素因此网格宽高均为70 × 22 1540 px画布宽度为64 1540 28 1632 px16 种颜色在四列布局中占 4 行高度为72 1540 38 34 4 × 34 48 72 1540 38 34 136 48 1868 px最终 RGBA 缓冲区包含1632 × 1868 × 4 12,194,304 bytes约为 11.63 MiB。这个数字是渲染阶段的原始像素占用并不是压缩后的 PNG 文件大小。理解两者差异很重要图片保存后可能只有几百 KiB但生成过程仍要先容纳完整 RGBA 数组。每新增一行图例高度增加 34 像素对应额外内存1632 × 34 × 4 221,952 bytes约 216.75 KiB。动态高度不仅避免裁切也让颜色较少的图纸少申请不必要的缓冲区。四、尺寸计算和绘制必须共享同一组常量尺寸正确并不代表内容一定落在正确位置。drawLegend()从主网格下方开始绘制conststartX42;conststartYmarginToppattern.height*cellSizeaxisBottom18;constcolWidthMath.floor((canvas.width-84)/legendColumns);对 1632 像素宽的画布列宽是(1632 - 84) / 4 387 px四列的起始 x 坐标依次为42、429、816、1203。70 行网格下第一行图例的 y 坐标为72 70 × 22 38 18 1668 px四行 y 坐标则是1668、1702、1736、1770。每项用一个 24×24 色块开头色号放在x 32数量放在x 90constcoli%4;constrowMath.floor(i/4);constxstartXcol*colWidth;constystartYrow*34;canvas.fillRect(x,y,24,24,color);canvas.strokeRect(x,y,24,24,borderColor,1);drawText(canvas,stat.colorCode,x32,y4,2,textColor);drawText(canvas,${stat.count},x90,y6,1,mutedColor);calculateSize()与drawLegend()都依赖legendColumns4和legendRowHeight34。如果一处改成五列、另一处仍按四列计算画布虽然足够大项目却会出现空白行或页脚间距异常。因此这两个值应该被视作跨函数契约而不是两个方便修改的数字。五、一维统计数组如何稳定映射到四列网格i % 4决定列floor(i / 4)决定行这种映射有两个好处数据层继续保持一维BeadColorStat[]无需为了界面重新组装二维数组。第 4、5、8、9 项的换行边界天然由整数运算确定不依赖测量文字宽度。16 项的索引分布如下第 0 行 0 1 2 3 第 1 行 4 5 6 7 第 2 行 8 9 10 11 第 3 行12 13 14 15由于工程的资源色板最多支持 16 个单字符索引四列布局刚好形成 4×4 的完整区域。若未来允许 20 或 24 色不需要改映射算法只会自然增加到 5 或 6 行。六、真正需要统一的是“颜色数量”的语义当前仓库中的countColors()会遍历整张色板并把计数为 0 的颜色也放入colorStatsfor(leti0;ipalette.length;i){// 统计同一 colorId 的格子数量stats.push({colorId:color.id,colorCode:color.code,colorName:color.name,hex:color.hex,count});}这意味着colorStats.length表示“色板容量”不一定表示“实际使用颜色数”。对 16 色资源而言即使某两个色号在图中没有出现图例仍可能保留 16 项和四行空间。这不是单纯的对错问题而是产品语义选择若图例用于备料只展示count 0的颜色更紧凑。若图例用于说明完整色板保留 0 数量项更完整。关键是尺寸计算与绘制必须消费同一份数组。不能在calculateSize()前过滤一次、在drawLegend()中又使用原数组否则行数和真实内容会再次失配。更稳妥的做法是先生成一次visibleStats之后所有图例逻辑只读取它。七、空图例为何仍保留一行Math.max(1, ...)让 0 色图也保留 34 像素。此时COLOR LEGEND标题仍会出现循环内部不绘制色块。这样做有两个实际好处页脚不会突然上移到坐标轴旁边版式仍有稳定的视觉分区。空数据仍能导出一张结构完整的诊断图而不是出现高度为 0 的特殊分支。但如果业务明确不允许空图纸入口应在渲染前返回错误而不是依赖这 34 像素掩盖数据异常。布局兜底负责内存安全业务校验负责解释为什么数据为空两者职责不同。八、页脚不被覆盖要看最后一行的真实底边16 色时最后一行起点是 1770色块高度 24因此其底边在 1794。页脚文字从canvas.height - 40开始也就是 1828。两者之间仍有 34 像素间隔。这个余量来自legendRowHeight、watermarkHeight与页脚落点的共同结果。若以后把色块从 24 放大到 32行高仍保持 34只剩 2 像素垂直间隔若再增加描边或第二行说明文字就可能与下一行重叠。因此调整图例视觉样式时不能只改fillRect()还要重新核对itemVisibleHeight legendRowHeight lastItemBottom watermarkTop watermarkBottom canvas.height九、用算式和关键像素验证而不是只看整图这类离屏布局适合分三层验证1. 几何结果70×70、16 色1632×1868。70×70、5 色图例 2 行高度1800。70×70、4 色图例 1 行高度1766。2. 关键位置第一列起点 x 为 42。第四列起点 x 为 1203。第五项必须位于第二行即 y 为 1702。第十六项必须位于第四行第四列。3. 边界像素最后一行色块不能越过 RGBA 缓冲区。图例色块边缘应存在 1 像素描边。页脚像素应位于最后一行图例之后。整图预览可以发现明显错位但只有这些固定数值能快速定位 off-by-one、行数取整和高度遗漏。十、可继续演进的布局接口当图例未来需要支持不同列数、长色号或横屏导出时可以把几何计算提取为一个纯结果interfaceLegendLayout{columns:number;rows:number;rowHeight:number;columnWidth:number;startX:number;startY:number;totalHeight:number;}calculateSize()与drawLegend()共同接收LegendLayout就不会各自重复公式。列数也可以根据画布宽度选择但仍要先得到确定的布局结果再申请像素缓冲区不能边画边修改高度。小结Harmony os 的 ArkTS 离屏导出没有自动排版系统画布多高、图例放在哪里都必须在写入第一个像素前算清楚。当前实现用四列、34 像素行高和向上取整把颜色数量稳定映射为图例行数70×70、16 色样本最终得到 1632×1868 的画布每增加一行只增加 34 像素高度和约 216.75 KiB RGBA 内存。比公式本身更重要的是三条工程约束尺寸计算和绘制共享常量过滤后的统计数组只能生成一次并被两处共同消费验证必须覆盖 4 到 5 的换行边界。守住这三点图例就能真正参与画布布局而不会成为导出链路中最后才暴露的裁切点。
返回列表