vue使用echarts实现柱状图动态排序效果
作者:叶落风尘
echarts在前端开发中实属必不可缺的大数据可视化工具,这篇文章主要为大家详细介绍了vue如何使用echarts实现柱状图动态排序效果,感兴趣的可以了解下
前言
echarts在前端开发中实属必不可缺的大数据可视化工具,在前段时间搬砖时遇到这样一个需求,需要实现一个动态排序的柱状图,用来展示不同部门在不同月份的绩效收益情况,能够直观地看到每个月各个部门的排名变化情况。并能够随时暂停,手动切换来展示某个月的具体情况。经过一下午一顿敲击键盘后,完美完成本次任务,总结记录一下,以后就可以愉快地摸鱼了。完整代码看文末,先上图看下实现效果:
安装使用
1、demo基于vue3框架,首先安装echarts。
yarn add echarts //我使用的版本:"echarts": "^5.2.2"
2、全局注册echarts,在app.vue 中引入echarts,使用provide,使所有的子组件都可以直接使用echarts
//app.vue import * as echarts from 'echarts'; provide('$echarts', echarts);
demo实现
生成一组测试数据
首先随机生成一组测试数据,包含1-10月的数据count,平均数avg和y轴的label。timeLineList用于用于播放进度条展示。
const generateRandomData = () => { const data = []; for (let i = 0; i < 100; i++) { let month = '2023-'; month = month + (Math.floor(i / 10) + 1); const count = Math.floor(Math.random() * 300) + 100; // 生成100到300的随机整数 const name = 'count' + i % 10; const avg = Math.floor(Math.random() * 300) + 100; data.push({ month, count, name, avg }); //生成月度集合,用于播放条展示 if(!timeLineList.value.includes(month)){ timeLineList.value.push(month) } } return data; }
编写模板部分
包含播放和暂停按钮、播放条和echart部分。
<template> <div class="chart-dialog-content"> <div class="opt-bar"> <button size="mini" v-if="playChart == false" @click="(playChart = true), initChart(0)"><i class="el-icon-video-play"></i>轮播</button> <button size="mini" v-if="playChart == true" @click="(playChart = false), initChart(activeIndex)"><i class="el-icon-video-pause"></i>暂停</button> </div> <section class="bottom"> <div class="time-line"> <div class="line-item" @click="changeChart(index)" v-for="(i, index) in timeLineList" :key="i" :class="{ active: index <= activeIndex, hover: index == activeIndex }"> <div class="line"></div> <div class="point"> <div class="text">{{ i }}</div> <div class="icon el-icon-caret-top" v-show="index == activeIndex"></div> </div> </div> </div> <div class="chart-content" id="chartContent"></div> </section> </div> </template>
echarts配置
echarts几个比要重要配置实现:
1、配置x轴的最大最小值,为了让变化看上去更明显,配置min值不从0开始。
xAxis: { max: 'dataMax', min: function (value: any) { return Math.round(value.min) - 10; }, axisLabel: { formatter: function (n: any) { return n; } } },
2、配置dataSet,从数据集合中取出某个月的数据
dataset: { source: data.filter(function (item) { return item[0] === startMonth; }) },
detaset需要的数据样例:
[ [ "2023-2", 322, "count0", 205 ], [ "2023-2", 218, "count1", 203 ], [ "2023-2", 206, "count2", 194 ], [ "2023-2", 220, "count3", 297 ], [ "2023-2", 349, "count4", 101 ], [ "2023-2", 247, "count5", 357 ], ... ]
3、配置平均线,取dataset数组中的数据
markLine: { symbolSize: 0, data: [ { name: '平均分', xAxis: Number( data.filter(function (item) { return item[0] === startMonth; })[0][3] ), lineStyle: { color: '#eb7e65', type: 'dashed' }, label: { show: true, color: '#fe4852', formatter: '{ll|平均值:' + Number( data.filter(function (item) { return item[0] === startMonth; })[0][3] ) + '\n}', rich: { ll: { fontSize: 16, lineHeight: 22, padding: [40, 0, 0, 0] } } } } ], animation: false },
让图表动起来
其实让图表动起来,只需要动态改变echarts的数据即可实现,但是需要在echarts配置中增加动画效果
animationDuration: 0, animationDurationUpdate: updateFrequency, animationEasing: 'linear', animationEasingUpdate: 'linear',
定时从数据集合data中取出对应月份的数据
if (playChart.value) { for (let i = startIndex; i < month.length - 1; ++i) { let myTimeout = setTimeout(() => { updateMonth(month[i + 1]); activeIndex.value = i + 1; //播放结束 if (i == month.length - 2) { setTimeout(() => { playChart.value = false; }, updateFrequency); } }, (i + 1 - startIndex) * updateFrequency); myTimeoutArr.value.push(myTimeout); } } function updateMonth(month: any) { let source = data.filter(function (item) { return item[0] === month; }); console.log(source) option.series[0].data = source; option.series[0].markLine = { symbolSize: 0, data: [ { name: '平均分', xAxis: Number(source[0][3]), lineStyle: { color: '#eb7e65', type: 'dashed' }, label: { show: true, color: '#fe4852', formatter: '{ll|平均值:' + Number(source[0][3]) + '\n}', rich: { ll: { fontSize: 16, lineHeight: 22, padding: [40, 0, 0, 0] } } } } ], animation: false }; myChart.value.setOption(option); }
完整代码
demo完整代码如下,可直接复制使用
<template> <div class="chart-dialog-content"> <div class="opt-bar"> <button size="mini" v-if="playChart == false" @click="(playChart = true), initChart(0)"><i class="el-icon-video-play"></i>轮播</button> <button size="mini" v-if="playChart == true" @click="(playChart = false), initChart(activeIndex)"><i class="el-icon-video-pause"></i>暂停</button> </div> <section class="bottom"> <div class="time-line"> <div class="line-item" @click="changeChart(index)" v-for="(i, index) in timeLineList" :key="i" :class="{ active: index <= activeIndex, hover: index == activeIndex }"> <div class="line"></div> <div class="point"> <div class="text">{{ i }}</div> <div class="icon el-icon-caret-top" v-show="index == activeIndex"></div> </div> </div> </div> <div class="chart-content" id="chartContent"></div> </section> </div> </template> <script setup lang="ts"> import { onMounted, ref, inject } from 'vue'; const activeIndex = ref(0); const myChart = ref<any>(null); const playChart = ref(true); const rspData = ref<any>([]); const timeLineList = ref<any>([]); const myTimeoutArr = ref<any>([]); const $echarts: any = inject('$echarts'); const generateRandomData = () => { const data = []; for (let i = 0; i < 100; i++) { let month = '2023-'; month = month + (Math.floor(i / 10) + 1); const count = Math.floor(Math.random() * 300) + 100; // 生成100到300的随机整数 const name = 'count' + i % 10; const avg = Math.floor(Math.random() * 300) + 100; data.push({ month, count, name, avg }); if(!timeLineList.value.includes(month)){ timeLineList.value.push(month) } } return data; } onMounted(() => { playChart.value = false; getData(); }); const getData = () => { timeLineList.value = []; rspData.value = generateRandomData(); initChart(0); } const changeChart = (index: number, flag = false) => { playChart.value = flag; initChart(index); } const initChart = (index = 0) => { myChart.value?.dispose(); //清理定时 myTimeoutArr.value.forEach((item: any) => { clearTimeout(item); }); myTimeoutArr.value = []; // 获取$echarts实例 myChart.value = $echarts.init(document.querySelector('#chartContent')); const updateFrequency = 3000; const dimension = 1; const data: any[] = []; rspData.value.forEach((item: any) => { data.push([item.month, item.count, item.name, item.avg]); }); const month: any[] = []; data.forEach((item) => { if (month.length === 0 || month[month.length - 1] !== item[0]) { month.push(item[0]); } }); let startIndex = index; let startMonth = month[startIndex]; let option = { grid: { top: 10, bottom: 30, left: 20, right: 40, containLabel: true }, xAxis: { max: 'dataMax', min: function (value: any) { return Math.round(value.min) - 10; }, axisLabel: { formatter: function (n: any) { return n; } } }, dataset: { source: data.filter(function (item) { return item[0] === startMonth; }) }, yAxis: { type: 'category', inverse: true, axisTick: { show: false }, axisLine: { show: true, lineStyle: { color: '#e2e2e2' } }, axisLabel: { show: true, fontSize: 14, formatter: function (value: any) { return value; }, color: '#8c8c8d', rich: { flag: { fontSize: 25, padding: 5 } } }, animationDuration: 300, animationDurationUpdate: 300 }, series: [ { realtimeSort: true, seriesLayoutBy: 'column', type: 'bar', itemStyle: { color: function () { return '#7ea2f4'; } }, markLine: { symbolSize: 0, data: [ { name: '平均分', xAxis: Number( data.filter(function (item) { return item[0] === startMonth; })[0][3] ), lineStyle: { color: '#eb7e65', type: 'dashed' }, label: { show: true, color: '#fe4852', formatter: '{ll|平均值:' + Number( data.filter(function (item) { return item[0] === startMonth; })[0][3] ) + '\n}', // distance: [-135, 150], // padding: [-50, 0, 50, 0], rich: { ll: { fontSize: 16, lineHeight: 22, padding: [40, 0, 0, 0] } } } } ], animation: false }, barMaxWidth: 20, encode: { x: dimension, y: 2 }, label: { show: true, // precision: 1, position: 'right', valueAnimation: true, fontFamily: 'monospace' } } ], // Disable init animation. animationDuration: 0, animationDurationUpdate: updateFrequency, animationEasing: 'linear', animationEasingUpdate: 'linear', // graphic: { // elements: [ // { // type: 'text', // right: 0, // bottom: 0, // style: { // text: startMonth, // font: 'bolder 24px monospace', // fill: 'rgba(100, 100, 100, 0.25)' // }, // z: 100 // } // ] // } }; myChart.value.setOption(option); activeIndex.value = index; if (playChart.value) { for (let i = startIndex; i < month.length - 1; ++i) { let myTimeout = setTimeout(() => { updateMonth(month[i + 1]); activeIndex.value = i + 1; //播放结束 if (i == month.length - 2) { setTimeout(() => { playChart.value = false; }, updateFrequency); } }, (i + 1 - startIndex) * updateFrequency); myTimeoutArr.value.push(myTimeout); } } function updateMonth(month: any) { let source = data.filter(function (item) { return item[0] === month; }); console.log(source) option.series[0].data = source; option.series[0].markLine = { symbolSize: 0, data: [ { name: '平均分', xAxis: Number(source[0][3]), lineStyle: { color: '#eb7e65', type: 'dashed' }, label: { show: true, color: '#fe4852', formatter: '{ll|平均值:' + Number(source[0][3]) + '\n}', rich: { ll: { fontSize: 16, lineHeight: 22, padding: [40, 0, 0, 0] } } } } ], animation: false }; // option.graphic.elements[0].style.text = month; myChart.value.setOption(option); } } </script> <style lang="less" scoped> .chart-dialog-content { .chart-content { height: 550px; width: 100%; margin: 0 auto; } .opt-bar { padding: 12px 16px; display: flex; align-items: center; >div { margin-right: 16px; } i { margin-right: 4px; } .desc { font-weight: bold; margin-right: 8px; } .el-date-editor { width: 170px; } } .bottom { padding: 16px; background: #f2f4f7; .time-line { display: flex; width: 80%; height: 36px; margin: 0 auto; .line-item { display: flex; align-items: center; width: 100%; cursor: pointer; &:first-child { width: 10px; .line { width: 0; } } &.hover { .text { color: #467ff1; } } &.active { .point { border: 2px solid #477ff0; .text { font-weight: 500; } } .line { background: #477ff0; } } .point { position: relative; width: 6px; height: 6px; border-radius: 50%; background: #fff; .text { position: absolute; white-space: nowrap; transform: translate(-50%, -135%); } .icon { font-size: 32px; position: absolute; color: #fff; transform: translate(-50%, 0); } } .line { width: 100%; height: 3px; background: #efefef; } } } .chart-content { background: #fff; } .type-radio { padding: 12px; background: #fff; .el-radio { font-weight: 400; color: rgba(0, 0, 0, 0.65); margin-right: 12px; } } } } </style>
以上就是vue使用echarts实现柱状图动态排序效果的详细内容,更多关于vue echarts动态柱状图的资料请关注脚本之家其它相关文章!
您可能感兴趣的文章:
- vue3+echarts实现好看的圆角环形图
- echarts设置tootip轮播切换展示(vue3搭配vue-echarts粘贴即用)
- 手把手教你Vue3 按需引入 Echarts的过程(收藏)
- vue导出excel和echart图形分别在不同工作表的实现方法
- vue2.0如何实现echarts饼图(pie)效果展示
- vue中如何使用echarts动态渲染数据
- vue使用echarts实现动态数据的示例详解
- Vuex进行Echarts数据页面初始化后如何更新dom
- vue+echarts图表的基本使用步骤总结
- 在Vue中使用Echarts+封装
- 使用vue3+ts打开echarts的正确方式
- vue3中使用Vchart的示例代码