2
头图

1.多层实现原理:每一层的x轴其实都是一个grid,里面的x轴为一个类目轴,x轴不设置data,y轴为value轴,series中填充的都是柱形图,每一个柱形图的data为{name:"要展示的名字",value=1}的形式,柱形图的间距 barGap:0,柱子的宽度barWidth自行设置,可以设置不等分,但是一一行内的barWidth加起来等于100%,即所有的柱子把x轴所要展示的区域撑满
2.不等分间距的实现:获得每一行与划分最细的x轴的(最上方)里面的数据对应关系,去设置对应的barWidth
3.框选缩放功能的实现:调用mycharts.on中类型为datazoom的函数,监听params中的startValue与endValue,然后根据初始值与结束值重新去更新要渲染的seris中的值;
tips:如果回退到最初始状态,会有bug,解决方式见代码
4.建议使用的数据格式:

[A1,B1,C1....,y1,y2,y3.....],
[A2,B1,C1....,y1,y2,y3.....],
[A3,B2,C1....,y1,y2,y3.....],
[A3,B1,C2....,y1,y2,y3.....],
[A4,B4,C2....,y1,y2,y3.....],
[A5,B4,C2....,y1,y2,y3.....],
......

A/B/C分别为x轴的第一层、第二层、第三层,种类越多的越靠近x轴
每一层中下一层的不同属性要分组靠在一起排列。
举例:

[A1,B1,C1....,y1,y2,y3.....]
[A2,B1,C1....,y1,y2,y3.....]
[A3,B2,C1....,y1,y2,y3.....] 正确

[A1,B1,C1....,y1,y2,y3.....]
[A2,B2,C1....,y1,y2,y3.....]
[A3,B1,C1....,y1,y2,y3.....] 错误

C1下相同的B1需要相邻放置

以下是源码:

//--------------初始数据部分--------------------
// 拼数据
  let series: any = [];
  
  // 存放多层x轴数据的series

  const seriesextends: KeyValue[] = [];

  // x轴的层数
  const xlevel: number = details[0].xaxisValue.length;
  // 折线图的个数
  const yAxisLineNum: number = details[0].yaxisLine.length;
  // 柱型图的个数
  const yAxisBarNum: number = details[0].yaxisBar.length;
  // 最多的x的值
  const xNum: number = details.length;
  // lenged中data的值
  const yName: string[] = [];

  // 初始化yAxisLineListObject的列表
  let yAxisLineListObject = {
    // yAxisLineValue1:['num1','num2','num3','num4']
  };

  for (let i: number = 0; i < yAxisLineNum; i++) {
    const tempyaxislinevalue: string[] = [];
    for (let j = 0; j < details.length; j++) {
      tempyaxislinevalue.push(details[j].yaxisLine[i].yvalue);
    }
    yAxisLineListObject = Object.assign({}, yAxisLineListObject, {
      [details[0].yaxisLine[i].yfield]: tempyaxislinevalue,
    });
  }
  // console.log(yAxisLineListObject);

  // 初始化yAxisBarListObject的列表
  let yAxisBarListObject = {
    // yAxisLineValue1:['num1','num2','num3','num4']
  };
  for (let i: number = 0; i < yAxisBarNum; i++) {
    const tempyaxisbarvalue: string[] = [];
    for (let j = 0; j < details.length; j++) {
      tempyaxisbarvalue.push(details[j].yaxisBar[i].yvalue);
    }
    yAxisBarListObject = Object.assign({}, yAxisBarListObject, {
      [details[0].yaxisBar[i].yfield]: tempyaxisbarvalue,
    });
  }
  // console.log(yAxisBarListObject);

  //向series里填充y的折线图数据
  for (let i: number = 0; i < Object.keys(yAxisLineListObject).length; i++) {
    yName.push(Object.keys(yAxisLineListObject)[i]);
    series.push({
      data: Object.values(yAxisLineListObject)[i],
      type: 'line',
      name: Object.keys(yAxisLineListObject)[i],
      yAxisIndex: 0,
      // xAxisIndex: 0,
    });
  }



  // console.log(series);

  //向series里填充y的柱状图数据
  for (let i: number = 0; i < Object.keys(yAxisBarListObject).length; i++) {
    yName.push(Object.keys(yAxisBarListObject)[i]);
    series.push({
      data: Object.values(yAxisBarListObject)[i],
      type: 'bar',
      name: Object.keys(yAxisBarListObject)[i],
      yAxisIndex: 1,
      // xAxisIndex: 1,
    });
  }

  
 

  // 存放所有层维度数据的数组
  const xAxisArray: any = [];

  // 先按维度的层次拼接x的名字
  const tempxaxisname = [];
  for (let i: number = 0; i < details.length; i++) {
    const tempxaxislevelname: string[] = [];
    tempxaxislevelname.push(details[i].xaxisValue[0]);
    for (let j: number = 1; j < xlevel; j++) {
      tempxaxislevelname.push(
        tempxaxislevelname[j - 1] + ',' + details[i].xaxisValue[j],
      );
    }
    tempxaxisname.push(tempxaxislevelname);
  }

  // console.log(tempxaxisname, '按层次拼接好的x的名字---------------');

  // 设置x轴的数据
  const xAxisData: any = [
    // {value:星期1}
  ];

  for (let i: number = 0; i < details.length; i++) {
    xAxisData.push({
      ['value']: tempxaxisname[i][xlevel - 1],
      ['textStyle']: {
        ['fontSize']: 8,
      },
    });
  }

  // 分维度取出x轴的名字数组
  for (let i: number = 0; i < xlevel; i++) {
    let tempxxaisvalue: KeyValue = {};
    // 该层循环确定一个维度上的名称和所占的单元格的长度
    for (let j = 0; j < details.length; j++) {
      if (
        Object.keys(tempxxaisvalue)[Object.keys(tempxxaisvalue).length - 1] ===
        tempxaxisname[j][i]
      ) {
        //如果和最后一位重复,则合并单元格,长度+1

        // console.log("重复,需要合并");
        const lastkey =
          Object.keys(tempxxaisvalue)[Object.keys(tempxxaisvalue).length - 1];
        // console.log(lastkey);
        tempxxaisvalue[lastkey]++;
      } else {
        // console.log("不重复,不需要合并");

        tempxxaisvalue = Object.assign({}, tempxxaisvalue, {
          [tempxaxisname[j][i]]: 1,
        });
      }
    }
    xAxisArray.push(tempxxaisvalue);
  }
  //    外层循环走完所有的维度都已经拼成了一个对象数组,对象里面分别包裹着每一层维度的名称和对应的长度,一个对象就是一个维度

  // console.log(
  //   xAxisArray,
  //   '要给多层x轴进行渲染的xAxisArray对象数据----------------',
  // );

  xAxisArray.reverse();

  //    设置grid的值
  const grid: any = [
    {
      top: 100,
      bottom: (xlevel + 2) * 30,
    },
    {
      top: 100,
      bottom: (xlevel + 2) * 30,
    },
  ];

  // 设置多层的x轴,配置series,grid,xAxis,yAxis
  const xAxisExtends: any = [];
  const yAxisExtends: any = [];
  for (let i: number = 0; i < xlevel; i++) {
    grid.push({
      height: 30,
      bottom: (xlevel - i + 1) * 30,
      tooltip: {
        // show: false,
      },
    });
    xAxisExtends.push({
      type: 'category',
      gridIndex: i + 2,
      axisLine: { show: false },
      axisLabel: { show: false },
      axisTick: { show: false },
    });
    yAxisExtends.push({
      type: 'value',
      gridIndex: i + 2,
      axisLine: { show: false },
      axisLabel: { show: false },
      axisTick: { show: false },
    });
    // 当前该维度的名字字符串和对应的所占单元格长度的字符串
    const tempsingleXAxisName: string[] = Object.keys(xAxisArray[i]);
    const tempsingleXAxisLength: number[] = Object.values(xAxisArray[i]);
    // console.log(xAxisArray);

    // 依次填入该维度的所有出现的名称与匹配的所占单元格的长度
    for (let j: number = 0; j < Object.keys(xAxisArray[i]).length; j++) {
      // 设置填充进bar中的name 的内容和样式,当长度大于当前bar的宽度时,以省略号显示
      let tempXAxisname: String = '';
      // tempsingleXAxisName[j].substring(tempsingleXAxisName[j].lastIndexOf(',')+1)为要展示的全部的字符串
      if (
        tempsingleXAxisName[j].substring(
          tempsingleXAxisName[j].lastIndexOf(',') + 1,
        ).length >
        (tempsingleXAxisLength[j] / xNum) * 100 - 1
      )
        tempXAxisname =
          tempsingleXAxisName[j]
            .substring(tempsingleXAxisName[j].lastIndexOf(',') + 1)
            .substring(0, Math.floor((tempsingleXAxisLength[j] / xNum) * 100)) +
          '..';
      else
        tempXAxisname = tempsingleXAxisName[j].substring(
          tempsingleXAxisName[j].lastIndexOf(',') + 1,
        );

      seriesextends.push({
        type: 'bar',
        barWidth: (tempsingleXAxisLength[j] / xNum) * 100 + '%',
        data: [{ name: tempXAxisname, value: 1 }],
        barGap: 0,
        label: {
          show: true,
          position: 'inside',
          formatter: '{b}',
          // offset: [0, 10],
        },
        itemStyle: {
          color: 'none',
          borderColor: '#000',
          borderWidth: 1,
        },
        animation: false,
        tooltip: {
          formatter: tempsingleXAxisName[j].substring(
            tempsingleXAxisName[j].lastIndexOf(',') + 1,
          ),
        },
        // barWidth:
        xAxisIndex: i + 2,
        yAxisIndex: i + 2,
      });
    }
    // console.log(series,'+++++++++++');
  }

//------------------------------------------------------------------------------------------------------------------

//echarts 中 option设置
let option: EChartsOption;
    

    option = {
      title: {
        text: titleinfo,
      },

      tooltip: {
        trigger: 'item',
        axisPointer: {
          type: 'shadow',
        },
      },
      grid: grid,
      legend: {
        data: yName,
      },
      xAxis: [
        {
          type: 'category',
          data: xAxisData,
          position: 'bottom',
          gridIndex: 0,
          nameTextStyle: {
            fontSize: 8,
          },
          axisTick: {
            length: 30,
            interval: 0,
          },
         
          axisLabel: { show: false },
        },
        {
          type: 'category',
          gridIndex: 0,
          data: xAxisData,
          axisLine: { show: false },
          axisLabel: { show: false },
          axisTick: { show: false },
        },

        // 下面都是多的x轴

        ...xAxisExtends,
      ],

      yAxis: [
        {
          type: 'value',
          name: '折线图数据',
          gridIndex: 0,
          min: 'dataMin',
          axisLabel: {
            formatter: function (value: number) {
              // console.log(value,'y轴的值');
              // console.log(value.toExponential(2),'处理后的y轴的值');

              return value.toExponential(1);
            },
          },
        },
        {
          type: 'value',
          name: '柱形图数据',
          gridIndex: 0,
          position: 'right',
          max: 'dataMax',
          min: 'dataMin',
          axisLabel: {
            formatter: function (value: number) {
              // console.log(value,'y轴的值');
              // console.log(value.toExponential(2),'处理后的y轴的值');

              return value.toExponential(1);
            },
          },
        },

        // 下面都是多的y轴
        ...yAxisExtends,
      ],

      series: [...series, ...seriesextends],

      toolbox: [
        {
          feature: {
            dataZoom: {
              show: true,
              // type: ['zoom'],
              yAxisIndex: false,
              xAxisIndex: 0,
            },
          },
          // right:-25
        },
      ],
    };
//---------------------------------------------------------------------------------------------------------------------------------------------------------

//缩放的监听,控制多层x轴的变化
 myChart.on('datazoom', (params: any) => {
      // console.log(xAxisData, 'on监听里面的xAxisData----------');
      let slicestart: number = 0;
      let sliceend: number = 0;
      //回退到最初始的状态时
      if (params.batch[0].start === 0) {
        slicestart = 0;
        sliceend = details.length - 1;
      }
      // 有startValue值的时候(常规缩放)
      else if (params.batch[0].startValue) {
        slicestart = params.batch[0].startValue;
        sliceend = params.batch[0].endValue;
      }

      setdatazoom(slicestart, sliceend);
    });
function setdatazoom(slicestart: number, sliceend: number) {
      
      const slicelength: number = sliceend - slicestart + 1;
      // 缩放之后的x轴的更新的data
      const sliceXAXisData = xAxisData.slice(slicestart, sliceend + 1);
      // console.log(
      //   sliceXAXisData,
      //   'sliceXAXisData+++++缩放时候的x轴的data数据+',
      // );
     

      
      
      // 存放所有层维度数据的数组
      const slicexAxisArray: any = [];

      // 先按维度的层次拼接x的名字
      const slicetempxaxisname = [];
      for (let i: number = slicestart; i < sliceend + 1; i++) {
        const tempxaxislevelname: string[] = [];
        tempxaxislevelname.push(details[i].xaxisValue[0]);
        for (let j: number = 1; j < xlevel; j++) {
          tempxaxislevelname.push(
            tempxaxislevelname[j - 1] + ',' + details[i].xaxisValue[j],
          );
        }
        slicetempxaxisname.push(tempxaxislevelname);
      }

      // console.log(
      //   slicetempxaxisname,
      //   '缩放后按层次拼接好的x的名字---------------',
      // );

      // 分维度取出x轴的名字数组
      for (let i: number = 0; i < xlevel; i++) {
        let tempxxaisvalue: KeyValue = {};
        // 该层循环确定一个维度上的名称和所占的单元格的长度
        for (let j: number = 0; j < slicetempxaxisname.length; j++) {
          if (
            Object.keys(tempxxaisvalue)[
              Object.keys(tempxxaisvalue).length - 1
            ] === slicetempxaxisname[j][i]
          ) {
            //如果和最后一位重复,则合并单元格,长度+1

            // console.log("重复,需要合并");
            const lastkey =
              Object.keys(tempxxaisvalue)[
                Object.keys(tempxxaisvalue).length - 1
              ];
            // console.log(lastkey);
            tempxxaisvalue[lastkey]++;
          } else {
            // console.log("不重复,不需要合并");

            tempxxaisvalue = Object.assign({}, tempxxaisvalue, {
              [slicetempxaxisname[j][i]]: 1,
            });
          }
        }
        slicexAxisArray.push(tempxxaisvalue);
      }
      //    外层循环走完所有的维度都已经拼成了一个对象数组,对象里面分别包裹着每一层维度的名称和对应的长度,一个对象就是一个维度

      // console.log(
      //   slicexAxisArray,
      //   '缩放时要给多层x轴进行渲染的xAxisArray对象数据----------------',
      // );

      slicexAxisArray.reverse();

      const sliceseriesextends = [];

      for (let i: number = 0; i < xlevel; i++) {
        // 当前该维度的名字字符串和对应的所占单元格长度的字符串
        const slicetempsingleXAxisName: string[] = Object.keys(
          slicexAxisArray[i],
        );
        const slicetempsingleXAxisLength: number[] = Object.values(
          slicexAxisArray[i],
        );
        // console.log(slicetempsingleXAxisName,'datazoomtempsingleXAxisName..................');
        // console.log(slicetempsingleXAxisLength,'datazoomtempsingleXAxisLength..................');

        // 依次填入该维度的所有出现的名称与匹配的所占单元格的长度
        for (
          let j: number = 0;
          j < Object.keys(slicexAxisArray[i]).length;
          j++
        ) {
          // 设置填充进bar中的name 的内容和样式,当长度大于当前bar的宽度时,以省略号显示
          let slicetempXAxisname: String = '';
          //slicetempsingleXAxisName[j].substring(slicetempsingleXAxisName[j].lastIndexOf(',')+1)为要展示的全部的字符串
          if (
            slicetempsingleXAxisName[j].substring(
              slicetempsingleXAxisName[j].lastIndexOf(',') + 1,
            ).length >
            (slicetempsingleXAxisLength[j] / slicelength) * 100 - 1
          ) {
            slicetempXAxisname =
              slicetempsingleXAxisName[j]
                .substring(slicetempsingleXAxisName[j].lastIndexOf(',') + 1)
                .substring(
                  0,
                  Math.floor(
                    (slicetempsingleXAxisLength[j] / slicelength) * 100,
                  ),
                ) + '..';
          } else
            slicetempXAxisname = slicetempsingleXAxisName[j].substring(
              slicetempsingleXAxisName[j].lastIndexOf(',') + 1,
            );

          sliceseriesextends.push({
            type: 'bar',
            barWidth: (slicetempsingleXAxisLength[j] / slicelength) * 100 + '%',
            data: [{ name: slicetempXAxisname, value: 1 }],
            barGap: 0,
            label: {
              show: true,
              position: 'inside',
              formatter: '{b}',
              // offset: [0, 10],
            },
            itemStyle: {
              color: 'none',
              borderColor: '#000',
              borderWidth: 1,
            },
            animation: false,
            tooltip: {
              formatter: slicetempsingleXAxisName[j].substring(
                slicetempsingleXAxisName[j].lastIndexOf(',') + 1,
              ),
            },
            // barWidth:
            xAxisIndex: i + 2,
            yAxisIndex: i + 2,
          });
        }
      }
      // console.log(sliceseriesextends, 'sliceseriesextends+++++++++++');

      

      myChart.setOption(
        {
          series: [...series, ...sliceseriesextends],
          yAxis: [
            {
              type: 'value',
              name: '折线图数据',
              gridIndex: 0,
              min: 'dataMin',
              max: 'dataMax',
              axisLabel: {
                formatter: function (value: number) {
                  // console.log(value,'y轴的值');
                  // console.log(value.toExponential(2),'处理后的y轴的值');

                  return value.toExponential(1);
                },
              },
            },
            {
              type: 'value',
              name: '柱形图数据',
              gridIndex: 0,
              position: 'right',
              max: 'dataMax',
              min: 'dataMin',
              axisLabel: {
                formatter: function (value: number) {
                  // console.log(value,'y轴的值');
                  // console.log(value.toExponential(2),'处理后的y轴的值');

                  return value.toExponential(1);
                },
              },
            },

            // 下面都是多的y轴
            ...yAxisExtends,
          ],

          
        },
        {
          replaceMerge: ['series', 'yAxis'], // 替换合并series,默认普通合并
        },
      );
    }

//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//原始数据details的格式
 details: [
        {
          yaxisBar: [
            {
              yfield: 'firstPassCPU',
              yvalue: '1E-14',
            },
            {
              yfield: 'firstPassCPK',
              yvalue: '-1E-14',
            },
          ],
          yaxisLine: [
            {
              yfield: 'firstPassCP',
              yvalue: '0',
            },
          ],
          xaxisValue: [
            '20211023',
            'AGD142M208-D001',
            '1000:GSM824_Idle_Current',
          ],
        },
        {
          yaxisBar: [
            {
              yfield: 'firstPassCPU',
              yvalue: '8E-15',
            },
            {
              yfield: 'firstPassCPK',
              yvalue: '-8E-15',
            },
          ],
          yaxisLine: [
            {
              yfield: 'firstPassCP',
              yvalue: '0',
            },
          ],
          xaxisValue: [
            '20211023',
            'AGD142M208-D001',
            '1001:GSM824_Max_Pout_0dBm_Drive',
          ],
        },
}

2 声望2 粉丝