基础从0实现echarts瀑布图

92 阅读3分钟

简介:实现思路纯为本人所想,欢迎交流,考虑拓展场景才自己去实现一个图表,但是单纯也是为了好玩才去实现的,代码冗余部分欢迎给出优化建议,但是均不采纳,因为懒得去优化。

实现思路:图文结合

image.png

image.png

image.png

image.png

/**
 * 将 dataItems 处理成需要的数据结构类型
 * 
 * @param {Object} series - 数据系列,包含需要处理的数据
 * @param {string} position - 数据位置,决定数据的排列顺序
 * @param {string} increase_color - 上升数据的颜色
 * @param {string} decrease_color - 下降数据的颜色
 * @param {string} sum_color - 累计数据的颜色
 * @returns {Array} - 返回经过处理后的数据结构
 */
function processDataItems(
  series,
  position,
  increase_color,
  decrease_color,
  sum_color
) {
  // 获取唯一的 ID 生成器
  const idUniqueHandler = uniqueHandler();
  
  // 复制第一个系列配置
  const seriesItemConfig = { ...series[0] };

  // 创建虚拟对象,用于瀑布图的初始结构
  const virtualObject = {
    ...seriesItemConfig,
    data: [],
    label: { show: false },
    emphasis: { label: { show: false } },
    id: idUniqueHandler('virtualObject#waterFallFigure'),
    relatedSeriesName: seriesItemConfig.name,
    barMinHeight: 0,
    stack: 'Total',
    isVisualSeries: true,
    tooltip: { show: false },
  };

  // 顶部对象,用于显示正数
  const topObject = {
    ...seriesItemConfig,
    data: [],
    stack: 'Total',
  };

  // 底部对象,用于显示负数
  const bottomObject = {
    ...seriesItemConfig,
    data: [],
    id: idUniqueHandler('bottomObject#waterFallFigure'),
    stack: 'Total',
    barMinHeight: 0,
    relatedSeriesName: seriesItemConfig.name,
    isVisualSeries: true,
  };

  const seriesData = series[0].data;
  const len = seriesData.length;
  const resData = [];
  let currentVirtualValue = 0;
  let lastVirtualValue = 0;
  let sum = 0;

  // 遍历数据系列,构建瀑布图数据结构
  for (let i = 0; i < len; i++) {
    const item = seriesData[i];
    sum += item.value;

    const isPositiveValue = Math.sign(item.value) >= 0;
    const isPositiveVirtualValue = Math.sign(currentVirtualValue) >= 0;

    // 处理正数的逻辑
    if (isPositiveValue) {
      if (i === 0 || isPositiveVirtualValue) {
        topObject.data.push({
          ...item,
          value: item.value,
          itemStyle: { color: increase_color },
        });
        bottomObject.data.push({ ...item, value: 0, label: { show: false } });
        virtualObject.data.push({
          ...item,
          value: currentVirtualValue,
          itemStyle: { color: 'transparent' },
          tooltip: { show: false }
        });
        currentVirtualValue += item.value;
      } else {
        lastVirtualValue = currentVirtualValue;
        currentVirtualValue += item.value;
        if (Math.sign(lastVirtualValue) < 0 && Math.sign(currentVirtualValue) < 0) {
          topObject.data.push({
            ...item,
            value: -item.value,
            itemStyle: { color: increase_color },
          });
          bottomObject.data.push({ ...item, value: 0, label: { show: false } });
          virtualObject.data.push({
            ...item,
            value: currentVirtualValue,
            itemStyle: { color: 'transparent' },
            tooltip: { show: false }
          });
        } else {
          topObject.data.push({
            ...item,
            value: -item.value + currentVirtualValue,
            itemStyle: { color: increase_color },
          });
          bottomObject.data.push({ ...item, value: 0, label: { show: false } });
          virtualObject.data.push({
            ...item,
            value: currentVirtualValue,
            itemStyle: { color: increase_color },
          });
          virtualObject.tooltip.show = true;
        }
      }
    } else {
      // 处理负数的逻辑
      if (i === 0 || !isPositiveVirtualValue) {
        topObject.data.push({ ...item, value: 0, label: { show: false } });
        bottomObject.data.push({
          ...item,
          value: item.value,
          itemStyle: { color: decrease_color },
        });
        virtualObject.data.push({
          ...item,
          value: currentVirtualValue,
          itemStyle: { color: 'transparent' },
          tooltip: { show: false }
        });
        currentVirtualValue += item.value;
      } else {
        lastVirtualValue = currentVirtualValue;
        currentVirtualValue += item.value;
        if (Math.sign(lastVirtualValue) > 0 && Math.sign(currentVirtualValue) > 0) {
          topObject.data.push({ ...item, value: 0, label: { show: false } });
          bottomObject.data.push({
            ...item,
            value: -item.value,
            itemStyle: { color: decrease_color },
          });
          virtualObject.data.push({
            ...item,
            value: currentVirtualValue,
            itemStyle: { color: 'transparent' },
            tooltip: { show: false }
          });
        } else {
          topObject.data.push({ ...item, value: 0, label: { show: false } });
          bottomObject.data.push({
            ...item,
            value: -item.value + currentVirtualValue,
            itemStyle: { color: decrease_color },
          });
          virtualObject.data.push({
            ...item,
            value: currentVirtualValue,
            itemStyle: { color: decrease_color },
          });
          virtualObject.tooltip.show = true;
        }
      }
    }
  }

  // 添加累计数据项
  virtualObject.data.push({ value: 0, name: '累计', label: { show: false } });
  topObject.data.push({ value: sum, name: '累计', itemStyle: { color: sum_color } });
  bottomObject.data.push({ value: 0, name: '累计', label: { show: false } });

  // 如果位置在左侧,反转数据顺序
  if (position === 'left') {
    virtualObject.data.reverse();
    topObject.data.reverse();
    bottomObject.data.reverse();
  }

  // 返回处理好的数据结构
  resData.push({ ...virtualObject, barMinHeight: 0 }, { ...topObject, barMinHeight: 0 }, { ...bottomObject, barMinHeight: 0 });
  return resData;
}


/**
 * 处理 formater 数据
 * 
 * @param {Array} resData - 包含瀑布图数据结构的数组
 * @returns {Array} - 返回处理后的数据结构
 */
function processFormaterData(resData) {
  // 获取各个数据对象的引用
  const virtualObjData = resData[0].data;
  const topObjData = resData[1].data;
  const bottomObjData = resData[2].data;

  // 获取虚拟对象数据的长度
  const virtualObjDataLen = virtualObjData.length;

  // 遍历每个虚拟对象数据项,进行格式化处理
  for (let i = 0; i < virtualObjDataLen; i++) {
    // 检查当前虚拟数据项的值是否有效且不透明
    if (virtualObjData[i].value !== 0 && virtualObjData[i].itemStyle.color !== "transparent") {
      // 处理顶部对象数据
      if (topObjData[i].value !== 0 && !isNaN(topObjData[i].value) && !isNaN(virtualObjData[i].value)) {
        _.merge(virtualObjData[i], {
          formatValue: Number(virtualObjData[i].value) + Number(topObjData[i].value)
        });
        _.merge(topObjData[i], {
          formatValue: Number(virtualObjData[i].value) + Number(topObjData[i].value)
        });
      } else {
        // 处理底部对象数据
        if (bottomObjData[i].value !== 0 && !isNaN(virtualObjData[i].value) && !isNaN(bottomObjData[i].value)) {
          _.merge(virtualObjData[i], {
            formatValue: Number(virtualObjData[i].value) + Number(bottomObjData[i].value)
          });
          _.merge(bottomObjData[i], {
            formatValue: Number(virtualObjData[i].value) + Number(bottomObjData[i].value)
          });
        }
      }
    }
  }

  // 合并处理后的数据返回
  _.merge(resData[0], { data: virtualObjData });
  _.merge(resData[1], { data: topObjData });
  _.merge(resData[2], { data: bottomObjData });

  return resData;
}

本文函数中返回的内容为series值,如有疑问欢迎打扰

image.png