对echart组件进行一个封装 个人笔记
图的id唯一性
主要是为了多个echart时有唯一标识符
通过uuid加表前缀的形式处理
id: 'chart_' + uuidv4()
图表的响应式布局
思路如下
使用 myChart.getDom() 方法以及与 ElementResizeDetector 结合的 resize 方法的原因在于实现图表的响应式布局和确保图表正确显示。
示例代码
// 假设 'myChartContainer' 是一个包含在 HTML 中的元素的 ID
const myChart = echarts.init(document.getElementById('myChartContainer'));
// 获取 ECharts 图表的 DOM 元素
const chartDom = myChart.getDom();
// 打印 DOM 元素的宽度和高度
console.log(chartDom.offsetWidth, chartDom.offsetHeight);
// 使用 ElementResizeDetector 监听 DOM 元素的尺寸变化
const ERD = new ElementResizeDetector();
ERD.listenTo(chartDom, () => {
myChart.resize(); // 调整图表以适应 DOM 元素尺寸的变化
});
在上面的代码中,我们首先通过 echarts.init() 创建了一个 ECharts 实例,并将其渲染到一个具有特定 ID 的 DOM 容器中。然后,我们使用 myChart.getDom() 获取了这个容器的引用,并使用 ElementResizeDetector 库来监听其尺寸变化,当尺寸变化时,我们调用 myChart.resize() 来更新图表的大小。
作用如下
- 响应式图表:在不同的屏幕尺寸或分辨率下,容器的尺寸可能会发生变化。通过监听容器尺寸的变化并调整图表大小,可以确保图表始终适应其容器,无论容器的当前尺寸如何。
- 保持图表比例:在容器尺寸变化后,如果图表大小没有相应调整,可能会导致图表内容变形或显示不完整。使用
resize方法可以保持图表的原始设计比例。 - 提升用户体验:对于用户来说,无论在什么设备或屏幕尺寸下浏览,都能够获得一致的、视觉上令人愉悦的图表展示,这是非常重要的。
- 动态内容更新:在某些应用场景中,图表的数据或配置项可能会动态变化。即使在这种情况下,也需要图表的大小能够正确适应容器的变化。
- 避免硬编码尺寸:硬编码图表的宽度和高度通常不是一个好的实践,因为它降低了布局的灵活性。使用
resize方法可以避免这个问题,让图表尺寸更加灵活。 - 跨浏览器兼容性:不同的浏览器可能对 CSS 和 DOM 的解析存在差异。通过编程方式监听和调整图表大小,可以减少这些差异带来的影响。
- 自动化处理:手动调整图表大小可能既耗时又容易出错。自动化这个过程可以减少人为错误,并提高开发效率。
多个折线图(series)
用于根据传入的数据和条件设置图表的系列(series)
条件判断:if (this.multiple)
这个判断用来决定是否需要创建多系列(multiple series)图表。当 multiple 属性为 true 时,意味着图表需要展示多个系列的数据。将多个series设置到一个option上
这时候可以用数组套上数组即可
if (this.multiple) {
const series = this.data.map(item => {
return {
data: item.value,
type: 'line',
name: item.name,
label: {
show: true
}
}
})
lineOption.series = series
lineOption.xAxis.data = this.xAxis
lineOption.legend = {
bottom: 0,
show: true,
itemWidth: 12,
itemHeight: 12,
icon: 'rect',
data: this.data.map(item => item.name)
}
lineOption.grid.bottom = 32
}
this.data.map(item => {...}): 这里使用map函数遍历data数组。data数组是传入组件的属性之一,预期包含多个数据点。item.value: 假设每个item对象都有一个value数组,该数组包含该数据点的数值。type: 'line': 设置系列类型为折线图。name: item.name: 设置系列的名称,item.name是数据点的名称或标识。label: { show: true }: 设置显示数据标签,这里label配置确保每个数据点旁边的数值标签是可见的。
element-resize-detector 说明
element-resize-detector 是一个 JavaScript 库,用于检测 DOM 元素尺寸的变化。它特别有用于以下场景:
- 响应式布局:在响应式设计中,当元素尺寸发生变化时,你可能需要执行一些操作,比如重新渲染图表、调整内容布局等。
- 动态内容调整:对于动态改变尺寸的内容,例如图片上传预览、编辑器等,
element-resize-detector可以帮助你检测到这些变化,并进行相应的调整。 - 性能优化:对于一些性能敏感的操作,比如重新计算图表数据,你可能不希望在每次窗口尺寸变化时都执行。
element-resize-detector允许你只对目标元素的尺寸变化做出响应。 - 兼容性:它提供了跨浏览器的解决方案,可以确保在不同的浏览器和设备上都能正常工作。
element-resize-detector 的工作原理是使用一种机制(通常是通过在元素上设置一个可观察的属性,然后监听这个属性的变化)来检测元素尺寸的变化。当元素的尺寸发生变化时,它会触发一个回调函数,你的应用程序可以在该回调函数中执行相应的逻辑。
使用 element-resize-detector 时,你通常会这样做:
- 创建一个
ElementResizeDetector实例。 - 使用该实例的
listenTo方法来指定需要监听的元素。 - 为
listenTo方法提供一个回调函数,该函数将在元素尺寸变化时被调用。
例如:
import ElementResizeDetector from 'element-resize-detector';
// 创建一个 ERD 实例
const erd = new ElementResizeDetector();
// 获取要监听的 DOM 元素
const myElement = document.getElementById('myElement');
// 开始监听元素的尺寸变化
erd.listenTo(myElement, () => {
// 当元素尺寸变化时,这个回调函数将被调用
console.log('Element size has changed!');
// 在这里执行你需要的操作,比如调整图表大小等
});
代码
需要安装依赖
npm install element-resize-detector
npm install echarts
npm install uuid
npm install sass
参数说明
props 传递参数说明
title (String): 用于设置图表标题的字符串,默认为空字符串。
data (Array): 一个数组,包含图表需要展示的数据对象,默认为空数组。数据对象应包含 name 和 value 字段,这些字段将用于图表的 X 轴和 Y 轴数据。
value (String): 指定数据对象中用于 Y 轴值的字段名,默认为 'value'。
name (String): 指定数据对象中用于 X 轴标签的字段名,默认为 'name'。
yAxisName (String): 设置 Y 轴名称的字符串,默认为空字符串。
xAxis (Array): 用于自定义 X 轴分类的数组,默认为空数组。
grid (Object): 设置图表栅格布局的对象,包含 top, left, right, bottom 和 containLabel 属性,具有默认值。
multiple (Boolean): 一个布尔值,指示图表是否应以多系列形式展示,默认为 false。
minInterval (Boolean): 一个布尔值,指示是否设置 Y 轴的最小间隔为 1,默认为 true。
组件代码如下
<template>
<div class="bar-chart ">
<div class="chart">
<div :id="id" style="width: 100%; height: 100%;" v-if="data.length"></div>
<div v-else>正在加载中</div>
</div>
<div class="title">
<h4>{{title}}</h4>
<slot name="headerRight"></slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script>
import ElementResizeDetector from 'element-resize-detector'
import * as echarts from 'echarts';
import { v4 as uuidv4 } from 'uuid'; // 导入uuid库的v4函数
export default {
name: 'bar-echarts',
props: {
title: {
type: String,
default: ''
},
data: {
type: Array,
default: () => []
},
value: {
type: String,
default: 'value'
},
name: {
type: String,
default: 'name'
},
yAxisName: {
type: String,
default: ''
},
xAxis: {
type: Array,
default: () => []
},
grid: {
type: Object,
default: () => {
return {
top: 24,
left: 24,
right: 24,
bottom: 0,
containLabel: true
}
}
},
multiple: {
type: Boolean,
default: false
},
minInterval: {
type: Boolean,
default: true
}
},
data() {
return {
id: 'chart_' + uuidv4()
}
},
watch: {
data: 'render'
},
mounted() {
this.render()
},
methods: {
render() {
if (!this.data.length) return
const colorList = ['#2358a3', '#60C976', '#F5D162', '#FF9D4D', '#EC7471', '#A9D372', '#69C9AB', '#7678E6']
this.$nextTick(() => {
if (document.getElementById(this.id)) {
echarts.dispose(document.getElementById(this.id))
}
const myChart = echarts.init(document.getElementById(this.id))
const barOption = {}
// if (this.minInterval) {
// barOption.yAxis[0].minInterval = 1
// }
myChart.setOption(barOption)
myChart.resize()
const ERD = new ElementResizeDetector()
ERD.listenTo(myChart.getDom(), () => {
myChart.resize()
})
})
}
}
}
</script>
<style lang="scss" scoped>
.bar-chart {
margin-bottom: 16px;
border: none;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
&:hover {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
cursor: unset;
}
.title {
margin-top: 20px;
margin-left: 5px;
display: flex;
justify-content: center;
}
.chart {
margin-top: 16px;
height: 300px;
text-align: left;
}
.exception-wrap {
display: flex;
flex-wrap: wrap;
}
.exception-wrap .exception-wrap-item {
border: 1px solid #DCDEE5;
margin: 10px;
height: 420px;
padding-top: 22px;
}
.exception-wrap-item.exception-part {
height: 260px;
padding-top: 48px;
flex: 1;
}
.exception-wrap-item.exception-gray {
background-color: #F5F6FA;
}
.exception-wrap .exception-wrap-item .text-wrap {
display: flex;
align-items: center;
justify-content: center;
color: #3A84FF;
font-size: 14px;
margin-top: 12px;
}
.exception-wrap .exception-wrap-item .text-wrap.text-part {
font-size: 12px;
margin-top: 10px;
}
.exception-wrap .exception-wrap-item .text-subtitle {
color: #979BA5;
font-size: 14px;
text-align: center;
margin-top: 14px;
}
.text-wrap .text-btn {
margin: 0 10px;
}
.text-wrap .text-btn:hover {
cursor: pointer;
}
}
</style>
option代码放进去即可
{
color: colorList,
grid: {
top: this.yAxisName ? 40 : 24,
left: 20,
right: 20,
bottom: 0,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'line'
},
extraCssText: 'box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.1);border-radius: 2px'
},
xAxis: {
type: 'category',
data: this.data.map(item => item[this.name]),
axisLabel: {
color: '#979BA5'
},
axisLine: {
lineStyle: {
color: '#DCDEE5'
}
}
},
yAxis: {
name: this.yAxisName,
nameTextStyle: {
color: '#979BA5',
fontSize: 12,
padding: 10
},
type: 'value',
axisLabel: {
color: '#979BA5'
},
axisLine: {
lineStyle: {
color: '#DCDEE5'
}
},
minInterval: 1
},
series: [{
data: this.data.map(item => item[this.value]),
type: 'line',
name: '数量',
label: {
show: true
}
}]
}
if (this.multiple) {
const series = this.data.map(item => {
return {
data: item.value,
type: 'line',
name: item.name,
label: {
show: true
}
}
})
barOption.series = series
barOption.xAxis.data = this.xAxis
barOption.legend = {
bottom: 0,
show: true,
itemWidth: 12,
itemHeight: 12,
icon: 'rect',
data: this.data.map(item => item.name)
}
barOption.grid.bottom = 32
}
一个测试组件的代码 (哈哈,用vue3写的测试用例)
<template>
<div class="top-list">
<div class="echart-list">
<div class="echart-item" v-for="(item, i) in chartDataList" :key="i">
<bar-echarts style="height: 100%;" :title="item.chartTitle" :data="item.chartData" :x-axis="xAxis" :multiple="multiple"/>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref,onMounted } from 'vue';
import BarEcharts from './barEcharts.vue';
const multiple = ref(false)
const chartDataList = ref([
{
chartTitle: '测试1',
chartData: [{ name: '类别A', value: 1200 },{ name: '类别B', value: 1100 }],
},
{
chartTitle: '测试2',
chartData: [{ name: '类别B', value: 2200 },{ name: '类别C', value: 1200 }],
},
// ...其他数据
])
const xAxis = ['类别A', '类别B', '类别C', '类别D']
const updated =async ()=>{
// 使用setTimeout模拟异步操作,如API请求
await setTimeout(() => {
multiple.value = true
// 这里更新chartDataList的数据
chartDataList.value = [
// 假设这是更新后的数据
{ chartTitle: '更新后-测试1', chartData: [{ name: '类别A', value: [100,200] },{name: '类别B', value: [200,110]} ] },
{ chartTitle: '更新后-测试2', chartData: [{ name: '类别A', value: [100,200] },{name: '类别B', value: [200,110]} ]},
// ...其他更新后的数据
];
console.log(chartDataList.value)
}, 3000); // 3秒后更新数据
}
// 使用onMounted钩子在组件挂载后执行异步操作
onMounted(()=>{
updated()
})
</script>
<style lang="scss" scope>
.top-list{
width: 100%;
.echart-list{
width: 100%;
display: flex;
justify-content: space-between;
.echart-item{
width: 23%;
height: 80%;
}
}
}
</style>