HarmonyOS-MPChart根据y轴刻度绘制渐变色曲线

news2025/1/15 21:05:13

本文是基于鸿蒙三方库mpchart(OpenHarmony-SIG/ohos-MPChart)的使用,自定义绘制方法,绘制一条颜色渐变的曲线。

mpchart本身的绘制功能是不支持颜色渐变的曲线的,只支持渐变色填充大块颜色。那么当我们的需求曲线根据y轴的刻度发生变化,就需要自定义绘制方法了。

从图中我们可以看到,左边的y轴是一个从底部到顶部颜色渐变的直线,从绿色渐变到红色,而且数据曲线根据y轴刻度做同样的渐变色。所以我们需要修改的就是两个部件的绘制效果,一个是左y轴的绘制效果,一个是数据线的绘制效果。它们涉及到两个绘制类:

YAxisRenderer 和 LineChartRenderer

首先来看数据线的绘制方法,因为这里以曲线为例,所以我们只需要修改绘制曲线的方法,找到mpchart源码中LineChartRenderer类的drawCubicBezier方法,我们自定义一个MyDataRender类继承自LineChartRenderer类,然后将LineChartRenderer类的drawCubicBezier方法复制到自定义的类中,在其基础上做修改:

主要的修改就是将canvas的strokeStyle设置为我们通过createLinearGradient方法创建的渐变色效果。这段代码创建了一个从底部到顶部的垂直渐变,颜色从绿色到红色变化,并将这个渐变应用到了Canvas的描边样式中。

完整代码如下:

import { EntryOhos, ILineDataSet, Style, Transformer, Utils, LineChartRenderer } from '@ohos/mpchart';

export default class MyDataRender extends LineChartRenderer{

  protected drawCubicBezier(c: CanvasRenderingContext2D, dataSet: ILineDataSet) {
    if (!this.mChart || !this.mXBounds) {
      return;
    }
    const phaseY: number = this.mAnimator ? this.mAnimator.getPhaseY() : 1;
    const trans: Transformer | null = this.mChart.getTransformer(dataSet.getAxisDependency());

    this.mXBounds.set(this.mChart, dataSet);

    const intensity: number = dataSet.getCubicIntensity();

    let cubicPath = new Path2D();
    // cubicPath.reset();
    if (this.mXBounds.range >= 1) {
      let prevDx: number = 0;
      let prevDy: number = 0;
      let curDx: number = 0;
      let curDy: number = 0;

      // Take an extra point from the left, and an extra from the right.
      // That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart.
      // So in the starting `prev` and `cur`, go -2, -1
      // And in the `lastIndex`, add +1

      const firstIndex: number = this.mXBounds.min + 1;
      const lastIndex: number = this.mXBounds.min + this.mXBounds.range;

      let prevPrev: EntryOhos | null;
      let prev: EntryOhos | null = dataSet.getEntryForIndex(Math.max(firstIndex - 2, 0));
      let cur: EntryOhos | null = dataSet.getEntryForIndex(Math.max(firstIndex - 1, 0));
      let next: EntryOhos | null = cur;
      let nextIndex: number = -1;

      if (cur === null) return;

      Utils.resetContext2DWithoutFont(c, this.mRenderPaint);
      // let the spline start
      cubicPath.moveTo(cur.getX(), cur.getY() * phaseY);

      for (let j: number = this.mXBounds.min + 1; j <= this.mXBounds.range + this.mXBounds.min; j++) {
        prevPrev = prev;
        prev = cur;
        cur = nextIndex === j ? next : dataSet.getEntryForIndex(j);

        nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j;
        next = dataSet.getEntryForIndex(nextIndex);

        prevDx = (cur.getX() - prevPrev.getX()) * intensity;
        prevDy = (cur.getY() - prevPrev.getY()) * intensity;
        curDx = (next.getX() - prev.getX()) * intensity;
        curDy = (next.getY() - prev.getY()) * intensity;

        cubicPath.bezierCurveTo(
          prev.getX() + prevDx,
          (prev.getY() + prevDy) * phaseY,
          cur.getX() - curDx,
          (cur.getY() - curDy) * phaseY,
          cur.getX(),
          cur.getY() * phaseY
        );
      }
    }

    // if filled is enabled, close the path
    if (dataSet.isDrawFilledEnabled()) {
      let cubicFillPath: Path2D = new Path2D();
      // cubicFillPath.reset();
      cubicFillPath.addPath(cubicPath);

      if (c && trans) {
        this.drawCubicFill(c, dataSet, cubicFillPath, trans, this.mXBounds);
      }
    }

    // this.mRenderPaint.setColor(dataSet.getColor());
    // this.mRenderPaint.setStyle(Style.STROKE);

    let grad = c.createLinearGradient(0, this.mChart.getHeight(), 0, 0);
    grad.addColorStop(0.0, '#00ff00')
    grad.addColorStop(1.0, '#ff0000')
    c.strokeStyle = grad;
    if (trans) {
      cubicPath = trans.pathValueToPixel(cubicPath);
    }

    Utils.resetContext2DWithoutFont(c, this.mRenderPaint);
    c.beginPath();
    c.stroke(cubicPath);
    c.closePath();
    this.mRenderPaint.setDashPathEffect(null);
  }
}

数据线的绘制方法修改完了,之后就是修改y轴的绘制方法了。与数据线的绘制类似,定义一个自定义类MyAxisRender继承自YAxisRenderer类,然后修改绘制左y轴的方法,即renderAxisLine方法:

同样是创建了一个从底部到顶部的垂直渐变,颜色从绿色到红色变化,并将这个渐变应用到了Canvas的描边样式中。

完整代码如下:

import { EntryOhos, ILineDataSet, Style, Transformer, Utils, LineChartRenderer, XAxisRenderer,
  YAxisRenderer,
  AxisDependency} from '@ohos/mpchart';

export default class MyAxisRender extends YAxisRenderer{
  public renderAxisLine(c: CanvasRenderingContext2D, extraLength: number): void {

    if (!this.mYAxis || !this.mViewPortHandler || !this.mYAxis.isEnabled() || !this.mYAxis.isDrawAxisLineEnabled()) {
      return;
    }

    if (this.mAxisLinePaint) {
      this.mAxisLinePaint.setColor(this.mYAxis.getAxisLineColor());
      this.mAxisLinePaint.setStrokeWidth(this.mYAxis.getAxisLineWidth());

      Utils.resetContext2DWithoutFont(c, this.mAxisLinePaint);
      if (this.mYAxis.getAxisDependency() == AxisDependency.LEFT) {
        c.beginPath()
        let grad = c.createLinearGradient(0, this.mViewPortHandler.contentBottom() + extraLength, 0, this.mViewPortHandler.contentTop());
        grad.addColorStop(0.0, '#00ff00')
        grad.addColorStop(1.0, '#ff0000')
        c.strokeStyle = grad;
        c.moveTo(this.mViewPortHandler.contentLeft(), this.mViewPortHandler.contentTop());
        c.lineTo(this.mViewPortHandler.contentLeft(), this.mViewPortHandler.contentBottom() + extraLength)
        c.stroke();
        c.closePath();
      } else {
        c.beginPath()
        c.moveTo(this.mViewPortHandler.contentRight(), this.mViewPortHandler.contentTop());
        c.lineTo(this.mViewPortHandler.contentRight(), this.mViewPortHandler.contentBottom() + extraLength)
        c.stroke();
        c.closePath();
      }
    }
  }

}

 最后就是使用代码了,代码如下:

import {
  JArrayList,EntryOhos,ILineDataSet,LineData,LineChart,LineChartModel,
  Mode,
  LineDataSet,
  AxisDependency,
  XAxisPosition,
} from '@ohos/mpchart';
import MyAxisRender from './MyAxisRender';
import data from '@ohos.telephony.data';
import MyAxisLeftRender from './MyAxisRender';
import MyDataRender from './MyDataRender';

@Entry
@Component
struct Index {
  private model: LineChartModel = new LineChartModel();

  aboutToAppear() {
    // 创建一个 JArrayList 对象,用于存储 EntryOhos 类型的数据
    let values: JArrayList<EntryOhos> = new JArrayList<EntryOhos>();
    // 循环生成 1 到 20 的随机数据,并添加到 values 中
    for (let i = 1; i <= 20; i++) {
      values.add(new EntryOhos(i, Math.random() * 100));
    }
    // 创建 LineDataSet 对象,使用 values 数据,并设置数据集的名称为 'DataSet'
    let dataSet = new LineDataSet(values, 'DataSet');
    dataSet.setMode(Mode.CUBIC_BEZIER);
    dataSet.setDrawCircles(false);
    let dataSetList: JArrayList<ILineDataSet> = new JArrayList<ILineDataSet>();
    dataSetList.add(dataSet);
    // 创建 LineData 对象,使用 dataSetList数据,并将其传递给model
    let lineData: LineData = new LineData(dataSetList);
    this.model?.setData(lineData);
    this.model.getAxisLeft()?.setAxisLineWidth(2);
    this.model.getXAxis()?.setPosition(XAxisPosition.BOTTOM);
    this.model.getAxisRight()?.setEnabled(false);
    this.model.getDescription()?.setEnabled(false);
    this.model.setRenderer(new MyDataRender(this.model, this.model.getAnimator()!, this.model.getViewPortHandler()))
    this.model.setRendererLeftYAxis(new MyAxisLeftRender(this.model.getViewPortHandler(), this.model.getAxisLeft()!, this.model.getTransformer(AxisDependency.LEFT)!))

  }

  build() {
    Column() {
      LineChart({ model: this.model })
        .width('100%')
        .height('50%')
        .backgroundColor(Color.White)
    }
  }
}

其中主要修改的代码就是这里,设置了绘制y轴线的类和绘制数据的类为我们自定义的两个类:

this.model.setRenderer(new MyDataRender(this.model, this.model.getAnimator()!, this.model.getViewPortHandler()))
this.model.setRendererLeftYAxis(new MyAxisLeftRender(this.model.getViewPortHandler(), this.model.getAxisLeft()!, this.model.getTransformer(AxisDependency.LEFT)!))

好了,再看一遍绘制效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1853815.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】IP协议、以太网帧格式

目录 网络层IP协议协议头格式网段划分分类划分法特殊的 IP 地址IP 地址的数量限制私有 IP 地址和公有 IP 地址路由路由表生成算法 数据链路层以太网以太网帧格式认识 MAC 地址ARP协议ARP数据报格式 ARP 协议的工作流程ARP欺骗 DNShosts 文件域名的层级关系域名服务器分类域名解…

【计算机网络篇】数据链路层(12)交换机式以太网___以太网交换机

文章目录 &#x1f354;交换式以太网&#x1f6f8;以太网交换机 &#x1f354;交换式以太网 仅使用交换机&#xff08;不使用集线器&#xff09;的以太网就是交换式以太网 &#x1f6f8;以太网交换机 以太网交换机本质上就是一个多接口的网桥&#xff1a; 交换机的每个接口…

记MySQL事务+消息队列引起的问题

问题描述&#xff1a; 先说一下流程&#xff1a;后端保存前端提交的图表信息&#xff0c;然后发送异步消息到消息队列&#xff0c;由下游服务去处理图表信息。 部署项目到服务器&#xff0c;验证项目功能的时候&#xff0c;出现了以下错误&#xff1a;数据库存在数据。下游服…

计算机组成原理 —— 存储系统(DRAM和SRAM,ROM)

计算机组成原理 —— 存储系统&#xff08;DRAM和SRAM&#xff09; DRAM和SRAMDRAM的刷新DRAM地址复用ROM&#xff08;Read-Only Memory&#xff08;只读存储器&#xff09;&#xff09; 我们今天来看DRAM和SRAM&#xff1a; DRAM和SRAM DRAM&#xff08;动态随机存取存储器&…

构建未来应用的核心,云原生技术栈解析

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《未来已来&#xff1a;云原生之旅》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、云原生技术栈 1、容器和容器编排 1.1 Docker 1.2 Kubernete…

JaveEE进阶----Spring Web MVC入门

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、什么是 Spring Web MVC&#xff1f;&#xff1f;1.1MVC 定义1.2 什么是Spring MVC ?1.3过浏览器和用户程序交互 二、 RequestMapping 注解三、Postman 前言…

能理解你的意图的自动化采集工具——AI和爬虫相结合

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三连支…

计网:网络应用层【Email应用/SMTP协议】

Email应用与SMTP协议 Email应用层的构成 客户端服务器协议 用户代理 用于读写邮件消息&#xff1b;与服务器交互&#xff0c;收发邮件消息 常见的客户端&#xff1a;Outlook&#xff0c;Foxmail&#xff08;这两个是需要下载的客户端&#xff09;&#xff0c;Web客户端&…

浏览器插件利器-allWebPluginV2.0.0.14-bata版发布

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX插件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持谷歌、火狐等浏…

【数据建模】微分方程与动力系统

文章目录 微分方程与动力系统1. 微分方程的理论基础1.1 函数、导数与微分1.2 一阶线性微分方程的解1.3 二阶常系数线性微分方程的解 2. 使用python求解微分方程2.1 求解微分2.2 求解定积分2.2.1 quad函数求解2.2.2 梯型法则求解 3. 使用Scipy和Sympy解微分方程3.1 使用sympy求解…

4. DSL入门_01

1. 常见的DSL (1) 查询所有: 查询出所有数据&#xff0c;一般测试的时候使用&#xff0c;例如&#xff1a; match_all .但是受分页限制&#xff0c;一般返回10条数据 (2) 全文检索(full text)查询&#xff1a;利用分词器对用户输入内容分词&#xff0c;然后去倒排索引中匹配&a…

三个 insert 导致的死锁问题

锁种类 插入意向锁&#xff08;insert intention lock&#xff09;对已有数据行的修改与删除&#xff0c;必须加强互斥锁 X 锁&#xff0c;那对于数据的插入&#xff0c;是否还需要加这么强的锁&#xff0c;来实施互斥呢&#xff1f;插入意向锁&#xff0c;孕育而生。插入意向…

任务5.2 掌握DStream基础操作

实战&#xff1a;DStream基础操作 了解DStream编程模型&#xff1a;DStream是Spark Streaming中对实时数据流的抽象&#xff0c;可以看作一系列持续的RDD。DStream可以通过外部数据源获取或通过现有DStream的高级操作获得。 操作本质&#xff1a;DStream上的操作最终会转化为对…

OneNote for Windows 10 下载

OneNote for Windows 10 安装 1.在浏览器中输入地址&#xff1a;https://apps.microsoft.com/detail/9wzdncrfhvjl?hlzh-cn&glUS2OneNote for Windows 10 - 在 Windows 上免费下载并安装 |Microsoft StoreOneNote 是用于在设备上捕获和组织你的一切内容的数字笔记本。快速…

对日期的处理

对日期的处理 对编码进行统一&#xff0c;在脚本最开始&#xff1a; # -*- coding: utf-8 -*-这里涉及到两个操作&#xff0c;一个是将数据进行标准化&#xff0c;比如有些日期是2024/05/06这并不符合日期的标准格式&#xff0c;需要转换成这样的2024-05-06 def tran_std(st…

八爪鱼现金流-030,升级日志

八爪鱼现金流 八爪鱼 2024年4月4日09:27:02 v-0.0.1 资产包、负债包&#xff0c;功能优化 2024年4月15日09:27:26 v-0.0.2 增加公告模块 2024年4月18日12:14:32 v-0.0.3 市场查询优化。创建人脱敏处理。增加市场风云菜单。 2024年4月18日15:57:10 v-0.0.4 对于无截止日…

[MYSQL] 数据库基础

1.什么是数据库 从数据库的名字可以看出,它是用来操作(增删查改....)数据的,事实上也的确如此,通过数据库,我们可以更方便.更高效的来操作.管理数据 以文件形式存储数据的缺点 文件的安全问题文件不利于数据的查询和删除文件不利于存储海量数据操作文件并不方便 为了解决上述问…

【Vue-Vben-Admin】1、初次运行和介绍

【Vue-Vben-Admin】1、初次运行和介绍 Vben-Admin 初次运行和介绍 小小的介绍规定版本文件树安装依赖运行项目 小小的介绍 一款 Vue3 Typescript4 Vite2 后台管理项目&#xff0c;功能挺多的&#xff0c;还有组件库 规定版本 此个人文档规定版本为 2.8.0&#xff0c;可能版本…

消息队列MQ相关面试题

消息队列MQ相关面试题 1 RabbitMQ 1.1 你们项目中哪里用到了RabbitMQ ? 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ 我们项目中很多地方都使用了RabbitMQ , RabbitMQ 是我们项目中服务通信的主要方式之一 , 我们项目中服务通信主要有二种方式实现 : 通过Fei…

STM32 温湿度采集与OLED显示

目录 一、I2C总线通信协议 1、I2C介绍 2、软件I2C和硬件I2C &#xff08;1&#xff09;硬件I2C &#xff08;2&#xff09;软件I2C 差异 二、AHT20温湿度传感器 接口原理介绍 1. 温度测量原理 2. 湿度测量原理 实物引脚 传感器性能 电气特性 三、任务实现 具…