HarmonyOS7 用 Canvas 画柱状图并不难:CanvasBarChartBasics 全流程拆解
文章目录前言这页案例的重点是什么完整代码data 和 labels 是图表的数据骨架为什么一定要先留 padding坐标轴和基线怎么画网格线为什么很关键柱宽和间距怎么分配柱子的高度怎么从数据映射出来顶部数值和底部标签为什么值得保留颜色数组为什么也值得单独设计这个案例怎么改成业务图表常见坑提醒写在最后前言一提到数据可视化很多人第一反应就是找图表库。这个思路没问题但有些场景下你未必需要一整套库。比如只是做一个轻量周报图、设备状态面板、运营数据卡片自己用Canvas画一个简单柱状图反而更灵活依赖也更轻。问题是图表跟普通图形不一样。它不是“画几个柱子”就完了还涉及坐标区域、留白、标签、刻度、数值位置。这也是为什么不少人一开始觉得图表很难。这个案例正好给了一个很好的起点手写一张最基础的柱状图。这页案例的重点是什么它不是要做一个专业图表库而是把最基础的数据图表达清楚。里面已经包含了柱状图最关键的几块结构数据数组和标签数组画布内边距Y 轴和底部基线网格线与刻度文字柱子本身顶部数值和底部标签你把这些关系弄明白以后换成折线图、面积图、进度图思路都能迁移。完整代码import{DEMO_BG_COLOR,DEMO_CARD_COLOR,DEMO_THEME_COLOR,DEMO_SUBTEXT_COLOR}from./typesinterfacePaddingConfig{top:numberbottom:numberleft:numberright:number}EntryComponentstruct CanvasBarChartBasics{StateisShow:booleantrueprivatesettings:RenderingContextSettingsnewRenderingContextSettings(true)privatecontext:CanvasRenderingContext2DnewCanvasRenderingContext2D(this.settings)privatedata:number[][65,80,45,90,55,75,60]privatelabels:string[][周一,周二,周三,周四,周五,周六,周日]build(){Column(){if(this.isShow){Scroll(){Column({space:16}){Text(Canvas 柱状图).fontSize(16).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)Column(){Text(每周数据统计).fontSize(13).fontColor(#4D96FF).margin({bottom:8})Column(){Canvas(this.context).onReady((){constw380consth220constpadding:PaddingConfig{top:20,bottom:40,left:40,right:20}constchartWw-padding.left-padding.rightconstchartHh-padding.top-padding.bottomconstbarWchartW/this.data.length*0.6constgapchartW/this.data.length*0.4this.context.clearRect(0,0,w,h)this.context.strokeStyle#E0E0E0this.context.lineWidth1this.context.beginPath()this.context.moveTo(padding.left,padding.top)this.context.lineTo(padding.left,padding.topchartH)this.context.lineTo(w-padding.right,padding.topchartH)this.context.stroke()for(leti0;i4;i){constypadding.topchartH*(1-i/4)this.context.strokeStyle#F0F0F0this.context.beginPath()this.context.moveTo(padding.left,y)this.context.lineTo(w-padding.right,y)this.context.stroke()this.context.font10px sans-serifthis.context.fillStyle#AAAAAAthis.context.fillText(25*i,padding.left-30,y3)}constcolors[#4D96FF,#6BCB77,#FFD93D,#FF6B6B,#9B59B6,#FFA500,#4ECDC4]for(leti0;ithis.data.length;i){constxpadding.left(chartW/this.data.length)*igap/2constbarH(this.data[i]/100)*chartHconstypadding.topchartH-barHthis.context.fillStylecolors[i]this.context.fillRect(x,y,barW,barH)this.context.font10px sans-serifthis.context.fillStyle#666666this.context.textAligncenterthis.context.fillText(this.data[i],xbarW/2,y-5)this.context.fillText(this.labels[i],xbarW/2,padding.topchartH18)}}).width(100%).height(240).backgroundColor(#FAFAFA).borderRadius(8)}}.backgroundColor(DEMO_CARD_COLOR).padding(12).borderRadius(12)Column({space:6}){Text(数据详情).fontSize(14).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)Row(){ForEach(this.labels,(label:string,_:number){Text(label).fontSize(11).fontColor(#666666).layoutWeight(1).textAlign(TextAlign.Center)})}.width(100%)Row(){ForEach(this.data,(val:number,_:number){Text(val).fontSize(13).fontColor(#1A1A1A).fontWeight(FontWeight.Bold).layoutWeight(1).textAlign(TextAlign.Center)})}.width(100%)}.width(100%).backgroundColor(#E8F4FD).padding(14).borderRadius(12)}.width(100%)}}Text(CanvasBarChartBasics - 绘制每周柱状图).fontSize(12).fontColor(DEMO_SUBTEXT_COLOR).margin({top:12})}.width(100%).height(100%).backgroundColor(DEMO_BG_COLOR).padding(16)}}data和labels是图表的数据骨架案例先定义了两组数组privatedata:number[][65,80,45,90,55,75,60]privatelabels:string[][周一,周二,周三,周四,周五,周六,周日]这就是图表最基本的输入。data决定柱子的高度labels决定底部每根柱子对应的说明。很多人一开始就急着画其实真正应该先想清楚的是数据结构长什么样。如果输入结构乱后面坐标、标签、颜色都会跟着乱。为什么一定要先留padding这句是全篇里最容易被低估的代码constpadding:PaddingConfig{top:20,bottom:40,left:40,right:20}图表不是填满整块画布就完事。你必须给坐标轴、刻度、标签、顶部数值留空间。所以这里把画布总尺寸w、h再减去内边距得到真正的图表可用区域constchartWw-padding.left-padding.rightconstchartHh-padding.top-padding.bottom这个思路特别重要。以后你做任何可视化几乎都要先从“总空间”和“绘图区”拆开。坐标轴和基线怎么画案例用路径画了左侧轴线和底部基线this.context.beginPath()this.context.moveTo(padding.left,padding.top)this.context.lineTo(padding.left,padding.topchartH)this.context.lineTo(w-padding.right,padding.topchartH)this.context.stroke()这里其实把图表的边界感一下子立起来了。即使你还没画柱子用户也能感觉到“这是个数据区域”。很多轻量图表不一定要完整包围框但底线和一条纵向参考轴通常很有必要。网格线为什么很关键案例里用循环画了五条水平网格线for(leti0;i4;i){constypadding.topchartH*(1-i/4)this.context.moveTo(padding.left,y)this.context.lineTo(w-padding.right,y)}它的作用不是装饰而是帮助读数。没有网格线时用户只能看出哪根柱子高、哪根柱子低有了刻度线和左侧数值他才更容易估出这是 50、75 还是接近 100。数据图的本质不是“好看”而是“能读”。柱宽和间距怎么分配案例把每一列的总空间拆成两部分constbarWchartW/this.data.length*0.6constgapchartW/this.data.length*0.4这是一种非常实用的比例分配法。每组位置 60% 给柱子40% 给空隙。这样柱子不会挤在一起也不会显得太散。这类写法的好处是数据项数量变了整体仍然能保持相对平衡。柱子的高度怎么从数据映射出来真正的数据映射就在这两句constbarH(this.data[i]/100)*chartHconstypadding.topchartH-barH第一句把数据值映射成柱高。第二句决定柱子从哪里开始画。因为Canvas的y轴是向下增长的所以柱子要想“从底部往上长”就必须先算出顶部坐标。这一步是很多人最容易算反的地方。顶部数值和底部标签为什么值得保留案例里每根柱子都画了两个文字this.context.fillText(this.data[i],xbarW/2,y-5)this.context.fillText(this.labels[i],xbarW/2,padding.topchartH18)这两个信息点其实很关键。顶部数值让用户不必用眼睛估计底部标签让用户知道这一列到底代表哪一天。它们一上图表就从“可视块”变成了“可阅读的数据表达”。颜色数组为什么也值得单独设计案例给每根柱子配了不同颜色constcolors[#4D96FF,#6BCB77,#FFD93D,#FF6B6B,#9B59B6,#FFA500,#4ECDC4]这适合演示效果但真实业务里要看场景。如果强调每一天是独立分类多色可以如果强调同一维度的连续数据统一色系往往更专业。比如一组销售额柱子通常不必七种颜色全上。这个案例怎么改成业务图表稍微改一下这页就能变成很多业务组件。比如把labels换成设备编号做设备监测面板把labels换成月份做月度趋势卡片把data接成接口返回值做真实报表模块把颜色策略改成阈值色做异常预警图也就是说这个案例已经不是玩具了它其实是个可复用的最小图表骨架。常见坑提醒第一个坑是没有先减掉padding结果标签和柱子全贴边。第二个坑是textAlign改成center后忘了恢复不过这个案例后续没有再写别的内容所以暂时没出问题。第三个坑是数据最大值写死为 100。如果真实数据不是这个量级需要先做动态最大值计算。第四个坑是直接把太多标签塞到底部文字会重叠。数据一多布局策略要变。写在最后柱状图这件事看起来像是图形问题实际上更像空间和数据关系问题。只要你把绘图区、比例映射、标签位置和信息层级想清楚用Canvas画一张清爽的图并不难。这一页特别值得反复练因为它已经把Canvas从“画图”推进到了“表达数据”。这一步走通后面的应用场景会一下子宽很多。

相关新闻

AI智能体记忆系统:Cognee开源平台架构与LangChain集成实战

AI智能体记忆系统:Cognee开源平台架构与LangChain集成实战

1. 项目概述:当AI智能体开始拥有“记忆”最近在AI智能体(Agent)的圈子里,一个词被频繁提起:记忆。无论是构建一个能持续对话的客服助手,还是一个能根据历史交互调整策略的自动化流程,智能体如何…

2026/11/2 4:12:07 阅读更多 →
HarmonyOS7 Canvas 怎么动起来:CanvasAnimationLoop 动画循环详解

HarmonyOS7 Canvas 怎么动起来:CanvasAnimationLoop 动画循环详解

文章目录前言这页案例到底讲了什么完整代码angle 是这段动画的发动机startAnimation() 做的其实是“开循环”动画帧开始前为什么一定要 clearRect()旋转为什么要配合 translate() 使用save() 和 restore() 为什么不能少颜色跟着角度变化,是个很巧的示范stopAnimatio…

2026/11/3 1:44:09 阅读更多 →
Java 反射全套常用方法汇总

Java 反射全套常用方法汇总

反射核心围绕 Class、Constructor、Method、Field 四大API,下面分类整理,附带作用与区分要点。前提:获取类字节码对象 1.Class clazz 目标类.class;2. Class clazz Class.forName("全类名");3.对象.getClass()一、Class 类&#…

2026/11/3 1:44:13 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/3 1:09:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/2 10:46:11 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/2 10:47:40 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/3 2:27:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/2 10:46:31 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/2 10:46:26 阅读更多 →

日新闻

周新闻

月新闻