Vue + echarts 环形图组件案例分享

2022-08-12  本文已影响0人  小小Bug你别跑
环形图

今天的下来需求要求封装两个echarts环形图的组件儿,没有设计图自己搞样式和配色
下面我们先看父级页面代码,这里边吧数据和颜色都作为参数穿到组件中,以保持组件的复用率。这里面我查找了三套配色冷暖色作为对比图使用,另外一套默认配色。

<template>
  <div class="as_container">
    <div> 
      <PieAnnular id="ecahrtId" :colorList='coolColorSystem' :dataCon="data"/>
    </div>
    <div> 
      <PieAnnular id="ecahrtIds" :colorList='carmColorSystem' :dataCon="data"/>
    </div>
    <div>
      <Pie id="echartsId" :colorList='colorList' :dataList="data"/>
    </div>
  </div>
</template>

<script>
import PieAnnular from './pieAnnular/pie.vue'
import Pie from './pieAnnular/index.vue'
export default {
  components:{
    PieAnnular,
    Pie
  },
  data(){
    return{
      coolColorSystem:['#61368f','#284797','#116bb1','#15a8aa','#1da563','#79bc52'],//冷色系
      carmColorSystem:['#efea37','#ea4f17','#ec892b','#e71519','#f0a929','#f36d83'],//暖色系
      // 通用色系10种
      colorList:['#46a6ff','#42cece','#59ce7c','#fbd643','#f36d83','#9d69e6','#5d5ed2','#4e5b8f','#ffad6a','#ffadcf'],
      data:[ //模拟数据
          {value:335, name:'直接访问'},
          {value:310, name:'邮件营销'},
          {value:234, name:'联盟广告'},
          {value:135, name:'视频广告'},
          {value:548, name:'搜索引擎'},
          {value:335, name:'直接访问2'}
      ]
    }
  }
}
</script>

<style lang="scss" scoped>
.as_container{
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  height: 100%;
  &>div{
    width: 50%;
    height: 400px;
  }
}
</style>

下面我们来看第一个组件


对比环形图

这个是这个组件两种配色效果,根据需求主要做对比使用,也是由于需求我们这里是用了title来做的中心园内的文字,不关联图、只作为固定数值展示。

<template>
  <div :id="ids" style=" width: 100%;height: 100%;background-color: #19233A;" />
</template>

<script>
export default {
  name: 'Annular',
  props: {
    id: {
      type: String,
      default: () => {
        return ''
      }
    },
    dataCon: {
      type: Array,
      default: () => {
        return []
      }
    },
    colorList: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  data() {
    return {
      ids: this.id,
      myChart: ''
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.drawPie()
    })
  },
  methods: {
    imgBase() {
      this.myChart.getDataURL()
    },
    // 性别比例环形图
    drawPie() {
      const arrName = []
      const arrValue = []
      this.dataCon.forEach((v, i) => {
        arrName.push(v.name)
        arrValue.push(v.value)
      })
      // 基于准备好的dom,初始化echarts实例
      this.myChart = this.$echarts.init(document.getElementById(this.ids))
      // 绘制图表

      var rich = {
        name: {
          color: 'rgba(236, 238, 242, 1)',
          fontSize: 12,
          padding: [10, 10, 0, 10],
          fontWeight: '400'
        },
        hr: {
          borderColor: 'rgba(63, 127, 255, 0.2)',
          width: '100%',
          borderWidth: 1,
          height: 0
        },
        cir: {
          fontSize: 18,
          padding: [10, 10, 0, 10]
        }
      }

      var colorList = this.colorList ? this.colorList : ['#8618F8', '#07BFFE', '#15BAFF', '#23FFEA', '#13FF9F']
      this.myChart.setOption({
        backgroundColor: '#042a4e',
        title: {
        text: '99',  
        x: "34.5%",
        y: "center",
        textAlign:'center',  //x不为'center'时我们需要使用其保持文字相对图的居中
            textStyle: {
                color: "rgb(255,255,255)",
                fontSize: 24,
                width:'30px',
                align: "center"
            },
        },
        tooltip: {
            trigger: 'item',
        },
        legend: {
          orient: 'vertical',
          trigger: 'item',
          show: true,
          top:'center',
          right:'4%',
          icon: 'circle',
          itemWidth: 14,
          textStyle: {
            color: '#ECEEF2',
            fontSize: 14,
            lineHeight: this.dataCon.length > 2 ? 14 : 20,
            rich: {
              percent: {
                color: '#fff',
                fontSize: 14,
                padding: [0, 24, 0, 0]
              }
            }
          },
          formatter: params => {
            return (
              '{title|' + params + '} : {value|' + this.dataCon.find((item) => {
                return item.name == params
              }).value + '%' + '}'
            )
          }
        },
        // -----环形图---------------------------------------
        series: [
          {
            itemStyle: {
              normal: {
                color: function(params) {
                  return colorList[params.dataIndex]
                }
              }
            },
            type: 'pie',
            radius: ['40%', '60%'],
            hoverAnimation: false,
            center: ['35.5%', '50%'],
            label: {
              normal: {
                position: 'inner',
                formatter: params => {
                  return ''
                },
                rich: rich
              }
            },
            data: this.dataCon
          },
          {
            itemStyle: {
              normal: {
                opacity: 0.5,
                color: function(params) {
                  return colorList[params.dataIndex]
                }
              }
            },
            type: 'pie',
            radius: ['30%', '40%'],
            hoverAnimation: false,
            center: ['35.5%', '50%'],
            label: {
              normal: {
                position: 'inner',
                formatter: params => {
                  return ''
                },
                rich: rich
              }
            },
            data: this.dataCon
          },
          // 外层白盘---------------------------------------------------
          {
            itemStyle: {
              normal: {
                color: 'rgba(63, 127, 255, 1)',
                shadowBlur: 6,
                shadowColor: 'rgba(63, 127, 255, 1)',
                shadowOffsetX: 0,
                shadowOffsetY: 0
              }
            },
            type: 'pie',
            silent: true, // 取消高亮
            radius: ['68%', '69%'],
            center: ['35.5%', '50%'],
            hoverAnimation: false, // 取消动画效果
            data: this.dataCon,
            label: {
              normal: {
                show: false,
                position: 'inner',
                formatter: params => {
                  return (
                    '{percent|' + params.percent.toFixed(0) + '%}'
                  )
                },
                rich: rich
              }
            },
            z: -1
          }
        ]
      })
      setTimeout(() => {
        window.onresize = () => {
          this.myChart.resize()
        }
        const resizeObserver = new ResizeObserver(entries => {
          this.myChart.resize()
        })
        resizeObserver.observe(document.getElementById(this.ids))
      }, 800)
    }
  }
}
</script>

下面我们来看第二个组件


通用环形图

这个环形图是通用的环形图组件,不做对比的情况下大部分是使用这个,主要用于数据的展示、不存在太多交互甚至连图例和hover效果也隐藏掉了、然后这个里面我们加了一个暂无数据的判断。

<template>
  <div style="width: 100%;height: 100%;background-color: #19233A;">
    <div v-show="dataList.length > 0" :id="ids" style="background-color: #19233A;height: 100%;" />
    <div v-show="dataList.length == 0" style="background-color: #19233A;position: relative;height: 100%;">
      <img src="@/XXX/XXX/noData.png" alt="" style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:160px;">
    </div>
  </div>
</template>

<script>
export default {
  name: 'Annular',
  props: {
    id: {
      type: String,
      default: () => {
        return ''
      }
    },
    dataList: {
      type: Array,
      default: () => {
        return []
      }
    },
    colorList: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  data() {
    return {
      ids: this.id,
      myChart: ''
    }
  },
  watch: {
    dataList(newValue, oldValue) {
      this.$nextTick(() => {
        this.drawPie()
      })
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.drawPie()
    })
  },
  methods: {
    drawPie() {
      if (this.dataList.length == 0) {
        return
      }
      // 指定图表的配置项和数据
      const chartDom = document.getElementById(this.id)
      // 有的话就获取已有echarts实例的DOM节点。
      this.myChart = this.$echarts.getInstanceByDom(chartDom)
      if (this.myChart == null) { // 如果不存在,就进行初始化
        this.myChart = this.$echarts.init(chartDom)
      }
      // 绘制图表
      var rich = {
        name: {
          color: 'rgba(236, 238, 242, 1)',
          fontSize: 12,
          padding: [10, 10, 0, 10],
          fontWeight: '400'
        },
        hr: {
          borderColor: 'rgba(63, 127, 255, 0.2)',
          width: '100%',
          borderWidth: 1,
          height: 1
        },
        cir: {
          fontSize: 18,
          padding: [0, 10, 0, 10]
        }
      }

      var colorList = this.colorList ? this.colorList : ['#8618F8', '#07BFFE', '#15BAFF', '#23FFEA', '#13FF9F', 'red']
      this.myChart.setOption({
        title: {
        text: '99', 
        x: "center",
        y: "center",
        //与上一个组件对比,当x为'center'时不使用textAlign。也可以保持
            textStyle: {
                color: "rgb(50,197,233)",
                fontSize: 24,
                align: "center"
            }
        },
        backgroundColor: '#042a4e',
        // tooltip: {
        //     trigger: 'item',
        //     formatter: ''
        // },
        series: [{
          name: '',
          type: 'pie',
          center: ['50%', '50%'],
          animation: false,
          radius: this.id != 'annularCon' ? ['38%', '50%'] : ['24%', '36%'],
          clockwise: true,
          avoidLabelOverlap: true,
          silent: true,
          hoverOffset: 0,
          itemStyle: {
            color: function(params) {
              return colorList[params.dataIndex]
            }
          },
          label: {
            show: true,
            position: 'outside',
            formatter: '{a|{b}:{d}%}\n{hr|}',
            rich: {
              hr: {
                borderRadius: 3,
                borderColor: 'rgba(63, 127, 255, 0.5)',
                borderWidth: 1,
                width: 3,
                height: 3,
                padding: [2, 3, 0, -11]
              },
              a: {
                padding: [-50, -70, -40, -80]
              }
            }
          },
          labelLine: {
            smooth: true,
            length: 25,
            length2: 65,
            lineStyle: {
              width: 1,
              color: 'rgba(63, 127, 255, 0.5)'
            }
          },
          data: this.dataList
        }, {
          itemStyle: {
            opacity: 0.5,
            color: function(params) {
              return colorList[params.dataIndex]
            }
          },
          animation: false,
          type: 'pie',
          radius: this.id != 'annularCon' ? ['32%', '44%'] : ['22%', '34%'],
          avoidLabelOverlap: true,
          center: ['50%', '50%'],
          label: {
            position: 'inner',
            formatter: params => {
              return ''
            },
            rich: rich
          },
          data: this.dataList
        },
        // 外层白盘---------------------------------------------------
        {
          itemStyle: {
            color: 'rgba(63, 127, 255, 0.4)',
            shadowBlur: 6,
            shadowColor: 'rgba(63, 127, 255, 0.4)',
            shadowOffsetX: 0,
            shadowOffsetY: 0
          },
          type: 'pie',
          silent: true, // 取消高亮
          radius: this.id != 'annularCon' ? ['55%', '56.5%'] : ['40%', '41.5%'],
          center: ['50%', '50%'],
          data: this.dataList,
          animation: false,
          avoidLabelOverlap: true,
          label: {
            show: false,
            position: 'inner',
            formatter: params => {
              return (
                '{percent|' + params.percent.toFixed(0) + '%}'
              )
            },
            rich: rich
          },
          z: -1
        },
        {
          name: '内圈',
          type: 'pie',
          radius: this.id != 'annularCon' ? ['32%', '28%'] : ['22%', '18%'],
          center: ['50%', '50%'],
          animation: false,
          itemStyle: {
            color: 'rgba(20, 56, 140, 0.3)'
          },
          emphasis: {
            itemStyle: {
              color: 'rgba(20, 56, 140, 0.3)'
            }
          },
          labelLine: {
            show: false
          },
          data: [{
            value: 1
          }],
          zlevel: 1
        }]
      })
      this.$nextTick(() => {
        window.onresize = () => {
          this.myChart.resize()
        }
        const resizeObserver = new ResizeObserver(entries => {
          this.myChart.resize()
        })
        resizeObserver.observe(document.getElementById(this.ids))
      })
    }
  }
}
</script>

这两个组件属于大圈套小圈实现的样式效果,样式上它们存在很多的相同之处,其实他们也可以封装成一个组件,只不过可能会多一些参数的传递,这个大家就按照现实需求来决定吧!

为避免侵权各位亲借鉴的时候注意不要完全照搬谢谢...

上一篇下一篇

猜你喜欢

热点阅读