echart

131 阅读5分钟

ECharts 基本概念

ECharts 基本概念: 系列

系列(series)是指:一组数值映射成对应的图

image.png

DETAILS

image.png

<template>
  <div class="overview">
    <title-two-level title="品牌概况"></title-two-level>
    <div class="container"> 
      <div v-if="chartReady" class="chart-label">
        <div class="num">{{ brandList[highlightIndex].count }}</div>
        <div class="name">{{ brandList[highlightIndex].band_name }}</div>
        <div class="percent">{{ brandList[highlightIndex].percent }}%</div>
      </div>
      <sc-tf-echart
        v-if="chartReady"
        class="chart"
        :option="option"
        @getInstance='getInstance'
      ></sc-tf-echart>
      
      <div class="list">
        <div v-for="(item, index) in brandList" :key="index" class="item" :class="{'selected': highlightIndex === index}">
          <div class="image" :style="{background: color[index] || 'rgba(0, 178, 255, 1)' }"></div>
          <div class="name">{{ item.band_name }}</div>
          <div class="num" :style="{background: `linear-gradient(180deg, #FFFFFF 0%, ${color[index] || 'rgba(0, 178, 255, 1)'} 100%)`,  '-webkit-background-clip': 'text', '-webkit-text-fill-color': 'transparent'}">{{ item.count }}</div>
          <div class="percent"><number-card :number="item.percent" suffix="%" fontSize="0.2rem" suffixSize="0.12rem"></number-card></div>
        </div>
      </div>
    </div>
   
  </div>
</template>
<script>
import TitleTwoLevel from './TitleTwoLevel.vue';
import NumberCard from './NumberCard.vue'
import { getBrandStatistics } from '@/api/requests'
import { mapState } from 'vuex'
import * as echarts from 'echarts'

export default {
  name: 'BrandOverview',
  components: {
    TitleTwoLevel,
    NumberCard
  },
  props: {
  },
  data() {
    return {
      brandList: [],
      chartReady: false,
      color: ['rgba(255, 153, 0, 1)', 'rgba(167, 255, 146, 1)', 'rgba(0, 255, 194, 1)', 'rgba(255, 199, 0, 1)', 'rgba(182, 148, 255, 1)', 'rgba(0, 178, 255, 1)'],
      colorFade: ['rgba(255, 153, 0, 0)', 'rgba(167, 255, 146, 0)', 'rgba(0, 255, 194, 0)', 'rgba(255, 199, 0, 0)', 'rgba(182, 148, 255, 0)', 'rgba(0, 178, 255, 0)'],
      highlightIndex: 0,
      option: {
        series: [
          {
            // 外面装饰的圆圈
            name: 'outerCircle',
            type: 'pie',
            radius: ['98%', '100%'],
            label:{
              normal: {
                show: false
              }
            },
            labelLine: {
              normal: {
                show: false
              }
            },
            emphasis: {
              disable: false,
              scale: false,
              scaleSize: 0
            },
            data: [
              { name: 'outerCircle',
                value: 0,
                itemStyle: {
                  normal: {
                     color: 'rgba(0, 237, 252, 0.5)'
                  }
                }
              }
            ]
          },
          {
            // 里面装饰的圆圈
            name: 'innerCircle',
            type: 'pie',
            radius: ['56%', '58%'],
            label:{
              normal: {
                show: false,
              }
            },
            labelLine: {
              normal: {
                show: false
              }
            },
            emphasis: {
              disable: false,
              scale: false,
              scaleSize: 0
            },
            //emphasis: {
              //label: {
                //show: true,
                //formatter: function(params) {
                  //const { name, percent, value} = params.data
                  //const list = [
                    //`{num|${value || 0}}`,
                    //`{name|${name}}`,
                    //`{percent|${percent || 0}%}`
                  //]
                  //return list.join('\n')
                //},
                //rich: {
                  //num: {
                    //fontFamily: 'D-DIN-PRO',
                    //fontWeight: 700,
                    //fontSize: 36,
                    //lineHeight: 39,
                    //color: 'rgb(0, 178, 255)'
                  //},
                  //name: {
                    //fontFamily: 'Noto Sans SC',
                    //fontWeight: 500,
                    //fontSize: 16,
                    //lineHeight: 23,
                    //color: '#fff'
                  //},
                  //percent: {
                    //fontFamily: 'D-DIN-PRO',
                    //fontWeight: 400,
                    //fontSize: 16,
                    //lineHeight: 17,
                    //color: 'rgb(0, 178, 255)'
                  //}
                //}
              //},
              //itemStyle: {
                //shadowBlur: 100,
                //shadowOffsetX: 0,
                //shadowColor: 'rgba(0, 0, 0, 0.5)'
              //},
            //},
            data: [
              { name: 'innerCircle',
                value: 0,
                itemStyle: {
                  normal: {
                     color: 'rgba(255, 255, 255, 0.2)'
                  }
                }
              }
            ]
          },
          {
            name: 'brandOverview',
            type: 'pie',
            radius: ['60%', '80%'],
            avoidLabelOverlap: false,
            label: {
              show: false,
              position: 'center',
            },
            labelLine: {
              show: false
            },
            data: []
          },
        ]
      },
     
    }
  },
  computed: {
    ...mapState('userInfo', [
      'department',
      'grade'
    ])
  },
  created() {
  },
  watch: {
    department: {
      handler(val) {
        if (Array.isArray(val) && val.length > 0) {
          this.getBrandStatisticsHandler()
        }
      },
      immediate: true
    }
  },
  methods: {
    getBrandStatisticsHandler(){
      getBrandStatistics({
        dept_id: parseInt(this.department[this.department.length - 1]),
        dept_level: this.grade
      }).then(res => {
        this.brandList = res
        this.brandList = [{
          band_name: "中控",
          count: 10,
          percent: "10%"
        }, {
          band_name: "大华",
          count: 10,
          percent: "10%"
        }, {
          band_name: "SCATS",
          count: 30,
          percent: "10%"
        }, {
          band_name: "华路德",
          count: 20,
          percent: "30%"
        }]
        if (Array.isArray(this.brandList)) {
          this.brandList.forEach(item => {
            item.percent = item.percent.split('%')[0]
          })
        }
        this.setData()
      })
    },
    setData() {
      const brandList = this.brandList.map((item, index) => {
        return {
          name: item.band_name,
          value: item.count,
          itemStyle: {
            normal: {
                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                  offset: 0,
                  color: this.color[index]? this.color[index]: this.color[this.color.length - 1]
              }, {
                  offset: 1,
                  color: this.colorFade[index]? this.colorFade[index]: this.colorFade[this.colorFade.length - 1]
              }]),
            }
          }
        }
      })
      this.option.series[2].data = brandList
      this.chartReady = true
     
    },
    getInstance(chart) {
        const that = this
        that.chart = chart
        chart.dispatchAction({
          type: 'highlight',
          seriesIndex: 2,
          dataIndex: 0,
        })
        chart.on('mouseover', {}, params => {
          const { seriesIndex, dataIndex } = params
          if (seriesIndex === 2 && this.highlightIndex !== dataIndex) {
            chart.dispatchAction({
              type: 'downplay',
              seriesIndex: 2,
              dataIndex: this.highlightIndex
            })
            chart.dispatchAction({
              type: 'highlight',
              seriesIndex: 2,
              dataIndex,
            })
            this.highlightIndex = dataIndex
          }
          
        })
        chart.on('mouseout', {}, params => {
          chart.setOption({
            color: that.color
          })
        })
      }
  }
}

ECharts 4.0 新特性:dataset

ECharts 4 开始支持了 数据集(dataset)组件用于单独的数据集声明,从而数据可以单独管理,被多个组件复用,并且可以自由指定数据到视觉的映射。这一特性能将逻辑和数据分离,带来更好的复用,并易于理解。

系列2

ECharts 基本概念: 组件

ECharts 中除了绘图之外其他部分,都可抽象为 「组件」。例如,ECharts 中至少有这些组件:xAxis(直角坐标系 X 轴)、yAxis(直角坐标系 Y 轴)、grid(直角坐标系底板)、angleAxis(极坐标系角度轴)...

组件

ECharts 基本概念:定位

大多数组件都提供了定位属性,我们可以采用类似 CSS absolute 的定位属性来控制组件的位置,下面这个案例可以通过修改 grid 组件定位来控制图表的位置

定位

ECharts 基本概念:坐标系

很多系列,例如 line(折线图)、bar(柱状图)、scatter(散点图)、heatmap(热力图)等等,需要运行在 “坐标系” 上。坐标系用于布局这些图,以及显示数据的刻度等等。例如 ECharts 中至少支持这些坐标系:直角坐标系、极坐标系、地理坐标系(GEO)、单轴坐标系、日历坐标系 等。其他一些系列,例如 pie(饼图)、tree(树图)等等,并不依赖坐标系,能独立存在。还有一些图,例如 graph(关系图)等,既能独立存在,也能布局在坐标系中,依据用户的设定而来。

一个坐标系,可能由多个组件协作而成。我们以最常见的直角坐标系来举例。直角坐标系中,包括有 xAxis(直角坐标系 X 轴)、yAxis(直角坐标系 Y 轴)、grid(直角坐标系底板)三种组件。xAxis、yAxis 被 grid 自动引用并组织起来,共同工作。

案例:散点图

我们来看下图,这是最简单的使用直角坐标系的方式:只声明了 xAxis、yAxis 和一个 scatter(散点图系列),ECharts 会为它们创建 grid 并进行关联:

坐标系

案例:双坐标系

再来看下图,两个 yAxis,共享了一个 xAxis。两个 series,也共享了这个 xAxis,但是分别使用不同的 yAxis,使用 yAxisIndex 来指定它自己使用的是哪个 yAxis:

坐标系

案例:多坐标系

再来看下图,一个 ECharts 实例中,有多个 grid,每个 grid 分别有 xAxis、yAxis,他们使用 xAxisIndex、yAxisIndex、gridIndex 来指定引用关系:

坐标系

柱状图默认选中,这个是showTip不是highlight,

这样鼠标移上去会改变showtip 如果不想变 也可以再写个坐标再写个大柱子用来高亮

tooltip 包括悬浮框也包括 鼠标放上去echart图的柱状背景或着折线图的线的样式

image.png

tooltip 包括悬浮框也包括 鼠标放上去echart图的柱状背景或着折线图的线的样式

tooltip: {
    show: "true",
    trigger: "axis",
    axisPointer: {
      // 坐标轴指示器,坐标轴触发有效
      type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
      shadowStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        // x:为1 从左往右
        // y:为1 从上往下
        // x2:为1 从右往左
        // y2: 为1 从下往上
          {
            offset: 0, // 从上往下0 -> 1
            color: "rgba(255,252,255,0.1)",
          },
          {
            offset: 0.99,
            color: "rgba(255,255,255,0.1)",
          },
          {
            offset: 0.99,
            color: "rgba(255,255,255,1)",
          },
          {
            offset: 1,
            color: "rgba(255,255,255,1)",
          },
        ]),
        width: "auto",
      },
    },
  },
this.flexibleChart.dispatchAction({

            type: 'showTip',

            seriesIndex: 0,

            dataIndex: currentIndex

          });
          
<sc-tf-echart :data="flowData" :option="flowChartOption" @getInstance="getChart"></sc-tf-echart>

组件库中的data:[{
        name: '东-直行',
        value: '100'
      },{
        name: '西-直行',
        value: '20'
      },{
        name: '南-直行',
        value: '50'
      },{
        name: '北-直行',
        value: '80'
      }]
是把 xAxis: {data: ["企业", "农专", "个体"]
和 series: [
    {data: [100, 50, 20]}
    ]
整合到一起

// 白色细线 dot细线的样式和 浮框的样式

axisPointer: {
            label: {
              show: false,
              margin: 10,
              backgroundColor: '#0b1f56',
              color: '#fff',
              fontSize: 14,
              fontFamily: 'Noto Sans SC'
            },
            lineStyle: {
              width: 1
            }
            
          },

image.png

data 横坐标

1、如果xAxis是 category

如果横坐标在在 xAxis中 data要和series中的data一一对应,否则数据会断掉或者渲染不出来

如果横坐标在series中 series的data是二维数组 markArea和markLine中的坐标要在data中 否则渲染不出来

image.png 2、如果xAxis是time,data可以随意

markArea和markLine中的坐标可以不在data中 也能渲染出来

image.png

image.png

在series里写 在饼图中间展示  在series同级写展示在饼图上下左右边上, 如果中间的label样式太丰富不好写 写div

// label: {

            //   show: true,

            //   formatter: '{b}\n{c}个\n{d}%', // 标签显示:名称+数量+百分比

            //   fontSize: 12,

            //   lineHeight: 20

            // },

            label: {

              show: false,

              color: '#7FB4D6',

              position: 'center',

              formatter: function (params) { 

                return  params.value.name+ '\n' + params.value.undefined+ ' \n' + params.percent + '%'

              }

            },