Echarts 阶梯瀑布图实现从正数跨到负数,以及负数跨到正数

由于官方的demo无法实现跨轴展示效果,故进行了下面的调整,并且实现了各项数据的统一计算,只需传入正负值的datas数据,即可自动区分各项的数据。由于传入的数字可能是小数,所以引入了math.js,防止数据相加时出现精度丢失的问题。我这里的math.js是经过实际需要进行了重新封装的,详情请参考https://www.npmjs.com/package/mathjs

import math from 'utils/math.js';
/*
* 收入支出合计瀑布柱形图
* @param {string} title 标题
* @param {array} labels: X轴labels
* @param {array} datas: 数据,整数为收入,负数为支出
* @param {number} rotate: x轴文本倾斜角度
* @param {number} maxNum: x轴文本最大显示字符数,超出显示省略号
*/ 
function  chartOption(title, labels, datas, rotate = 45, maxNum = 4) {
  const dataSum = [];   // 透明项的data数据
  const dataIn1 = [];	// 收入上半段的data数据 (跨象限时)
  const dataIn2 = [];	// 收入下半段的data数据 (跨象限时)
  const dataOut1 = [];  // 支出上半段的data数据 (跨象限时)
  const dataOut2 = [];	// 支出下半段的data数据 (跨象限时)
  const dataTotal = []; // 最终金额合计的data数据
  const setSumIndex = [];	// 有跨象限时值的坐标
  const len = datas.length;
  // 组装数据
  datas.forEach((item, index) => {
    if (index === 0) {
      dataSum.push(0);
    } else if (index < len - 1) {
      let num = 0;
      for(let i = 0; i < index; i++) {
        num = math.add(num, datas[i]);
      }
      if ((item >= 0 && num >= 0) || (item < 0 && num <= 0)) {
        dataSum.push(num);
      } else {
        num = math.add(num, item);
        dataSum.push(num);
      }   
    } else {
      dataSum.push(0);
    }
    if (index === len - 1) {
      dataIn1.push('-');
      dataIn2.push('-');
      dataOut1.push('-');
      dataOut2.push('-');
      dataTotal.push(item);
    } else if (item >= 0) {
      // 当前起始位置
      let beginNum = 0;
      for(let i = 0; i < index; i++) {
        beginNum = math.add(beginNum, datas[i]);
      }
      if (index === 0 || beginNum >= 0) {
        dataIn1.push(item);
        dataIn2.push('-');
      } else if (beginNum < 0) {
        if (Math.abs(beginNum) === item) {
          dataIn1.push(item);
          dataIn2.push('-');
        } else if (Math.abs(beginNum) > item) {
          dataIn1.push(item * -1);
          dataIn2.push('-');
        } else {
          dataIn1.push(math.add(item, beginNum));
          dataIn2.push(beginNum);
          setSumIndex.push(index);
        }
      }
      dataOut1.push('-');
      dataOut2.push('-');
      dataTotal.push('-');
    } else if (item < 0) {
      dataIn1.push('-');
      dataIn2.push('-');
      // 当前起始位置
      let beginNum = 0;
      for(let i = 0; i < index; i++) {
        beginNum = math.add(beginNum, datas[i]);
      }
      if (index === 0 || beginNum <= 0) {
        dataOut1.push(item);
        dataOut2.push('-');
      } else if (beginNum > 0) {
        if (beginNum >= Math.abs(item)) {
          dataOut1.push(item * -1);
          dataOut2.push('-');          
        } else {
          dataOut1.push(math.add(item, beginNum));
          dataOut2.push(beginNum);
          setSumIndex.push(index);
        }
      }
      dataTotal.push('-');
    }
  });
  // 跨分区的为0
  setSumIndex.forEach(item => {
    dataSum[item] = 0;
  })
  const option = {
    title: {
      text: title,
      subtext: '人民币'
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      },
      formatter: (params) => {
        let tar = null;
        let val = 0;
        const len = params.length;
        for (let i = 1; i < len; i++) {
          if (params[i].value !== '-') {
            if (tar === null) {
              tar = params[i];
            }
            if (tar.seriesName === '合计') {
              val = math.add(val, params[i].value);
            } else {
              val = math.add(val, Math.abs(params[i].value));
            }            
          }
        }
        // 支出显示负数
        if (tar.seriesName === '支出') {
          val = math.multi(val, -1);
        }
        return tar && tar.name + '<br/>' + tar.seriesName + ' : ' + val;
      }
    },
    legend: {
      data: ['收入', '支出', '合计'],
      x:'right',
      y:'top'
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      axisLine: {
        onZero: false
      },
      axisLabel : {//坐标轴刻度标签的相关设置。
        interval:0,
        rotate: rotate, // 倾斜45度
        formatter(value) {
          if (value.length > maxNum) {
            return `${value.slice(0, maxNum)}...`;
          }
          return value;
        }
      },
      data: labels // x轴显示的文本
    },
    yAxis: {
      type: 'value',
      axisLine: {
        show: false
      }
    },
    series: [
      {
        name: 'Placeholder',
        type: 'bar',
        stack: 'Total',
        silent: true,
        itemStyle: {
          borderColor: 'transparent',
          color: 'transparent'
        },
        emphasis: {
          itemStyle: {
            borderColor: 'transparent',
            color: 'transparent'
          }
        },
        data: dataSum
      }, {
        name: '收入',
        type: 'bar',
        stack: 'Total',
        label: {
          show: true,
          position: 'top',
          formatter: (params) => {
            const dataIndex = params.dataIndex;
            if (dataIn1[dataIndex] !== '-') {
              const d1 = Math.abs(dataIn1[dataIndex]);
              const d2 = Math.abs(dataIn2[dataIndex] === '-' ? 0 : dataIn2[dataIndex]);
              return math.add(d1, d2);
            } else {
              return '';
            }
          }
        },
        data: dataIn1        
      }, {
        name: '收入',
        type: 'bar',
        stack: 'Total',
        label: {
          show: false,
          position: 'top'
        },
        data: dataIn2
      }, {
        name: '支出',
        type: 'bar',
        stack: 'Total',
        label: {
          show: true,
          position: 'bottom',
          formatter: (params) => {
            const dataIndex = params.dataIndex;
            if (dataOut1[dataIndex] !== '-') {
              const d1 = Math.abs(dataOut1[dataIndex]);
              const d2 = Math.abs(dataOut2[dataIndex] === '-' ? 0 : dataOut2[dataIndex]);
              return math.multi(math.add(d1, d2), -1);
            } else {
              return '';
            }
          }
        },
        data: dataOut1
      }, {
        name: '支出',
        type: 'bar',
        stack: 'Total',
        label: {
          show: false,
          position: 'bottom'
        },
        data: dataOut2
      }, {
        name: '合计',
        type: 'bar',
        stack: 'Total',
        label: {
          show: true,
          position: 'top'
        },
        data: dataTotal
      }
    ]
  };
  return option;
}

实现效果图如下:
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ECharts是一个由百度开发的基于JavaScript的开源可视化库,用于创建交互式的数据可视化图表。它提供了丰富的图表类型和灵活的配置选项,使用户能够轻松地创建各种各样的图表,包括折线图、柱状图、饼图、散点图等。 阶梯折线图是ECharts的一种折线图类型,它通过连接数据点的水平线段和垂直线段来呈现数据的变化趋势。与普通折线图相比,阶梯折线图更加强调数据的离散性和变化的步进性,适用于展示离散数据的变化情况。 要创建一个阶梯折线图,你可以使用ECharts提供的API来配置图表的样式、数据和交互行为。以下是一个简单的示例代码: ```javascript // 引入ECharts库 import echarts from 'echarts'; // 创建一个具有指定宽度和高度的容器 const chartContainer = document.getElementById('chart'); // 初始化图表 const chart = echarts.init(chartContainer); // 配置图表选项 const option = { title: { text: '阶梯折线图示例', }, xAxis: { type: 'category', data: ['A', 'B', 'C', 'D', 'E'], }, yAxis: { type: 'value', }, series: [{ type: 'line', step: 'start', // 设置为start表示阶梯折线图 data: [120, 200, 150, 80, 70], }], }; // 使用配置项显示图表 chart.setOption(option); ``` 以上代码创建了一个简单的阶梯折线图,x轴表示类别,y轴表示数值,数据点通过水平线段和垂直线段连接。你可以根据自己的需求修改数据和样式来创建不同类型的阶梯折线图。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值