HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图 原创 精华

中软小助手
发布于 2022-5-18 10:38
浏览
3收藏

作者:梁青松

前言

最近项目中有柱状图的功能,看了下JS中的组件chart,发现并不适用要求,研究之后决定用canvas动手画一个。

项目说明

本项目基于ArkUI中JS扩展的类Web开发范式,关于语法和概念直接看官网官方文档地址:基于JS扩展的类Web开发范式1 基于JS扩展的类Web开发范式2

工具版本:DevEco Studio 3.0 Beta2

SDK版本:3.0.0.1(API Version 7 Beta2)

提供画布组件,用于自定义绘制图形:画布组件canvas

效果演示

默认选中第一条,点击柱状图,切换选中效果。

但是发现绘制复杂一点的内容,调用清屏接口clearRect()后,重新绘制内容会闪烁,在官网论坛上面问了其他人,也都有遇到此问题,希望官方早点修复这个bug

HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区

使用到的API

方法/属性 解释
getBoundingClientRect() 获取组件大小和位置信息
getContext() 获取canvas绘图上下文对象
save() 对当前的绘图上下文进行保存
beginPath() 创建一个新的绘制路径
moveTo() 路径从当前点移动到指定点
lineTo() 从当前点到指定点进行路径连接
stroke() 进行边框绘制操作
arc() 绘制弧线路径
fill() 对封闭路径进行填充
restore() 对保存的绘图上下文进行恢复
strokeStyle 属性:设置描边的颜色
lineWidth 属性:设置绘制线条的宽度
fillStyle 属性:指定绘制的填充色
font 属性:设置文本绘制中的字体样式
textAlign 属性:设置文本绘制中的文本对齐方式
lineCap 属性:指定线端点的样式

实现步骤

组件设置了上下左右间距,所以内容区域宽度 = 组件宽度 - 左间距 - 右间距,内容区域的高度 = 组件高度 - 上间距 - 下间距

1、画横线(x轴)、右边的文字

HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区

// 获取画布组件
const element = this.$refs.canvas;
// 获取绘图上下文
const ctx = element.getContext('2d', {antialias: true});

// 获取组件大小和位置信息
const rect = element.getBoundingClientRect()
// 测量y轴最大数的文本长度
const yTextMaxWidth = ctx.measureText('' + this.yAxisMaxValue).width

// x轴起始坐标
const xAxisStart = this.paddingLeft
// x轴结束坐标 = 组件宽度 - 右间距 - y轴文字最大宽度
const xAxisEnd = rect.width - this.paddingRight - yTextMaxWidth

// y轴初始坐标
const yAxisStart = this.paddingTop
// y轴结束坐标 = 组件高度 - 底间距 - 底部文字高度 - 文字顶间距
const yAxisEnd = rect.height - this.paddingBottom - 10 - this.xAxisTextTopPadding

// y轴初始值-画文字
let yValue = this.yAxisMaxValue
// y轴平均值-画文字
let yAverageValue = (this.yAxisMaxValue - this.yAxisMinValue) / this.yAxisDivide

// y轴初始坐标
let yAxis = yAxisStart
// 画横线、画右边文字
while (yAxis <= yAxisEnd) {
    // 画线
    // 对当前的绘图上下文进行保存
    ctx.save()
    // 线颜色
    ctx.strokeStyle = this.xAxisColor
    // 创建一个新的绘制路径
    ctx.beginPath()
    // 线条的宽度
    ctx.lineWidth = this.xAxisWidth
    // 路径从当前点移动到指定点
    ctx.moveTo(xAxisStart, yAxis)
    // 从当前点到指定点进行路径连接
    ctx.lineTo(xAxisEnd, yAxis)
    // 进行边框绘制操作
    ctx.stroke();
    // 对保存的绘图上下文进行恢复
    ctx.restore()

    // 画文本
    ctx.save()
    // 指定绘制的填充色
    ctx.fillStyle = this.yAxisTextColor
    // 设置文本绘制中的字体样式
    ctx.font = this.yAxisTextFont
    // 设置文本绘制中的文本对齐方式:文本右对齐
    ctx.textAlign = 'right'
    // 绘制填充类文本
    ctx.fillText('' + yValue, xAxisEnd + yTextMaxWidth, yAxis + 2.5)
    ctx.restore()

    // 右边文本
    yValue -= yAverageValue

    // 更新y轴坐标:每次加上y轴等分
    yAxis += (yAxisEnd - yAxisStart) / this.yAxisDivide
}

2、画圆柱图、和底部文字

HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区
HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区

 // 画x轴的实际宽度,两边柱状图不靠边
 const xDrawWidth = xAxisEnd - xAxisStart - this.xAxisPadding * 2
 // 起始点
 let xAxis = this.paddingLeft + this.xAxisPadding
 // x轴平均值
 let xAverageWidth = (xDrawWidth) / (this.chartData.length - 1)

 // 画x轴上的文字和数据
 for (let i = 0; i < this.chartData.length; i++) {
     const item = this.chartData[i]

     // 画文本
     ctx.save()
     ctx.fillStyle = this.xAxisTextColor
     ctx.font = this.xAxisTextFont
     ctx.textAlign = 'center'
     ctx.fillText(item.xData, xAxis, rect.height - this.paddingBottom)
     ctx.restore()

     // 画柱图
     ctx.save()
     // 创建一个新的绘制路径
     ctx.beginPath()
     // 线条的宽度,设置到最小,因为我们需要填充圆柱     
     ctx.lineWidth = 0.1
     // 路径从当前点移动到指定点
     ctx.moveTo(xAxis - this.columnarWidth / 2, yAxisEnd - this.xAxisWidth)
     // 根据数据比例得出 画的高度
     const yDrawHeight = (yAxisEnd - yAxisStart) * item.yData / this.yAxisMaxValue
     // 从y轴结束开始画,值结束的坐标:yAxisValueEnd 为了显示圆角:+ 柱图宽度/2(圆的半径)
     const yAxisValueEnd = yAxisEnd - yDrawHeight + this.xAxisWidth + this.columnarWidth / 2
     // 从当前点到指定点进行路径连接
     ctx.lineTo(xAxis - this.columnarWidth / 2, yAxisValueEnd)
     // 画圆角
     ctx.arc(xAxis, yAxisValueEnd, this.columnarWidth / 2, 3.14, 0)
     // 从当前点到指定点进行路径连接
     ctx.lineTo(xAxis + this.columnarWidth / 2, yAxisEnd - this.xAxisWidth)
     // 进行边框绘制操作
     ctx.stroke()
     // 填充颜色
     ctx.fillStyle = this.columnarColor
     // 填充
     ctx.fill()
     ctx.restore()

     //  更新x轴坐标:每次加上x轴等分
     xAxis += xAverageWidth
 }

3、点击选中效果:画柱图时,记录x轴的位置

HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区
HarmonyOs - ArkUI(JS)画布组件canvas之自定义柱状图-鸿蒙开发者社区

 // 存入索引和对应的坐标
 this.columnarXArray = []
 // 画x轴上的文字和数据
 for (let i = 0; i < this.chartData.length; i++) {
     const item = this.chartData[i]
     const columnarX = {
         index: i, columnarX: xAxis
     }
     // 记录x轴,索引和对应的坐标
     this.columnarXArray.push(columnarX) 
     
     // 画文本
     ......
     // 文本颜色
     ctx.fillStyle = this.selectIndex === i ? this.selectXAxisTextColor : this.xAxisTextColor
     ......
     
     // 画选中的线
     if(this.selectIndex === i){
         // 画线
         ctx.save()
         ctx.strokeStyle = this.yAxisColor
         ctx.lineCap = 'butt'
         ctx.lineWidth = this.yAxisWidth
         ctx.beginPath()
         ctx.moveTo(xAxis, yAxisEnd - this.xAxisWidth)
         ctx.lineTo(xAxis, this.paddingTop)
         ctx.stroke();
         ctx.restore()
     }
     
     // 画柱图
     ......
     // 填充颜色
     ctx.fillStyle = this.selectIndex === i ? this.selectColumnarColor : this.columnarColor
     ......
 } 

根据触摸事件的坐标,判断是否在范围内,更新选中的索引

// 触摸按下
onTouchStart(event) {
    console.log(event.touches[0].localX)
    // 点击x坐标,相对于组件
    const clickX = event.touches[0].localX
    let lastSelectIndex = this.selectIndex
    // 筛选出点击的柱图索引
    for (let i = 0; i < this.columnarXArray.length; i++) {
        let item = this.columnarXArray[i]
        if (Math.abs(item.columnarX - clickX) <= this.columnarWidth + 5) {
            this.selectIndex = item.index
            break
        }
    }
    // 重新绘制
    if (this.selectIndex !== lastSelectIndex) {
        this.draw()
    }
}

项目地址

完整代码:https://gitee.com/liangdidi/HistogramDemo

总结

此项目并没有特别复杂的地方,注释也很详细,主要是xy轴的起始、结束位置的计算,屏幕的原点坐标(0,0)是在左上角,最后根据系统提供的api画出想要的效果。有些效果看起来很复杂,但是一步一步的拆解,懂得其原理之后,多练多用,也能做出炫酷的效果。

每天进步一点点、需要付出努力亿点点。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-5-18 10:41:34修改
6
收藏 3
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

自己画也太酷了,感谢大佬分享方法

1
回复
2022-5-18 10:58:38
鸿联
鸿联

非常实用

1
回复
2022-5-18 13:38:32
回复
    相关推荐