UCharts for Harmony - 鸿蒙可视化图表库

81 阅读7分钟

UCharts

Github · 三方库中心仓


前言

在移动应用开发中,数据可视化是提升用户体验的重要手段。随着 HarmonyOS 生态的快速发展,开发者们迫切需要一款功能强大、易于使用的图表库。今天为大家介绍一款优秀的开源第三方组件 —— UCharts,它为 HarmonyOS 开发者提供了丰富的图表解决方案。

介绍

UCharts 是一个基于 TypeScript 实现的类型丰富、高性能、模块化、可扩展、支持主题定制的跨平台图表库。底层渲染逻辑全部采用 TypeScript 实现,上层通过适配层(adapters)适配到不同平台,包括鸿蒙(HarmonyOS)、微信小程序、uniapp 等,真正实现"一套核心,多端复用"。
现已适配 HarmonyOS 平台。支持多种常用图表类型,满足鸿蒙应用的数据可视化需求。目前代码已经全部开源到Github三方库中心仓,欢迎各位下载使用并提出宝贵意见!

背景

UCharts的前身uCharts是一款使用JavaScript基于canvas API开发的多平台前端应用图表库,已是2020年开发的版本。旧版本存在的一些架构问题和多端适配耦合问题,面对现如今高速发展的各种前端技术和平台,已不在具有技术优势。
UCharts重新设计了底层架构,模块化开发,核心层只实现与平台无关的渲染逻辑,独立的事件系统和动画系统。上层设计通过适配层适配到不同平台,解耦各个平台的交互逻辑。
用户只需要简单的配置,即可实现各种精美图表的绘制展示,满足各类应用的数据可视化需求。

特性

  • 🧩 模块化设计:底层渲染与平台适配解耦,易于扩展和维护
  • 🛡️ TypeScript 全面支持:类型安全,开发体验优秀
  • 🎨 丰富图表类型:柱状图、条状图、折线图、区域图、山峰图等20种不同类型图表
  • ⚡ 高性能渲染:底层优化,独立的动画和事件系统
  • 🔌 易于扩展:支持自定义图表类型和平台适配
  • 🍭 自定义样式:支持主题定制

体验预览

升级到 HarmonyOS NEXT 的手机, 可在应用商店搜索 bestibest 关键字, 下载第一个名为 IBest-UI 的应用, 即可体验。

图表示例

以下为部分图表类型的鸿蒙平台实际渲染效果:

样例解析

图表绘制

如果想要绘制图表,只需要简单几个步骤即可实现:

 1. 页面中添加图表组件,并为组件初始化控制器。

 2. 设置图表配置,通过组件控制器更新图表

import { ChartOptions, UCharts, UChartsController } from '@ibestservices/ucharts'
@Entry
@Component
struct Index {
	private chart: UChartsController = new UChartsController();
	private opts: Partial<ChartOptions> = {
		type: "column",
    		categories: ["2018","2019","2020","2021","2022","2023"],
    		series: [
			{ name: "目标值", data: [35,36,31,33,13,34]},
			{ name: "完成量", data: [18,27,21,24,6,28]} 
		],
    		xAxis: { disableGrid: true },
    		yAxis: { data: [{min: 0}] }
	}
	
	build() {
    		Column(){
      			UCharts({ controller: this.chart, onReady: () => {
          			this.chart.updateData(this.opts)
      			}})
      			/*
       			* 或者初始化时传入默认配置
       			* @State chart: UChartsController = new UChartsController(this.opts);
       			* UCharts({ controller: this.chart })
       			* */
    		}	
  	}
}
扩展属性

每一种图表类型都有其对应的扩展属性配置,除了上面例子中的基础图表绘制。还可通过设置扩展属性来绘制各种丰富的图表样式。其中柱状图扩展属性配置项如下:

/**
 * 柱状图扩展配置
 */
export interface ColumnExtra {
  type: 'group'|'stack'|'meter'      //柱状图类型,可选值:'group'分组柱状图,'stack'堆叠柱状图,'meter'温度计式图,默认group
  width: number                      //柱状图每个柱子的图形宽度
  seriesGap: number                  //多series每个柱子之间的间距
  categoryGap: number                //每个category点位(X轴点)柱子组之间的间距
  barBorderCircle: boolean           //启用分组柱状图半圆边框,默认false
  barBorderRadius: [number,number,number,number]   //自定义4个圆角半径[左上,右上,右下,左下]
  linearType: 'none'|'opacity'|'custom'            //渐变类型,可选值:"none"关闭渐变,"opacity"透明渐变,"custom"自定义颜色,默认none
  linearOpacity: number              //透明渐变的透明度(值范围0到1,值越小越透明),默认1
  customColor: string[]              //自定义渐变颜色,数组类型对应series的数组长度以匹配不同series颜色的不同配色方案,例如["#FA7D8D", "#EB88E2"]
  colorStop: number                  //渐变色的显示比例(值范围0到1,值越大自定义颜色程度越高)
  meterBorder: number                //温度计式图表的边框宽度,默认1
  meterFillColor: string             //温度计式图表的空余填充颜色,默认#FFFFFF
  activeBgColor: string              //当前点击柱状图的背景颜色,默认#000000
  activeBgOpacity: number            //当前点击柱状图的背景颜色透明度,默认0.08
  labelPosition: 'outside'|'insideTop'|'center'|'bottom'  //数据标签位置,有效值为"outside"外部,"insideTop"内顶部,"center"内中间,"bottom"内底部,默认outside
}

目录结构

├── core/                # 图表核心能力(平台无关)
│   ├── types/           # 类型定义
│   ├── utils/           # 工具函数
│   ├── chart/           # 各类图表渲染器
│   ├── event/           # 事件系统
│   ├── animation/       # 动画系统
│   └── factory.ts       # 图表工厂
├── adapters/            # 平台适配层
│   ├── harmony/         # 鸿蒙适配
│   ├── h5/              # 原生H5适配
│   ├── wechat/          # 微信小程序适配
│   └── uniapp/          # uniapp适配
├── interface/           # 对外统一接口
│   ├── CanvasContext.ts   # 跨平台统一 canvas context 接口定义
├── examples/            # 示例代码
├── docs/                # 文档
└── README.md

跨平台适配层说明

 - 每个平台在 adapters/ 下有独立目录,负责将平台 API 适配为统一的底层渲染接口。

 - 适配层需实现统一的适配接口,并暴露标准的 context、事件等能力。

 - 新增平台时,仅需在 adapters/ 下新增目录并实现适配接口,无需修改 core 层代码。

 - CanvasContext 统一接口:

     - ChartOptions.context 字段要求传入的 canvas context 必须兼容 interface/CanvasContext 类型。

     - 各平台适配层需将平台原生 canvas context 封装/适配为该接口,保证 core 层渲染逻辑的统一调用。

     - 具体接口定义见 interface/CanvasContext.ts,如需适配新平台,请实现该接口。

鸿蒙平台的适配实现

适配 CanvasContext 类型

使用代理模式,实现CanvasContext适配器,拦截 HarmonyOS 平台不支持的属性,进行兼容处理。

// 定义CanvasRenderingContext2D的CanvasContext适配器
class CanvasContextProxy implements ProxyHandler<CanvasRenderingContext2D>, ProxyHandler<CanvasContext> {
  // 拦截属性设置操作
  set(target: CanvasRenderingContext2D|CanvasContext, propertyKey: string, value: string, receiver: AnyType): AnyType {
    // 确保只设置存在的可写属性
    if (typeof propertyKey === 'string') {
      try {
        (target as AnyType)[propertyKey] = value;
        return true;
      } catch (e) {
        console.warn(`无法设置属性 ${String(propertyKey)}`, e);
      }
    }
    return false;
  }

  get(target: CanvasRenderingContext2D|CanvasContext, propertyKey: string, receiver: AnyType): AnyType {
    if (propertyKey === "createCircularGradient") {
      return undefined
    }

    const value: AnyType = (target as AnyType)[propertyKey];
    return typeof value === 'function' ? value.bind(target) : value;
  }
}

使用CanvasContextProxy适配CanvasRenderingContext

const handler = new CanvasContextProxy();
const context = new CanvasRenderingContext2D(new RenderingContextSettings(true));
const canvasContext = new Proxy(context, handler) as CanvasContext;
适配点击和触屏交互

通过将UCharts组件的点击事件和触摸事件坐标点适配到对应核心层接口

onMouseEvent(event: MouseEvent) {
    const p: Point = {
      x: event.x,
      y: event.y
    }
    if(event.action == MouseAction.Press) {
      this.chartRenderer?.scrollStart(p)
      this.chartRenderer?.touchLegend(p)
      this.chartRenderer?.showToolTip(p)
    } else if(event.action == MouseAction.Move) {
      this.chartRenderer?.scroll(p)
      if(!this.opts?.enableScroll) this.chartRenderer?.showToolTip(p)
    } else if(event.action == MouseAction.Release) {
      this.chartRenderer?.scrollEnd()
    }
  }

  onTouchEvent(event: TouchEvent) {
    const touch = event.touches[0]
    const p: Point = {
      x: touch.x,
      y: touch.y
    }
    if(event.type == TouchType.Down) {
      this.chartRenderer?.scrollStart(p)
      this.chartRenderer?.touchLegend(p)
      this.chartRenderer?.showToolTip(p)
    } else if(event.type == TouchType.Move) {
      this.chartRenderer?.scroll(p)
      if(event.touches.length > 1) {
        this.chartRenderer?.doubleZoom([
          {x: event.touches[0].x, y: event.touches[0].y},
          {x: event.touches[1].x, y: event.touches[1].y}
        ])
      }
      if(!this.opts?.enableScroll) this.chartRenderer?.showToolTip(p)
    } else if(event.type == TouchType.Up) {
      this.chartRenderer?.scrollEnd()
    }
  }

最终实现 HarmonyOS 平台的适配。

未来发展

随着 HarmonyOS 生态的不断完善,UCharts 也在持续演进:

更多图表类型:不断增加新的图表类型支持

性能优化:持续优化渲染性能和内存使用

交互增强:提供更丰富的交互功能

主题系统:完善的主题定制系统

对于正在开发 HarmonyOS 应用的开发者来说,UCharts 无疑是一个值得尝试的优秀第三方库。它不仅能够帮助您快速实现数据可视化功能,还能为您的应用带来更好的用户体验。

致谢

感谢所有开源贡献者和用户的支持!希望这篇文章能够帮助您更好地了解和使用 UCharts,在 HarmonyOS 开发中创造出更加精彩的数据可视化应用!