0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于鸿蒙原生ArkTS语法开发的图表组件--柱状图

陈姚丰 来源:jf_83680738 作者:jf_83680738 2025-03-16 16:01 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

大家好,我是陈杨。在上一篇文章中,我简要介绍了折线图的实现逻辑,并解释了整体图表的绘制规则。根据这些规则,我们还可以绘制更多种类的图表组件。在本期中,我将讲解如何实现柱状图,并引入了一个新的功能。鉴于柱状图跟折线图可共用的基础配置很多,我将不再重复介绍基础知识,如果你对此感兴趣,可以翻阅上一篇文章了解更多内容。

在开始技术讲解之前,我想插播一个消息。为了方便大家今后使用与图表相关的组件,我已经陆续封装了相关系列的组件,使其可以开箱即用。未来,我还将采取开源策略,希望大家共同分享创造更多实用的工具。目前,相关组件的使用文档地址为:[http://meichuang.org.cn/McBarChart]

进入正题,老规矩先把实现结构整理出来。结构中有一些我上篇文章已经讲过,所以这里不再做过多的阐述。直讲新内容

  • 公共属性
    • 画布的宽高
    • 画布内部间距
    • ...
  • 绘画坐标
    • 绘画x与y坐标轴
    • ...
  • 绘画柱状区
    • 绘画柱子
    • 绘画特性功能(文本标签
  • Tooltip(
    • 基本属性
    • 定位显示

绘画柱状区

在上一篇文章中,已经包含了定义公共属性和绘制坐标的代码。如果您对此感兴趣,可以去查看。本期的内容主要涵盖了柱状图区域的基本属性、柱子的绘制以及特性功能的实现。

绘画柱子

先绘画出柱状区的概览图:

image.png

通过概览图,我们可以得到以下步骤:首先计算出每个刻度的X坐标,然后将数值转换为对应的高度,并设置柱子的宽度,最后利用canvas的矩形绘制方法来绘制相应的柱子。接下来我们将详细介绍具体的算法

每个刻度的X坐标算法 :首先将实际数据长度length除以画布的宽度width,得到等分刻度。然后,将等分刻度乘以索引值即可得到每个刻度的X坐标。

数据转化高度算法 :首先将实际数据的最大值maxValue除以画布的高度height,得到缩放倍数。然后,使用每个刻度的实际数值乘以缩放倍数即可得到对应的高度值。

柱子宽度 :设置基础值,可以动态传参。

了解大概算法,我们将算法转换成代码。代码如下:

.onReady(() = > {  
  ...  
  // 上面是绘制x轴跟y轴的代码  
  // 绘画折线  
  const ySacle = (this.context.height - cSpace *2) / maxValue // 计算出y轴与实际最大值的缩放倍数  
  //连线
  for(var i=0; i< this.options.data.length; i++){  
    const dotVal = String(this.options.data[i].value)  
    const barW = 10
    // 画布的高度减去下边内部高度加x轴高度
    const barH = parseInt(dotVal * ySacle) 
    // 计算每个数值的x坐标值,减去barW/2是为了柱子能够居中显示
    const x = xSplitSpacing * (i + 1) + cSpace + maxNameW - barW / 2 
    // 由于画布的左边是从左上角开始计算的,用画布高度减去缩放后的高度得到柱子顶部的坐标
    const y = this.context.height - cSpace - barH 
    ctx.beginPath()
    ctx.rect( x, y, barW, barH)
    ctx.fillStyle = "green"
    ctx.fill()
    ctx.closePath()
  }  
  ctx.stroke();  
})

绘画特性功能(文本标签)

绘制文本标签其实也很简单。由于我们已经计算出每根柱子的起点坐标,所以只需要将计算得到的坐标减去文本的宽度和高度,就可以得到文本标签的位置。以下是具体的代码示例:

.onReady(() = > {  
  ...  
  // 上面是绘制x轴跟y轴的代码  
  // 绘画折线  
  const ySacle = (this.context.height - cSpace *2) / maxValue // 计算出y轴与实际最大值的缩放倍数  
  //连线
  for(let i = 0; i < this.options.data.length; i++){  
    const dotVal = String(this.options.data[i].value)  
    const barW = 10
    // 画布的高度减去下边内部高度加x轴高度
    const barH = parseInt(dotVal * ySacle) 
    // 计算每个数值的x坐标值,减去barW/2是为了柱子能够居中显示
    const x = xSplitSpacing * (i + 1) + cSpace + maxNameW - barW / 2 
    // 由于画布的左边是从左上角开始计算的,用画布高度减去缩放后的高度得到柱子顶部的坐标
    const y = this.context.height - cSpace - barH
    ... 绘画柱子
    
    // 绘制文本标签  
    const textWidth = this.context.measureText(dotVal).width; // 获取文字的长度  
    const textHeight = this.context.measureText(dotVal).height; // 获取文字的长度  
    this.context.fillText(dotVal, x - textWidth / 2, y - textHeight / 2); // 文字
  }  
  ctx.stroke();  
})

整个绘制基础柱状图的功能已经完成了。大家可以尝试使用,并根据自己的业务需求来实现相应的功能。希望这些代码能够对您有所帮助!

Tooltip(提示层)

在讲解完整个柱状图的绘画之后,接下来我们将探讨如何实现提示层功能。提示层功能在使用图表呈现数据时是必不可少的,它可以让数据更加直观地展示,同时增加图表的交互性,避免过于单调。

如果使用传统的 JavaScript 开发机制,实现提示层功能相对简单:点击图表内容,判断坐标获取对应索引数据,动态创建

元素来展示数据,计算画布和提示层的宽高,并决定提示层的最佳位置。这是大致的实现思路。

然而,在 ArkTS 语言中,我们需要解决以下两个问题:

  1. 无法动态创建
    元素。
  2. 无法实时获取元素的宽高。
  3. 无法触发组件之外的内容隐藏提示层。

针对这些问题,我们可以按照以下步骤将思路转化为代码并解决相应的问题。

绑定事件/创建动态组件

首先,对画布进行单击事件的绑定,并获取点击位置的 xy 值。然后循环遍历数据,对比判断 x 值是否大于对应索引的刻度值,如果大于,则记录对应的索引数据,否则继续判断。代码示例如下:

Canvas(this.context)
  .width('100%')
  .height('100%')
  .onReady(() = > {
    // 绘画内容区
  })
  .gesture(
    TapGesture({ count: 1 })
      .onAction((e: GestureEvent) = > {
         const ctx = this.context
         // 获取点击的x、y值
         let pos = {
          x: e.localX,
          y: e.localY
        }
        // 获取x轴的刻度等分
        let xSplitSpacing = parseInt(String((ctx.width - cSpace * 2 - maxNameW) / this.options.data.length))
        // 循环数据判断
        const activeObj = {}
        const activeX = null
        for(let i = 0; i < this.options.data.length; i++){  
           const item = this.options.data[i]
           if(pos.x > i * xSplitSpacing) {
               activeObj = item
               activeX = i * xSplitSpacing 
           }
        }
        // 显示提示层
        if(this.activeX !== null) {
           ....
        }
      })
  )

由于我们没有办法直接动态添加元素,那我们要先定义好一个组件来呈现我们的数据,动态控制显示跟隐藏。

@State tooltipInfo: InterfaceObj = {}
@State tooltipPos: InterfaceObj = {
    x: -100000,
    y: -100000
}

Column() {
  Canvas(this.context)
  .width('100%')
  .height('100%')
  .onReady(() = > {
    // 绘画内容区
  })
  .gesture(
    TapGesture({ count: 1 })
      .onAction((e: GestureEvent) = > {
        ...判断逻辑
        // 显示提示层
        if(this.activeX !== null) {
           this.tooltipInfo = activeObj
           this.tooltipPos.x = activeX
        }
      })
  )
 if(Object.keys(this.tooltipInfo).length) {
    Column () {
      Text(this.tooltipInfo.title)
      ForEach(this.tooltipInfo.data, (item, index) = > {
        Text(item.name + ':' + item.num)
      })
    }
 }
}

好的,利用ArkTS提供的渲染控制能力来动态显示元素节点是一个不错的解决方案。这样我们就成功地解决了无法动态添加节点的问题,并顺利完成了第一步。

定位适配显示

接下来,我们需要实现适配显示的定位功能,使提示层能够定位在鼠标点击的位置,并且不超出屏幕范围。在上面显示提示层时,我记录了点击图表时数据项对应的 X 坐标,这样就可以为提示层设置相对定位的 X 属性。至于 Y 轴的定位,我选择居中显示,当然你们也可以根据数据项的 Y 坐标进行定位。

在设置提示层的 X 坐标时,当点击右边最后几个数据项或者提示层内容较大时,可能会导致提示层超出画布内容,从而造成数据显示不全。解决这个问题的方法也比较简单:判断获取提示层自身的宽度加上 X 坐标是否大于画布宽度,如果大于,则证明超出了画布的范围。然后,将 X 坐标减去画布的宽度,就可以得到最终的 X 坐标。

然而,问题也随之而来。由于 ArkTS 没有提供直接获取某些元素宽度和高度的功能,一开始我以为无法继续下去了。但是,在仔细阅读官方文档之后,终于发现了一点线索。这里我就不卖关子了,这个 API 就是"组件区域变化事件"。你可以在官方文档中找到相关的信息:

组件区域变化事件,快速跳转

image.png

这个事件主要用于监听某个元素位置或尺寸的变化,并在变化发生时回调,提供最新的位置和尺寸信息。这正好符合我们的需求,因为我们的 X 坐标是不断变化的,这样我们就可以获取到元素的尺寸了。下面是完整的代码示例:

@State tooltipInfo: InterfaceObj = {}
@State tooltipPos: InterfaceObj = {
    x: -100000,
    y: -100000
}

Column() {
  Canvas(this.context)
  .width('100%')
  .height('100%')
  .onReady(() = > {
    // 绘画内容区
  })
  .gesture(
    // 点击画布相关事项
  )
 if(Object.keys(this.tooltipInfo).length) {
    Column () {
      Text(this.tooltipInfo.title)
      ForEach(this.tooltipInfo.data, (item, index) = > {
        Text(item.name + ':' + item.num)
      })
    }
    .position({
      x: this.tooltipPos.x,
      y: this.tooltipPos.y
    })
    .onAreaChange((oldValue: Area, newValue: Area) = > {
        const { x } = this.tooltipInfo.pos
        const { width: W, height: H } = this.context
        const { width, height } = newValue
        if (x + 40 + width > W - 10) {
          this.tooltipPos.x = x - width + 20
        } else {
          this.tooltipPos.x = x + 40
        }
        this.tooltipPos.y = H / 2 - height / 2
    })
 }
}

结束

讲到这里,我们已经完成了柱状图组件以及提示层功能的开发,并成功封装成了组件,即将发布到相关的文档上。希望大家在使用过程中能够学到很多知识。如果你有任何需要交流的地方,请在下面留言评论,我会第一时间回复你。感谢大家的支持!

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    60

    文章

    2856

    浏览量

    45340
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    开源啦!!!基于鸿蒙ArkTS封装的图表组件《McCharts》,大家快来一起共创

    的地方请大家高抬贵手,宽容一下,谢谢。 这次主要是给大家带来一个重磅消息,就是我自己使用鸿蒙ArkTS语法开发图表
    发表于 03-15 15:21

    lcd12864显示柱状图

    单片机用stc89c52,将采集的电压用柱状图显示,会动的那种
    发表于 05-26 23:02

    《Visual C# 2008程序设计经典案例设计与实现》---柱状图表分析

    《Visual C# 2008程序设计经典案例设计与实现》---柱状图表分析[hide][/hide]
    发表于 05-13 19:21

    《Visual C# 2008程序设计经典案例设计与实现》---柱状图表的升序和降序

    《Visual C# 2008程序设计经典案例设计与实现》---柱状图表的升序和降序[hide][/hide]
    发表于 05-13 19:23

    《Visual C# 2008程序设计经典案例设计与实现》---利用Windows组件打印数据库数据柱状图表

    《Visual C# 2008程序设计经典案例设计与实现》---利用Windows组件打印数据库数据柱状图表
    发表于 05-22 21:03

    请问labview中使用net的chart控件如何实现柱状图

    本帖最后由 一只耳朵怪 于 2018-6-19 09:44 编辑 最近有个项目需要画柱状图,并在柱状图上添加横坐标文本,并在柱状图中添加数值标记,百度搜索了一大圈,也在QQ群里请教了很多人,最终实现了上述功能,有需要的朋友
    发表于 06-15 13:58

    Labview调用OWC11制作柱状图

    Labview调用OWC11制作柱状图
    发表于 02-22 11:00

    Matplotlib绘制柱柱状图、直方图、条形的使用语法

    Matplotlib - 柱状图、直方图、条形 bar() & barh() 所有用法详解
    发表于 04-18 08:17

    Labview 柱状图

    请教一下,labview怎么生成下图这种柱状图,可以标识上下限数值,标识数量。
    发表于 09-21 15:38

    LabVIEW强大的生产产量---柱状图表---嵌入MES系统

    ` 本帖最后由 wcl86 于 2021-5-14 15:28 编辑 LabVIEW做一些柱状图可以链接至你们的小MES系统之类的,希望能帮助大家啦,文件可回复下载附件通用逻辑运动通用视觉`
    发表于 08-06 17:25

    怎么将每小时得到的合格和不合格的结果用柱状图表示出来?

    怎么将每小时得到的合格和不合格的结果用柱状图表示出来,类似下面这样的
    发表于 09-24 11:54

    怎样用MATLAB去给柱状图加数据标签呢

    怎样用MATLAB去给柱状图加数据标签呢?记录一下matlab画柱状图的过程
    发表于 11-19 07:13

    HarmonyOS/OpenHarmony应用开发-ArkTS语言基本语法说明

    的封装和复用UI描述。 @Extend/@Style:扩展内置组件和封装属性样式,更灵活地组合内置组件。 stateStyles:多态样式,可以依据组件的内部状态的不同,设置不同样式。*附件:HarmonyOSOpenHarmo
    发表于 06-01 10:25

    柱状图Histogram Plot

    柱状图 Histogram Plot资料免费下载。
    发表于 06-01 17:00 17次下载

    如何用seabron生成柱状图和散点图

    生成柱状图 柱状图是我们经常会见到的数据图表,每个柱状都表示一组数据 import seaborn import matplotlib.pyplot as pltmonths = [
    的头像 发表于 10-07 11:20 1912次阅读
    如何用seabron生成<b class='flag-5'>柱状图</b>和散点图