当前位置: 首页 > article >正文

封装渐变堆叠柱状图组件附完整代码

在这里插入图片描述

组件功能

这是一个渐变堆叠柱状图组件,主要功能包括:

  1. 在一根柱子上同时显示高、中、低三种危险级别数据
  2. 使用渐变色区分不同危险级别(高危红色、中危橙色、低危蓝色)
  3. 悬停显示详细数据信息(包括总量和各级别数据)
  4. 自适应容器大小变化

使用方法在父组件中引入组件并传入数据

<template><JianbianZhu :warningData="warningData" :warningSevenItem="warningSevenItem" />
</template><script>
export default {data() {return {warningData: {high: [30, 40, 35, 50, 60, 40, 80],    // 高危数据middle: [70, 60, 65, 60, 60, 80, 70],  // 中危数据low: [50, 70, 80, 70, 60, 70, 60]      // 低危数据},warningSevenItem: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] // X轴标签}}
}
</script>

核心代码实现

1. 堆叠柱状图配置

// 核心实现:创建堆叠柱状图,三个系列分别代表低、中、高危
series: [{name: '低危',type: 'bar',stack: '总量',  // 设置堆叠,关键属性data: lowData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#70B2F7' },  // 顶部颜色{ offset: 0.5, color: '#52A2FF' }, // 中间颜色{ offset: 1, color: '#1970C2' }   // 底部颜色])}},{name: '中危',type: 'bar',stack: '总量',data: middleData,// 中危渐变色配置...},{name: '高危',type: 'bar',stack: '总量',data: highData,// 高危渐变色配置...}
]

2. 渐变色实现

// 通过LinearGradient创建渐变效果
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#FF2E2E' },  // 顶部颜色(更鲜艳){ offset: 0.5, color: '#FF5252' }, // 中间颜色{ offset: 1, color: '#FF8A8A' }   // 底部颜色(过渡到中危)
]),

3. 数据提示框配置

tooltip: {trigger: 'axis',formatter: function(params) {const index = params[0].dataIndex;const date = xAxisData[index];const total = totalData[index] || 0;let result = `${date}<br/>总数: ${total}<br/>`;// 添加各危险级别数据params.forEach((param) => {let value = param.value || 0;result += `${param.seriesName}: ${value}<br/>`;});return result;}
}

4. 数据变化更新

// 监听数据变化,只在真正变化时更新图表
watch(() => [props.warningData, props.warningSevenItem], ([newWarningData, newWarningSevenItem]) => {const newWarningDataStr = JSON.stringify(newWarningData);const newWarningSevenItemStr = JSON.stringify(newWarningSevenItem);// 检查数据是否有变化const dataChanged = newWarningDataStr !== prevWarningData.value || newWarningSevenItemStr !== prevWarningSevenItem.value;if (dataChanged) {if (chartInstance) {updateChart();} else {initChart();}}
}, { deep: true });

自定义调整

  1. 修改显示顺序:调整series数组中三个对象的顺序即可改变柱状图中高中低危的堆叠顺序

  2. 调整颜色:修改各系列的LinearGradient配置可以改变渐变色效果

  3. 调整圆角:目前顶部系列设置了borderRadius: [2, 2, 0, 0]实现顶部圆角效果

完整组件代码:

<template><div ref="chartContainer" class="chart-container"><div ref="chart" class="chart"></div></div>
</template><script lang="ts">
import { defineComponent, onMounted, ref, watch, onUnmounted } from 'vue';
import * as echarts from 'echarts';export default defineComponent({name: 'JianbianZhu',props: {warningData: {type: Object,required: true},warningSevenItem: {type: Array,required: true}},setup(props) {const chartRef = ref<HTMLElement | null>(null);let chartInstance: echarts.ECharts | null = null;// 保存上一次的数据快照,用于比较数据是否变化const prevWarningData = ref<string>('');const prevWarningSevenItem = ref<string>('');// 处理窗口大小变化const handleResize = () => {if (chartInstance) {chartInstance.resize();}};const initChart = () => {if (!chartRef.value) return;// 创建图表实例chartInstance = echarts.init(chartRef.value);// 准备数据const xAxisData = props.warningSevenItem;const highData = props.warningData.high || [];const middleData = props.warningData.middle || [];const lowData = props.warningData.low || [];// 计算总数据用于展示const totalData = highData.map((val: number, index: number) => {return val + (middleData[index] || 0) + (lowData[index] || 0);});// 更新数据快照prevWarningData.value = JSON.stringify(props.warningData);prevWarningSevenItem.value = JSON.stringify(props.warningSevenItem);// 配置图表选项const option = {tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'},formatter: function(params: any) {const index = params[0].dataIndex;const date = xAxisData[index];const total = totalData[index] || 0;let result = `${date}<br/>总数: ${total}<br/>`;// 按顺序添加高中低危数据params.forEach((param: any) => {let value = param.value || 0;result += `${param.seriesName}: ${value}<br/>`;});return result;}},legend: {data: ['低危', '中危', '高危'],textStyle: {color: 'rgba(255, 255, 255, 0.65)'},right: '5%',top: '0%'},grid: {left: '5%',right: '5%',bottom: '10%',top: '15%',containLabel: true},xAxis: {type: 'category',data: xAxisData,axisLine: {lineStyle: {color: 'rgba(255, 255, 255, 0.2)'}},axisLabel: {color: 'rgba(255, 255, 255, 0.65)',fontSize: 12,interval: 0,rotate: 0},axisTick: {show: false}},yAxis: {type: 'value',name: '',nameTextStyle: {color: 'rgba(255, 255, 255, 0.65)'},min: 0,axisLine: {show: false},axisTick: {show: false},splitLine: {lineStyle: {color: 'rgba(255, 255, 255, 0.1)',type: 'dashed',width: 0.5}},axisLabel: {color: 'rgba(255, 255, 255, 0.65)',fontSize: 12,formatter: function(value: number) {if (value >= 1000) {return Math.floor(value / 1000) + 'k';}return value;}}},series: [{name: '低危',type: 'bar',stack: '总量',data: lowData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#70B2F7' },  // 顶部颜色(与中危底部接近){ offset: 0.5, color: '#52A2FF' }, // 中间颜色{ offset: 1, color: '#1970C2' }   // 底部颜色(更深)])},emphasis: {itemStyle: {opacity: 0.9}},z: 10},{name: '中危',type: 'bar',stack: '总量',data: middleData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#FFA066' },  // 顶部颜色(与高危底部接近){ offset: 0.5, color: '#FFA647' }, // 中间颜色{ offset: 1, color: '#FFD0A1' }   // 底部颜色(过渡到低危)])},emphasis: {itemStyle: {opacity: 0.9}},z: 10},{name: '高危',type: 'bar',stack: '总量',data: highData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#FF2E2E' },  // 顶部颜色(更鲜艳){ offset: 0.5, color: '#FF5252' }, // 中间颜色{ offset: 1, color: '#FF8A8A' }   // 底部颜色(过渡到中危)]),borderRadius: [2, 2, 0, 0] // 只有最上面的柱子需要圆角},emphasis: {itemStyle: {opacity: 0.9}},z: 10},],backgroundColor: 'transparent'};// 设置图表选项chartInstance.setOption(option);// 监听窗口大小变化自动调整图表大小window.addEventListener('resize', handleResize);};// 更新图表,不销毁实例const updateChart = () => {if (!chartInstance || !chartRef.value) return;// 准备数据const xAxisData = props.warningSevenItem;const highData = props.warningData.high || [];const middleData = props.warningData.middle || [];const lowData = props.warningData.low || [];// 计算总数据用于展示const totalData = highData.map((val: number, index: number) => {return val + (middleData[index] || 0) + (lowData[index] || 0);});// 更新数据快照prevWarningData.value = JSON.stringify(props.warningData);prevWarningSevenItem.value = JSON.stringify(props.warningSevenItem);// 解决方案:创建完整的配置,使用true参数强制重置所有配置// 这将确保tooltip格式化器使用最新数据chartInstance.clear();  // 清除当前图表// 完整重新配置图表const option = {tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'},formatter: function(params: any) {const index = params[0].dataIndex;const date = xAxisData[index];const total = totalData[index] || 0;let result = `${date}<br/>总数: ${total}<br/>`;// 按顺序添加高中低危数据params.forEach((param: any) => {let value = param.value || 0;result += `${param.seriesName}: ${value}<br/>`;});return result;}},legend: {data: ['高危', '中危', '低危'],textStyle: {color: 'rgba(255, 255, 255, 0.65)'},right: '5%',top: '0%'},grid: {left: '5%',right: '5%',bottom: '10%',top: '15%',containLabel: true},xAxis: {type: 'category',data: xAxisData,axisLine: {lineStyle: {color: 'rgba(255, 255, 255, 0.2)'}},axisLabel: {color: 'rgba(255, 255, 255, 0.65)',fontSize: 12,interval: 0,rotate: 0},axisTick: {show: false}},yAxis: {type: 'value',name: '',nameTextStyle: {color: 'rgba(255, 255, 255, 0.65)'},min: 0,axisLine: {show: false},axisTick: {show: false},splitLine: {lineStyle: {color: 'rgba(255, 255, 255, 0.1)',type: 'dashed',width: 0.5}},axisLabel: {color: 'rgba(255, 255, 255, 0.65)',fontSize: 12,formatter: function(value: number) {if (value >= 1000) {return Math.floor(value / 1000) + 'k';}return value;}}},series: [{name: '高危',type: 'bar',stack: '总量',data: highData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#FF2E2E' },  // 顶部颜色(更鲜艳){ offset: 0.5, color: '#FF5252' }, // 中间颜色{ offset: 1, color: '#FF8A8A' }   // 底部颜色(过渡到中危)]),borderRadius: [2, 2, 0, 0] // 只有最上面的柱子需要圆角},emphasis: {itemStyle: {opacity: 0.9}},z: 10},{name: '中危',type: 'bar',stack: '总量',data: middleData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#FFA066' },  // 顶部颜色(与高危底部接近){ offset: 0.5, color: '#FFA647' }, // 中间颜色{ offset: 1, color: '#FFD0A1' }   // 底部颜色(过渡到低危)])},emphasis: {itemStyle: {opacity: 0.9}},z: 10},{name: '低危',type: 'bar',stack: '总量',data: lowData,barWidth: '20%',itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#70B2F7' },  // 顶部颜色(与中危底部接近){ offset: 0.5, color: '#52A2FF' }, // 中间颜色{ offset: 1, color: '#1970C2' }   // 底部颜色(更深)])},emphasis: {itemStyle: {opacity: 0.9}},z: 10}],backgroundColor: 'transparent'};// 使用完整配置重新初始化图表chartInstance.setOption(option);};// 在组件挂载后初始化图表onMounted(() => {initChart();});// 监听数据变化,仅在数据真正变化时更新图表watch(() => [props.warningData, props.warningSevenItem], ([newWarningData, newWarningSevenItem]) => {const newWarningDataStr = JSON.stringify(newWarningData);const newWarningSevenItemStr = JSON.stringify(newWarningSevenItem);// 检查数据是否有变化const dataChanged = newWarningDataStr !== prevWarningData.value || newWarningSevenItemStr !== prevWarningSevenItem.value;if (dataChanged) {// 如果数据有变化,更新图表if (chartInstance) {updateChart();} else {initChart();}}}, { deep: true });// 组件卸载时清理资源onUnmounted(() => {if (chartInstance) {// 移除resize事件监听window.removeEventListener('resize', handleResize);chartInstance.dispose();chartInstance = null;}});return {chart: chartRef};}
});
</script><style scoped>
.chart-container {width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;
}.chart {width: 100%;height: 100%;min-height: 280px;
}
</style>
http://www.lryc.cn/news/2387485.html

相关文章:

  • 分布式项目保证消息幂等性的常见策略
  • 山东大学软件学院创新项目实训开发日志——第十三周
  • 如何在sublime text中批量为每一行开头或者结尾添加删除指定内容
  • Cesium 透明渐变墙 解决方案
  • 网络原理与 TCP/IP 协议详解
  • day022-定时任务-故障案例与发送邮件
  • 新增 git submodule 子模块
  • List优雅分组
  • Linux 使用 Docker 安装 Milvus的两种方式
  • AR眼镜+AI视频盒子+视频监控联网平台:消防救援的智能革命
  • 编程技能:字符串函数10,strchr
  • 使用tunasync部署企业内部开源软件镜像站-Centos Stream 9
  • c/c++的opencv像素级操作二值化
  • C++----Vector的模拟实现
  • Mac redis下载和安装
  • [25-cv-05718]BSF律所代理潮流品牌KAWS公仔(商标+版权)
  • 【PhysUnits】9 取负重载(negation.rs)
  • 深度思考、弹性实施,业务流程自动化的实践指南
  • UWB:litepoint获取txquality里面的NRMSE
  • VUE npm ERR! code ERESOLVE, npm ERR! ERESOLVE could not resolve, 错误有效解决
  • IoT/HCIP实验-1/物联网开发平台实验Part1(快速入门,MQTT.fx对接IoTDA)
  • DMA STM32H7 Domains and space distrubution
  • 洪水危险性评价与风险防控全攻略:从HEC-RAS数值模拟到ArcGIS水文分析,一键式自动化工具实战,助力防洪减灾与应急管理
  • Gemini Pro 2.5 输出
  • SQL Server 和 MySQL 对比
  • Leetcode 3269. 构建两个递增数组
  • 三轴云台之积分分离PID控制算法篇
  • 【Elasticsearch】scripted_upsert
  • uv - 一个现代化的项目+环境管理工具
  • 经典密码学和现代密码学的结构及其主要区别(2)维吉尼亚密码—附py代码