漏斗图的矩形显示(熊大的神奇效果)

描述:当前是关于Echarts图表中的 漏斗图 示例。
 
            let data = [
   { value: 0.5, name: 'Visit' },
   { value: 0.6, name: 'Visit1' },
   { value: 0.8, name: 'Visit2' },
   { value: 1, name: 'Visit3' },
   { value: 1.2, name: 'Visit4' },
]
// 设置颜色-因为用到事件动态setoption了自带颜色不保熟
let colors = [
   {
      type: 'linear',
      x: 0,
      y: 0,
      x2: 1,
      y2: 1,
      colorStops: [{
         offset: 0, color: '#ffa39e' // 0% 处的颜色
      }, {
         offset: 1, color: '#f5222d' // 100% 处的颜色
      }],
      global: false // 缺省为 false
   },
   {
      type: 'linear',
      x: 0,
      y: 0,
      x2: 1,
      y2: 1,
      colorStops: [{
         offset: 0, color: '#91d5ff' // 0% 处的颜色
      }, {
         offset: 1, color: '#1890ff' // 100% 处的颜色
      }],
      global: false // 缺省为 false
   },
   {
      type: 'linear',
      x: 0,
      y: 0,
      x2: 1,
      y2: 1,
      colorStops: [{
         offset: 0, color: '#ffd591' // 0% 处的颜色
      }, {
         offset: 1, color: '#fa8c16' // 100% 处的颜色
      }],
      global: false // 缺省为 false
   },
   {
      type: 'linear',
      x: 0,
      y: 0,
      x2: 1,
      y2: 1,
      colorStops: [{
         offset: 0, color: '#87e8de' // 0% 处的颜色
      }, {
         offset: 1, color: '#13c2c2' // 100% 处的颜色
      }],
      global: false // 缺省为 false
   },
   {
      type: 'linear',
      x: 0,
      y: 0,
      x2: 1,
      y2: 1,
      colorStops: [{
         offset: 0, color: '#0128b460' // 0% 处的颜色
      }, {
         offset: 1, color: '#0128b4' // 100% 处的颜色
      }],
      global: false // 缺省为 false
   }
]
data.map((v, k) => {
   v['itemStyle'] = { color: colors[k] }
})
let legendData = data.map((v) => v.name);

// 是否平均分配宽度
let isAverageWidth_ = false;

// 最大 100(表示为百分比) 可以在37行吧 '%' 去掉设置数值
let maxBarHeight = 30;

/**
 * 函数描述
 * @param {array} fnData 数据
 * @param {boolean} isAverageWidth 是否固定宽度(固定的宽度根据100/fnData长度)
 * @param {number=} maxBarHeight 柱子的最大高度
 * @return {array} 处理后的series
 */
const computedData = (fnData, isAverageWidth, maxBarHeight) => {
   let newData = [];
   let count = fnData.map((v) => v.value).reduce((a, b) => {
      return a + b;
   }, 0);
   let averageWidth = 100 / fnData.length;
   fnData.forEach((item, index) => {
      let proportion = item.value / count;
      let size = maxBarHeight * proportion + '%';
      let barWidth = averageWidth + '%';
      let leftOffset = 0;


      // 这里会有图例未选中状态的bug 事件部分有解释
      // 这里就可以根据item.itemStyle.opacity == 0 判断隐藏的
      // 如果隐藏的就不参与 width的运算和offset的运算
      if (isAverageWidth) {
         barWidth = averageWidth + '%';
         leftOffset = averageWidth * index + '%';
      } else {
         let barWidthNum = 100 * proportion
         barWidth = barWidthNum + '%'
         for (let i = 0; i < index; i++) {
            let proportion_ = fnData[i].value / count;
            leftOffset += 100 * proportion_
         }
         leftOffset += '%';
      }


      let seriesItem = {
         name: item.name,
         type: 'funnel',
         orient: 'horizontal',
         width: barWidth,
         left: leftOffset,
         minSize: size,
         maxSize: size,
         top: "-20%", //上下位置
         sort: 'descending',
         gap: 2,
         label: {
            show: true,
            position: 'inside',
            shadowBlur:0,
            color:"#fff",
            fontSize:14,
            backgroundColor:'rgba(255,255,255,0)'
         },
         emphasis:{
            label:{
               fontSize:14
            }
         },
         labelLine: {
            length: 10,
            lineStyle: {
               width: 1,
               type: 'solid'
            }
         },
         itemStyle: {
            borderColor: '#fff',
            borderWidth: 0,
            opacity: item.value == 0 ? 0 : 1
         },
         emphasis: {
            label: {
               fontSize: 14
            }
         },
         data: [
            item
         ]
      }

      newData.push(seriesItem)
   })
   return newData;
}
let resData = computedData(data, isAverageWidth_, maxBarHeight);
// 可以查看series 的格式自行修改这里方便设置图例所加
console.log(resData);
option = {
   tooltip: {
      trigger: 'item',
      formatter: '{a} <br/>{b} : {c}'
   },
   legend: {
      data: legendData,
      top: "10%"
   },
   grid: {
      containLabel: true,
      left: 0,
      bottom: 0,
      right: 0,
      top: 0
   },
   color:colors, //把需要设置的颜色帮到全局 这里用于设置图例颜色
   series: resData
};

myChart.on('legendselectchanged', function (lparam) {
   let selects = lparam.selected;
   let newDataFilter = [];
   data.filter((item) => {
      if (selects[item.name]) {
         newDataFilter.push(item);
      } else {
         // 这里会存在一个问题 value为0的时候图例 未选中状态的icon无法显示只显示text;
         // 可以通过在computedData 方法内和clone的独特属性判断 设置width为0 offsetLeft同样需要判断处理
         // 例如设置 itemStyle.opacity = 0; 
         // 这里没加提供思路
         let clone = {
            ...item,
            value: 0.00000000000000000000000000000000000000000000000001  //解决问题可以采用这个但是感觉有问题
            // itemStyle: {
            //   opacity: 0
            // }
         }
         newDataFilter.push(clone)
      }
   })
   let resData = computedData(newDataFilter, isAverageWidth_, maxBarHeight);
   myChart.setOption({
      series: resData
   })
})