系列文章目录
HarmonyOS Next 系列之省市区弹窗选择器实现(一)
HarmonyOS Next 系列之验证码输入组件实现(二)
HarmonyOS Next 系列之底部标签栏TabBar实现(三)
HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)
HarmonyOS Next 系列之从手机选择图片或拍照上传功能实现(五)
HarmonyOS Next 系列之可移动悬浮按钮实现(六)
HarmonyOS Next 系列之沉浸式状态实现的多种方式(七)
HarmonyOS Next系列之Echarts图表组件(折线图、柱状图、饼图等)实现(八)
文章目录
前言
HarmonyOS Next(基于API11)实现Echarts图表组件(折线图、柱状图、饼图等)。
Echarts作为web端最流行开源的图表库,有着
多种图表类型,丰富的配置、强大的交互功能、可扩展性强等优点,如果能在鸿蒙上使用对于熟悉web开发同学将无缝衔接,大大减少开发和学习成本。
一、实现原理分析
echarts作为JavaScript实现的开源可视化库只能通过网页形式渲染,所以可以 通过web组件内嵌本地网页形式混合开发。web和应用交互则通过WebviewController.runJavaScript(code)形式往网页注入执行代码。
二、代码实现
1.项目内新建本地html文件引入echarts.min.js
resources/rawfile目录下新建echarts.html和添加echarts.min.js
echarts.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport"/> <script src="./echarts.min.js"></script> <style> *{ padding:0; margin:0 } body,html{ height:100%; width:100% } #container{ height:100%; width:100% } </style> </head> <body> <div id="container"></div> </body> <script> const container = document.getElementById('container') let myChart = echarts.init(container); function setOption(option){ myChart?.clear(); myChart?.setOption(option) } </script> </html>
echarts.min.js:
可以从官网在线定制下载echart.js定制
说明:此步骤新建了一个html文件引入了echarts.min.js并初始化echarts,定义了一个
setOption方法,后续通过调用该方法传入配置就能渲染出图表
2.Echart.ets组件封装
封装一个全局通用的echart组件,目录结构如上图所示,其中Echarts.ets为组件文件,ViewModel.ets为图表相关配置数据类型定义
Echarts.ets
import webview from '@ohos.web.webview' @Component export default struct Echarts { //控制器 controller: webview.WebviewController = new webview.WebviewController(); //组件宽 @Prop eWidth: string | number = '100%' //组件高,单位vp @Prop eHeight: string | number = 300 //渲染完成回调 renderCallBack: (e: Echarts) => void = () => { } //更新或渲染组件 render(option: Record<string, ESObject> | string) { this.controller.runJavaScript(`setOption(${typeof option === 'string' ? option : JSON.stringify(option)})`) } build() { Column() { Web({ src: $rawfile('echarts.html'), controller: this.controller }) .width('100%') .height('100%') .onPageEnd(e => { this.renderCallBack && this.renderCallBack(this) }) } .width(this.eWidth) .height(this.eHeight) } }
说明:
组件定义了长宽属性,默认宽度100%,高度300vp,
renderCallBack回调函数作用把整个echarts组件实例暴露给引用页面。
render方法重新加载渲染组件,入参为图表配置,入参既可以是对象也可以是字符串,无论何种类型最终都将转换为字符串注入web执行。
在引用页面通过renderCallBack暴露出去的实例调用render方法即可自由刷新图表数据
ViewModel.ets:
/** * echart配置数据类型定义 */ export interface EChartsOption { grid?: EchartGrid, title?: EChartsTitle; tooltip?: EChartsTooltip; legend?: EChartsLegend; xAxis: EChartsXAxis; yAxis: EChartsYAxis; series: EChartsSeries[]; } export interface EchartGrid { top?: number|string bottom?: number|string left?: number|string right?: number|string } export interface EChartsTitle { text?: string; } export interface EChartsTooltip {} export interface EChartsLegend { show?:boolean data?: string[]; } export interface EChartsXAxis { type?: string; data: string[]; axisLine?: XAxisAxisLine; axisTick?: XAxisAxisTick boundaryGap?: boolean | Array<string> axisLabel?: AxisLabel } export interface XAxisAxisLine { show?: boolean } export interface XAxisAxisTick { show?: boolean } export interface EChartsYAxis { type?: string splitLine?: YAxisSplitLine splitNumber?: number min?: number max?: number scale?: boolean interval?: number } export interface AxisLabel { show?: boolean formatter?: string | ((value: number | string, index: number) => string) } export interface YAxisSplitLine { lineStyle?: AxisSplitLineLineStyle } export interface AxisSplitLineLineStyle { type?: string } export interface EChartsSeries { name?: string; type?: string; data: number[]; }
ViewModel.ets里面定义了一些常见的图表配置数据结构(类型),方便在引入页中引入使用,可以按需继添加扩展
3.页面使用
Index.ets
import Echarts from '../components/Echarts/Echarts' import { EChartsOption } from '../components/Echarts/ViewModel' @Entry @Component struct Index { //图表实例 myEchart: Echarts | null = null /* * 图表配置 */ option: EChartsOption = { //标题 title: { text: '基础柱状图' }, //图例 legend: { data: ['访问量'] }, //x轴配置 xAxis: { type: 'category', data: [] }, //y轴配置 yAxis: { type: 'value' }, //数据配置 series: [ { data: [], type: 'bar',//柱状图 name: '访问量' } ] }; aboutToAppear(): void { this.getData() } //接口请求获取数据 getData() { //模拟接口请求 setTimeout(() => { //设置x轴数据 this.option.xAxis.data = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] //设置y轴数据 this.option.series[0].data = [120, 200, 150, 80, 70, 110, 130] //调用render重新渲染 this.myEchart?.render(this.option) }, 2000) } // 组件实例 chart?: Echarts; build() { Column() { Echarts({ eHeight: 300, //回调 renderCallBack: (e: Echarts) => { this.myEchart = e //初次渲染组件,接口获取数据是异步此时this.option可能还没有新数据 this.myEchart.render(this.option) } }) } .width('100%') .height('100%') } }
运行效果:
三、配置项含函数需特殊处理
对于配置项包含函数的情况,例如坐标轴标签设置AxisLabel.formatter字段可以是个函数类型,此时就需要特殊处理。这是因为当我们传递option为对象给render函数时候
this.controller.runJavaScript(`setOption(${typeof option === 'string' ? option : JSON.stringify(option)})`)
此时调用JSON.stringify把对象转换为字符串,而JSON.stringify入参内函数会直接被去掉。
因此我们需要自己把option处理成字符串传入即可
代码示例:
给折线图y轴刻度值加个万字
Index.ets
import Echarts from '../components/Echarts/Echarts' @Entry @Component struct Index { //图表配置 @State option: string = `` //x轴数据 @State xAxisData: string[] = []; //y轴数据 @State seriesData: number[] = []; myEchart: Echarts | null = null; aboutToAppear(): void { this.getData() } /* * 设置配置并重新渲染 */ setOption() { this.option = `{ title: { text: '基础柱折线图' }, grid:{ left:"15%" }, legend:{ data: ['访问量'] }, xAxis: { type: 'category', data: ${JSON.stringify(this.xAxisData)} }, yAxis: { axisLabel: { show: true, formatter:(value, index)=> { return value + '万'; } } }, series: [ { data: ${JSON.stringify(this.seriesData)}, type: 'line', name:'访问量' } ] }` this.myEchart?.render(this.option) } //接口请求获取数据 getData() { //模拟接口请求 setTimeout(() => { this.xAxisData = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] this.seriesData = [120, 200, 150, 80, 70, 110, 130] this.setOption() }, 2000) } // 组件实例 chart?: Echarts; build() { Column() { Echarts({ eHeight: 300, renderCallBack: (e: Echarts) => { this.myEchart = e this.setOption() } }) } .width('100%') .height('100%') } }
运行效果:
总结
当然作为混合开发产物性能自然比上不上原生,但是echarts有着跨平台兼容性好等优点,对付数据量不大、交互要求不高的场景绰绰有余。目前处于起步发展阶段的鸿蒙来说还没有一款稳定成熟、配置丰富能满足各种ui设计要求的官方或三方组件库。所以只要你的开发场景对性能要求不高,web形式的echart也是个不错的选择,尤其能实现各种定制化ui。