目录
Vue3 + hook + Apexcharts实现可视化图表渲染的封装 - 第一节 - 基础封装 (当前所在)
Vue3 + hook + Apexcharts实现可视化图表渲染的封装 - 第二节 - 图表的全屏功能(Vuex)
摘要
可视化是切图仔的必备技能,大部分需求我都是通过现有工具,不必自己实现。比如
Echarts
、D3
、Antv
等可视化工具,但是我们今天的主角都是这些,是一个叫Apexcharts
的可视化工具。Apexcharts
是js+svg
实现的(ps:Echarts
同时支持canvas
和svg
渲染图表)。
因为工作偶然接触到Apexcharts
,但是该工具的社区环境是真的差,如果遇到什么问题在网上基本上搜不到信息,只有官方文档。这里对开发使用过程的封装进行整理保存,后续针对其他的问题进行整理。
正文
安装
npm i -D apexcharts
npm i -D vue3-apexcharts
基础使用
基础使用具体请查看 vue3-apexcharts 文档,
<template>
<div>
<ApexChart
width="500"
type="bar"
:options="chartOptions"
:series="series"
/>
</div>
</template>
<script setup>
import ApexChart from 'vue3-apexcharts'
const chartOptions = {
chart: {
id: "vuechart-example",
},
xaxis: {
categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998],
},
},
const series = [
{
name: "series-1",
data: [30, 40, 35, 50, 49, 60, 70, 91],
},
],
</script>
在前端开发过程中图表的数据都是通过接口获取的,在数据更新后需要动态触发
Apexcharts
的更新,当我们在ApexChart
中绑定一个ref时:<ApexChart ref="chartRef" />
,可以通过chartRef
访问图表的DOM
元素,然后通过chartRef.updateOptions(最新的数据)
函数去触发图表的重新渲染。注意:必须在ApexChart
DOM元素更新之后才能获取到updateOptions
所以最好提前渲染一个数据为空的图表。如果无法获取,可以通过nextTieck()
去渲染。
图表渲染的 hook 封装
当我们通过
v-for
去渲染多个<Apexchart />
时,我们可以通过动态创建ref
正确绑定到对应的图表项中。下面直接放封装的hook
/*
** useRenderChart.js
** chartList 为传入的需要渲染的图表数组,具体格式见下面使用过程
*/
const useRenderChart = (chartList) => {
// 对scatter数据进行做截取,如果
const getScatterData = (data, intercept) => {
const obj = {}
Object.keys(data).forEach(key => {
obj[key] = data[key].slice(0, intercept)
})
return obj
}
// 触发图表的重新渲染,chartObj.updateOptions(data)返回的是一个对象包含新的series|options
const updateChart = (data, chartRef, chartObj) => {
chartRef.updateOptions(chartObj.updateOptions(data))
}
// 统一渲染函数,触发所有图表的重新更新
const renderCharts = (data) => {
chartList.forEach(item => {
const { key, refName, chartObj, intercept, type, interceptFn } = item
item.data = data[key]
let chartData = []
if (type === 'scatter' && !interceptFn) {
chartData = getScatterData(data[key], intercept)
} else if (intercept) {
chartData = interceptFn ? interceptFn(data[key], intercept) : data[key].slice(0, intercept)
} else {
chartData = data[key]
}
if (!chartData) return
if (Array.isArray(chartData)) {
chartData.length && updateChart(chartData, refName, chartObj)
} else {
updateChart(chartData, refName, chartObj)
}
})
}
// 动态设置图表的ref
const setRefMap = (el, index) => {
chartList[index].refName = el
}
return {
getScatterData,
renderCharts,
setRefMap,
}
}
export default useRenderChart
使用
<template>
<div v-for="(item, index) in chartList" :key="item.key" :id="item.key" class="chart-item">
<div class="title">{{ item.title }}</div>
<ApexChart
:ref="el => setRefMap(el, index)"
:type="item.type"
:options="item.chartObj.options"
:series="item.chartObj.series"
/>
</div>
</template>
<script setup>
import ApexChart from 'vue3-apexcharts'
import { BoxPlotChart, KMChart } from '@/utils/chartsInit'
import useRenderChart from '@/hook/useRenderChart'
/**
* 图表数据处理
* @param {Array} chartList 需要渲染的图表列表
* @param {Object} chartObj 图表对象 - 可做统一封装
* @param {String} key 图表key,用于渲染图表时找到对应的数据
* @param {String} title 图表标题
* @param {String} type 图表类型
* @param {Number} intercept 图表数据截取长度
* @param {Function} interceptFn 自定义图表数据截取函数
*/
const chartList = reactive([
{
key: 'coef_data',
title: 'Coefficient',
type: 'boxPlot',
chartObj: BoxPlotChart,
},
{
key: 'train',
title: 'KM plot (train)',
type: 'line',
chartObj: KMChart,
intercept: 1000,
interceptFn: (data, intercept) => {
return data.map(item => {
const obj = {}
obj.prob = item.prob?.slice(0, intercept)
obj.time = item.time?.slice(0, intercept)
return obj
})
},
},
])
const { renderCharts, setRefMap } = useRenderChart(chartList)
// 请求数据后触发图表重新渲染
async function handleGetData() {
// ...
// data数据一个对象{},包含chartList的每个key,对应值为图表的新数据
renderCharts(data)
}
chartsInit.js
的图表封装,以上面的BoxPlotChart
为例子
export const BoxPlotChart = {
series: [],
options: {
xaxis: {
type: 'category',
},
// ...
},
updateOptions: data => {
return {
series: [],
xaxis: {},
// ...等等options的配置
}
},
}
总结
对于上面的统一封装的
useRenderChart
这里去掉了全屏查看图表功能
的,因为涉及到其他一部分逻辑,以及对上面所有代码的更改。将在下一篇文章补充实现。