Echarts implements a funnel chart (use the side length of the trapezoid to reflect the difference between the business volume of a certain link and the previous link)

What is a funnel chart

Funnel chart: As the name suggests, it is an icon like a funnel. The trapezoidal area is used to represent the difference between the business volume of a certain link and the previous link.

Business needs

To implement a funnel chart based on data, it is required to use the side length of the trapezoid to reflect the difference between the business volume of a certain link and the previous link. If the last data is not 0 after filtering, the data must be able to leak (that is, the last box of the funnel chart is not a pointed triangle but a rectangle). If it is 0 after filtering, the data needs to be leaked, that is, the last one is a pointed triangle.

Here is a funnel chart example from the Echarts official website to simulate it.

When the filtered data is 0

option = {
  title: {
    text: 'Funnel'
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} <br/>{b} : {c}%'
  },
  toolbox: {
    feature: {
      dataView: { readOnly: false },
      restore: {},
      saveAsImage: {}
    }
  },
  legend: {
    data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']
  },
  series: [
    {
      name: 'Funnel',
      type: 'funnel',
      left: '10%',
      top: 60,
      bottom: 60,
      width: '80%',
      min: 20,
      max: 100,
      minSize: '20%',
      maxSize: '100%',
      sort: 'descending',
      gap: 2,
      label: {
        show: true,
        position: 'inside'
      },
      labelLine: {
        length: 10,
        lineStyle: {
          width: 1,
          type: 'solid'
        }
      },
      itemStyle: {
        borderColor: '#fff',
        borderWidth: 1
      },
      emphasis: {
        label: {
          fontSize: 20
        }
      },
      data: [
        { value: 60, name: 'Visit' },
        { value: 40, name: 'Inquiry' },
        { value: 20, name: 'Order' },
        { value: 80, name: 'Click' },
        { value: 100, name: 'Show' }
      ]
    }
  ]
};

When the data after filtering is not 0

option = {
  title: {
    text: 'Funnel'
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} <br/>{b} : {c}%'
  },
  toolbox: {
    feature: {
      dataView: { readOnly: false },
      restore: {},
      saveAsImage: {}
    }
  },
  legend: {
    data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']
  },
  series: [
    {
      name: 'Funnel',
      type: 'funnel',
      left: '10%',
      top: 60,
      bottom: 60,
      width: '80%',
      min: 0,
      max: 100,
      minSize: '0%',
      maxSize: '100%',
      sort: 'descending',
      gap: 2,
      label: {
        show: true,
        position: 'inside'
      },
      labelLine: {
        length: 10,
        lineStyle: {
          width: 1,
          type: 'solid'
        }
      },
      itemStyle: {
        borderColor: '#fff',
        borderWidth: 1
      },
      emphasis: {
        label: {
          fontSize: 20
        }
      },
      data: [
        { value: 60, name: 'Visit' },
        { value: 40, name: 'Inquiry' },
        { value: 20, name: 'Order' },
        { value: 80, name: 'Click' },
        { value: 100, name: 'Show' }
      ]
    }
  ]
};

The most important thing here is to operate min, max, minSize, maxSize in the configuration items.

Here min takes the minimum value of the value in the data, max takes the maximum value of the value in the data, and maxSize is always set to ‘100%’.

The above effect can be achieved by setting minSize to min/max%. However, in actual development, you are not only given one array of data for you to judge the filtered data. To achieve such an effect, there are usually two sets of data for comparison. For example:

Reflection of actual code in the project

//Introduce echarts on demand
import * as echarts from 'echarts/core'
import {
  TitleComponent,
  ToolboxComponent,
  TooltipComponent,
  LegendComponent
} from 'echarts/components'
import { FunnelChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers'

echarts.use([
  TitleComponent,
  ToolboxComponent,
  TooltipComponent,
  LegendComponent,
  FunnelChart,
  CanvasRenderer
])


//Configuration item data
data(){
  return {
      option: {
        title: {
          text: 'Data Overview',
          show: false
        },
        tooltip: {
          trigger: 'item',
          formatter: '{b}'
        },
        toolbox: {
          feature: {
            saveAsImage: {}
          }
        },
        legend: {
          data: []
        },
        series: [
          {
            name: '',
            type: 'funnel',
            left: '10%',
            top: 54,
            bottom: 0,
            min: 0,
            max: 0,
            width: '90%',
            height: '100%',
            minSize: '0%',
            maxSize: '100%',//Here, the maximum number is used as 100% to occupy the length
            sort: 'descending',
            gap: 1,
            label: {
              show: true,
              position: 'inside',
              textStyle: {
                color: '#FFFFFF'
              }
            },
            labelLine: {
              length: 10,
              lineStyle: {
                width: 1,
                type: 'solid'
              }
            },
            itemStyle: {
              borderColor: '#fff',
              borderWidth: 1
            },
            emphasis: {
              label: {
                fontSize: 20
              }
            },
            data: []
          }
        ]
      }
  }
}

//method
methods:{
  //Data overview
    async dataView() {
      await this.$axios
        .post(
          'xxx',
          params
        )
        .then((res) => {
          if ( + res?.ret === 0) {
            this.dataViewList = res?.data || []
            this.$nextTick(() => {
              const chartDom = document.getElementById('main')
              this.myChart = echarts.init(chartDom)
              const arr = this.dataViewList?.map((item) => item.arriveCount)
              const arrPassCount = this.dataViewList?.map(
                (item) => item.passCount
              )
              this.option.series[0].data = this.dataViewList?.map((item) => {
                return {
                  name: `${item.dataLifePoint} ${item.arriveCount}`,
                  value: item.arriveCount
                }
              })

              this.option.series[0].min =
                arrPassCount[arrPassCount.length - 1] === 0
                  ? 0
                  : Math.min(...arr)

              this.option.series[0].max = Math.max(...arr)
              this.option.series[0].minSize =
                arrPassCount[arrPassCount.length - 1] === 0
                  ? 0
                  : (Math.min(...arr) * 100) / Math.max(...arr) + '%'
              this.option.series[0].height = this.dataViewList.length * 47.99
              this.funnelHeight = this.dataViewList.length * 47.99 + 54

              this.myChart.setOption(this.option)
            })
          } else {
            this.$Message.error(res.msg)
          }
        })
    },
}