实现echart大屏动画效果及全屏布局错乱解决方式

news2024/12/18 13:35:40

如何实现echarts动画效果?如何实现表格或多个垂直布局的柱状图自动滚动效果?如何解决tooltip位置超出屏幕问题,如何解决legend文字过长,布局错乱问题?如何处理饼图的中心图片永远居中?

本文将主要解决以上问题,如有错漏,请指正.

一、大屏动画效果

这里的动画效果主要指,tooltip自动轮播,排名的柱状图或表格自动滚动效果。

1.实现柱状图,折线图自动轮播效果。

以折线图为例,其他的类似。

①封装一个折线图组件

<template>
  <div class="bar-line"
       ref="chartRef"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
  props: {
    dataList: {
      required: false,
      type: Object,
    },
    unit: {
      type: Array,
      required: false,
    },
  },
  watch: {
    dataList(value, oldValue) {
        this.currentOption = value;
        if (this.animotion) {
          this.clearTimeAnimation()
          this.set_Animation()
        } else {
          this.setOption();
        }
      }
    },
  data() {
    return {
      currentChart: null,
      time_Highlight: null,
      currentOption:null,
      //是否tooltip开启动画
      animotion:true,
    }
  },
  methods: {
    isNullorEmpty(str) {
      return (
        str === '' ||
        str == '--' ||
        str === null ||
        str === undefined ||
        isNaN(str)
      )
    },
    initChart(){
  this.init();
  let myChart=echarts.init(this.$refs.chartRef);
  this.currentChart=myChart
  if (this.animotion) {
          this.clearTimeAnimation()
          this.set_Animation()
        } else {
          this.setOption();
        }

},
setOption() {
  let myChart=this.currentChart
  let option=this.currentOption
  myChart.clear();
  option &&myChart.setOption(option);
  window.addEventListener('resize', () => {
    myChart.resize();
    myChart.setOption(option)
  });
  
 
},
 //  清除定时器
 clearTimeAnimation() {
 
        if (this.time_Highlight) {
          clearInterval(this.time_Highlight)
        }
      },

      set_Animation() {
      
        // 获取当前this
        var that = this;
     var option=that.currentOption;
     

        // 鼠标悬浮事件
        that.currentChart.on('mouseover', (params) => {
          // console.log('悬浮')
          // 清除定时器
          that.clearTimeAnimation();

          that.currentChart.dispatchAction({
            type: 'downplay',
            seriesIndex: seriesIndex
          });
          that.currentChart.dispatchAction({
            type: 'highlight',
            seriesIndex: seriesIndex,
            dataIndex: params.dataIndex
          });
           that.currentChart.dispatchAction({
            type: 'showTip',
            seriesIndex: 0,
            dataIndex: params.dataIndex,
          });
          that.time_Highlight = null;

        })

        // 鼠标移出事件
        that.currentChart.on('mouseout', () => {
          // 取消高亮效果
          that.currentChart.dispatchAction({
            type: 'downplay',
            seriesIndex: seriesIndex
          });
          that.clearTimeAnimation();
          setHighlight()
          
        })
        // 当前 X 轴下标
        var myChartIndex = 0;
        // 拿到数组长度
        var dataLen = option.series[0].data.length;
        // 需要显示的系列的下标
        var seriesIndex = option.series.map((item, index) => (index))
        // 是否完成一轮动画
        var isReDataZoom = false;
        // 是否 Y 轴滚动缩放  Y轴缩放 需要增加 orient='vertical' 属性 Y轴0到1是向上的  需要1到0是向下滚动
        var orientZoom =  false;
        if (orientZoom) {
          //Y 最顶端开始
          myChartIndex = dataLen - 1;
        }


        // 设置高亮和提示框
        function setHighlight() {
          option && that.currentChart.setOption(option);
          that.time_Highlight = setInterval(() => {
            // 取消高亮
            that.currentChart.dispatchAction({
              type: 'downplay',
              seriesIndex: seriesIndex,
              dataIndex: myChartIndex
            });
            // 取消提示框
           that.currentChart.dispatchAction({
              type: 'hideTip',
              seriesIndex: 0,
              dataIndex: myChartIndex
            });
            // 是否Y轴
            if (orientZoom) {
              myChartIndex = (myChartIndex - 1) % dataLen;
              if (myChartIndex < 0) {
                myChartIndex = dataLen;
                // 同步
                isReDataZoom = true;
              }
            } else {
              myChartIndex = (myChartIndex + 1) % dataLen;
              if (myChartIndex === 0) {
                // 同步
                isReDataZoom = true;
              }
            }

            // 高亮
            that.currentChart.dispatchAction({
              type: 'highlight',
              seriesIndex: seriesIndex,
              dataIndex: myChartIndex
            });
            // 显示提示框
            that.currentChart.dispatchAction({
              type: 'showTip',
              seriesIndex: 0,
              dataIndex: myChartIndex
            });
          }, 1950)
        }


       setHighlight()
        
      },

    init() {
      let that=this;
      let unit=this.unit;

      let xData = this.dataList.xData
      const seriesList = []
      this.dataList.seriesList.forEach((item, index) => {
        let series = {}
        series = {
          name: item.name,
          type: 'line',
          symbol: 'emptyCircle',
          symbolSize: 4, //标记的大小
          data: item.yData,

          yAxisIndex: item.yIndex === 1 ? item.yIndex : 0,
          connectNulls: item.connectNulls ? item.connectNulls : false,
          color: item.color ? item.color : 'skyblue',
        }
        if (item.type == 'line' && item.isAreaColor) {
          series.lineStyle = {
            width: 2,
          }
          series.areaStyle = {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 0,
              y2: 1,
              colorStops: [
                {
                  offset: 0,
                  color: item.start, // 0% 处的颜色
                },
                {
                  offset: 1,
                  color: item.end, // 100% 处的颜色
                },
              ],
            },
          }
        }
        seriesList.push(series)
      })

      let option = {
        grid: {
          left: '5%',
          right: '5%',
          top: '15%',
          bottom: '12%',
          containLabel: true,
        },
        legend: {
          show: true,
          y: '0%',
          itemWidth: 15,
          itemHeight: 10,
          lineStyle: {
            type: 'solid',
          },
          textStyle: {
            color: '#eee',
            fontSize: '11',
          },
  

        },
        tooltip: {
          trigger: 'axis',
          axisPointer: { type: 'shadow' },
          formatter: function (params) {
            let tip = params[0].name;
            for (let i = 0; i < params.length; i++)
            {
                const unit2 = seriesList[i].yAxisIndex === 1 ? unit[1] : unit[0];
              if( that.isNullorEmpty(params[i].value)){
                tip +=
                '<br/>' + params[i].marker + params[i].seriesName + ':' +'--' + unit2;
              }else{
                tip +=
                '<br/>' + params[i].marker + params[i].seriesName + ':' + Number(params[i].value).toFixed(2)  + unit2;
              }
               
             
              


            }
            return tip;
          },
        },
        xAxis: [
          {
            type: 'category',
            axisLabel: {
              textStyle: {
                color: '#fff',
                fontSize: '12',
              },
              interval: 0,
            },
            axisLine: {
              show: true,
              lineStyle: {
                color: 'rgb(41,111,198)',
              },
            },
            axisTick: {
              show: false,
            },
            data: xData,
          },
        ],
        yAxis: [
          {
            name: unit[0],
            nameTextStyle: {
              fontSize: '12' /* 14/192 */,

              color: '#D9EDFF',
            },
            type: 'value',
            axisTick: {
              show: false,
            },
            splitLine: {
              show: false,
              lineStyle: { type: 'solid', color: 'rgb(41,111,198)' },
            },
            axisLabel: {
              textStyle: {
                color: ' rgb(125, 170, 197)',
              },
              fontSize: '12' /* 14/192 */,
            },
            axisLine: {
              show: true,
              lineStyle: {
                color: 'rgb(41,111,198)',
              },
            },
          },
          {
            nameTextStyle: {
              fontSize: '12' /* 14/192 */,

              color: '#D9EDFF',
            },
            name: unit[1],
            show: this.dataList.showDoubleY,

            type: 'value',
            axisTick: {
              show: false,
            },
            splitLine: {
              show: false,
              lineStyle: { type: 'solid', color: 'rgb(41,111,198)' },
            },
            axisLabel: {
              textStyle: {
                color: ' rgb(125, 170, 197)',
              },
              fontSize: '12' /* 14/192 */,
            },
            axisLine: {
              show: true,
              lineStyle: {
                color: 'rgb(41,111,198)',
              },
            },
          },
        ],
        series: seriesList,
      }

      this.currentOption=option;
    },
  },
  mounted() {
    if (this.dataList.xData) {
      this.initChart();
    }
  },
  beforeDestroy() {
      // 清除定时器
      this.clearTimeAnimation()
      this.currentChart && this.currentChart.dispose()
      this.currentChart = null;
    }
}
</script>
<style scoped>
.bar-line {
  width: 100%;
  height: 100%;
}
</style>

②使用折线图

      <CategoryChart :key="lineChart"
                         :data-list="line"
                         :unit="['%']"
                     />
   lineChart: {
        xData: ['01','02','03','04','05','06'],
        seriesList: [
          {
            type: 'line',
            name: 'series1',
            isAreaColor: true,
            yIndex: 0,
            yData: [],
            color: '#28CCDC',
            start: 'rgba(40,204,220,0.6)',
            end: 'rgba(40,204,220,0.1)',
          },
          {
            type: 'line',
            isAreaColor: true,
            name: 'series2',
            yIndex: 0,
            yData: [],
            color: '#E9D41E',
            start: 'rgba(233,212,30,0.6)',
            end: 'rgba(233,212,30,0.1)',
          },
        ],
        showDoubleY: false,
      },

2.实现饼图tooltip效果

同下方饼图全屏后布局错乱一起讲解。


3.实现自动滚动动画效果

(1)表格数据自动滚动

使用插件vue3-seamless-scroll-无缝滚动或vue-seamless-scroll

参考:

vue3实现无缝滚动列表-vue3-seamless-scroll-CSDN博客

(2)当排名使用多个柱状图实现,

注意:以下方案未实现无缝滚动.

(这里因为使用一个柱状图的多个series无法实现设计图效果,所以使用多个柱状图的单个series实现设计图效果)

vue3-seamless-scroll的原理是再复制一份一样的在原数据下放,而echarts是不可以用相同的id的,所以复制的柱状图不能正确显示.

(1)搭建结构

   <div class="content-box"
             id="scroll-containers">
          <div class="scroll-container">
            <BarSort :key="item.id"
                              :data-list="item.data"
                              :maxArr="maxArr"
                              :id="item.id"
                              v-for="(item, index) in list" />
           
          </div>

        </div>
      .content-box {
        width: 100%;
        position: relative;

        overflow: hidden;
        .scroll-container {
          display: flex;
          flex-direction: column;
        }
      }

(2)分装组件 

<template>
  <div class="bar-line"
       ref="chartRef"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
  props: {
    dataList: {
    required: false,
    type: Array,
  },
  maxArr: {
    required: false,
    type: Array,
  },
  id: {
    required: false,
    type: String,
  }
  },

    watch: {
      dataList: {
        handler: function(newValue,oldValue) {
          this.init();
        }
      }
    },
 
  data() {
    return {
      currentChart: null,
    }
  },
  methods: {
    isNullorEmpty(str) {
      return (
        str === '' ||
        str == '--' ||
        str === null ||
        str === undefined ||
        isNaN(str)
      )
    },
    init() {
      let id=this.id;
      let datas = this.dataList;
  let xData = [this.id + this.dataList[0].name]
  // let maxArr = new Array(datas.length).fill(datas[0].value * 2);
  let maxArr = this.maxArr;
  let valueArr = [];
  for (let i = 0; i < datas.length; i++)
  {
    let obj = {
      value: datas[0].value,
      name: datas[i].value,
    }
    valueArr.push(obj)
  }
  const option = {
    title: {
      text: "",
      show: false
    },
    tooltip: {
      show: true,
      position: [10, -20],
      formatter: function (params) {
        if (params.seriesIndex == 0)
        {
          return params.name + ':' + params.data['value'] + ''
        }

      },
    },
    grid: {
      left: '10%',
      right: '10%',
      bottom: '2%',
      top: '20%',
      // containLabel: true,
    },
    xAxis: {
      show: false
    },
    yAxis: [
      {
        type: 'category',
        inverse: true,
        nameGap: 16,
        axisLine: {
          show: false
        },
        axisTick: {
          show: false
        },
        axisLabel: {
          interval: 0,
          align: 'left',
          margin: 30,
          textStyle: {
            color: '#fff',
            align: 'left',
            fontSize: 10
          },
          rich: {
            a: {
              padding: [0, 0, 20, 10]
            },
            a1: {
              color: '#E93F3F',
              backgroundColor: 'rgba(233, 63, 63, 0.20)',
              shadowColor: '#E93F3F',
              borderColor: '#E93F3F',
              borderWidth: 0.5,
              width: 20,
              height: 20,
              align: 'center',
              borderRadius: 2
            },
            a2: {
              color: '#D7743E',
              backgroundColor: 'rgba(215,116,62,0.2)',
              shadowColor: '#D7743E',
              borderColor: '#D7743E',
              borderWidth: 0.5,
              width: 20,
              height: 20,
              align: 'center',
              borderRadius: 2
            },
            a3: {
              color: '#D7BC3E',
              backgroundColor: 'rgba(215,188,62,0.3)',
              shadowColor: '#D7BC3E',
              borderColor: '#D7BC3E',
              borderWidth: 0.5,
              width: 20,
              height: 20,
              align: 'center',
              borderRadius: 2
            },
            b: {
              color: '#A5C6FF',
              backgroundColor: 'rgba(32, 109, 241, 0.2)',
              shadowColor: '#3374ba',
              borderColor: '#3374ba',
              borderWidth: 0.5,
              width: 20,
              height: 20,
              align: 'center',
              borderRadius: 2
            }
          },
          formatter: function (params) {
            if (parseInt(params.slice(0, 1)) > 2)
            {
              return [
                '{b|' + (parseInt(params.slice(0, 1)) + 1) + '}' + '  ' + '{a|' + params.slice(1) + '}'
              ].join('\n')
            } else
              if (parseInt(params.slice(0, 1)) == 0)
              {
                return [
                  '{a1|' + (parseInt(params.slice(0, 1)) + 1) + '}' + '  ' + '{a|' + params.slice(1) + '}'
                ].join('\n')
              }
            if (parseInt(params.slice(0, 1)) == 1)
            {
              return [
                '{a2|' + (parseInt(params.slice(0, 1)) + 1) + '}' + '  ' + '{a|' + params.slice(1) + '}'
              ].join('\n')
            }
            if (parseInt(params.slice(0, 1)) == 2)
            {
              return [
                '{a3|' + (parseInt(params.slice(0, 1)) + 1) + '}' + '  ' + '{a|' + params.slice(1) + '}'
              ].join('\n')
            }
          }
        },
        data: xData
      },
      // {
      //   type: 'category',
      //   inverse: true,
      //   axisTick: 'none',
      //   axisLine: 'none',
      //   axisLabel: {
      //     show: true,
      //     fontSize: 12,
      //     color: '#fff',
      //     inside: true,
      //     formatter: '{a|{value}}',
      //     rich: {
      //       a: {
      //         padding: [0, 0, 30, 0]
      //       }
      //     }
      //   },
      //   data: datas.map((item) => item.value),
      // },
    ],

    series: [
      {
        name: 'barSer',
        type: 'bar',
        roam: false,
        visualMap: false,
        zlevel: 2,
        barWidth:8,
        barMaxWidth: 8,
        barGap: 0,
        itemStyle: {
          normal: {
            color: function (params) {
              var colorList = [
                {
                  colorStops: [{
                    offset: 0,
                    color: 'rgba(32, 109, 242, 0.4)' // 0% 处的颜色
                  }, {
                    offset: 1,
                    color: 'rgba(32, 109, 242, 0.99)' // 100% 处的颜色
                  }]
                },
                {
                  colorStops: [{
                    offset: 0,
                    color: 'rgba(233, 63, 63, 0.4)' // 0% 处的颜色
                  }, {
                    offset: 1,
                    color: 'rgba(233, 63, 63, 0.99)' // 100% 处的颜色
                  }]
                }, {
                  colorStops: [{
                    offset: 0,
                    color: 'rgba(215, 116, 62, 0.4)' // 0% 处的颜色
                  }, {
                    offset: 1,
                    color: 'rgba(215, 116, 62, 0.99)' // 100% 处的颜色
                  }]
                },
                {
                  colorStops: [{
                    offset: 0,
                    color: 'rgba(215, 189, 94, 0.4)' // 0% 处的颜色
                  }, {
                    offset: 1,
                    color: 'rgba(215, 189, 94, 0.99)' // 100% 处的颜色
                  }]
                }
              ];
              if (id > 2)
              {
                return colorList[0]
              }
              else
              {
                if (id == 0)
                {
                  return colorList[1]
                }
                if (id == 1)
                {
                  return colorList[2]
                }
                if (id == 2)
                {
                  return colorList[3]
                }
              }

            },
            barBorderRadius: 15
          }
        },
        data: datas
      },
      {
        name: '背景',
        type: 'bar',
        barWidth: 8,
        barGap: '-100%',
        itemStyle: {
          color: 'rgba(18, 78, 133, 0.55)',
          barBorderRadius: 15
        },
        tooltip: {
          show: false
        },
        data: maxArr,
      },
      {
        name: '值',
        type: 'bar',
        barWidth: 8,
        barGap: '-100%',
        data: maxArr,
        itemStyle: {
          normal: {
            color: 'transparent',
          },

        },
        label: {
          normal: {
            color: '#fff',
            show: true,
            position: ['94%', '-15px'],
            textStyle: {
              fontSize: 12,
            },
            formatter: function (params) {
            
              return valueArr[0].name;
            },
          },
        },
      },
    ],



  }
      let myChart = echarts.init(this.$refs.chartRef)
      myChart.clear()
      option && myChart.setOption(option)
      window.addEventListener('resize', () => {
        myChart.resize()
        myChart.setOption(option)
      })
    },
  },
  mounted() {
    this.init()
  },
}
</script>
<style scoped>
.bar-line {
  width: calc(100% - 20px);
  height: 100%;
  /* height: 16%; */
  margin: 8px;
  height:40px;
  background: rgb(18 78 133 / 20%);
  border: 1px solid #113E73;
}
</style>

(3)分装滚动函数

    initScroll() {
      let container = document.getElementById('scroll-containers')
      if (this.scrollInterval) {
        container.scrollTop = 0
        clearInterval(this.scrollInterval)
        this.scrollInterval = null
      }
      this.scrollInterval = setInterval(() => {
        container.scrollTop += 2 // 向上滚动的速度
        // 获取指定区域的高度
        const elementHeight = container.clientHeight
        // 获取指定区域内文档内容的总高度
        const contentHeight = container.scrollHeight
        // 获取指定区域的滚动位置
        const scrollPosition = container.scrollTop
        // 计算滚动条距离底部的距离
        const distanceToBottom = contentHeight - elementHeight - scrollPosition
        if (distanceToBottom === 0) {
          container.scrollTop = 0
        }
      }, 50) // 滚动间隔,单位毫秒
    },

(4)给柱状图赋值

     maxArr: [400],
list: [
        {
          id: '0',
          data: [
            {
              name: 'xxx1',
              value: 0,
            },
          ],
        },
        {
          id: '1',
          data: [
            {
              name: 'xxx2',
              value: 0,
            },
          ],
        },
        {
          id: '2',
          data: [
            {
              name: 'xxx3',
              value: 0,
            },
          ],
        },
        {
          id: '3',
          data: [
            {
              name: 'xxx4',
              value: 0,
            },
          ],
        },
        {
          id: '4',
          data: [
            {
              name: 'xxx5',
              value: 0,
            },
          ],
        },
      ],

 至此自动滚动效果完成.


二、解决全屏下饼图使用的graphic图片布局偏移问题

当设计图的布局是如下图所示,如何保证图片永远在饼图居中位置?

①拆分盒子模型为左右两部分,左右两侧为两个饼图,左侧不显示legend,右侧不显示饼图.

 <div class="content-box">
          <div class="pie-left-box">
            <pieChart1 :key="rightCenterList"
                       :data-list="rightCenterList"
                       :pieParams="pieParams2"
                       :unit="['%']"
                       :changeParams="changeParams2"
                       :legendIndexOver="legendIndexOver2"
                       :legendIndexOut="legendIndexOut2"
                       :legendIndexOverFlag="legendIndexOverFlag2"
                       :legendIndexOutFlag="legendIndexOutFlag2" />
          </div>
          <div class="pie-right-box">
            <pieChart2 :key="rightCenterList"
                       :data-list="rightCenterList"
                       @changeLegendSelected="changeLegendSelected2"
                       :unit="['%']"
                       @legendMouseover="legendMouseover2"
                       @legendMouseout="legendMouseout2" />
          </div>
 </div>

②封装饼图组件-左侧饼图

(1)封装autoPlay函数,处理tooltip自动轮播

(2)封装 LineFeedLabel函数,处理legend过长问题

(3)tooltip中position函数,处理tooltip弹框被盒子盖住,或超出屏幕问题

<template>
  <div class="bar-line"
       ref="chartRef"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {

  props: {
      dataList: {
    required: false,
    type: Object,
  },
  unit: {
    required: false,
    type: Array,
  },
  pieParams: {
    required: false,
    type: String,
  },
  changeParams: {
    required: false,
    type: Boolean,
  },
  legendIndexOver: {
    required: false,

  },
  legendIndexOut: {
    required: false,

  },
  legendIndexOverFlag: {
    required: false,
    type: Boolean,
  },
  legendIndexOutFlag: {
    required: false,
    type: Boolean,
  }
  },
  watch: {
    changeParams: {
        handler (newVal, oldVal) {
          if (newVal !== oldVal) {
            this.handleChangeLegendSelected(this.pieParams)
          }
        },
       
      },
      legendIndexOverFlag: {
        handler (newVal, oldVal) {
          if (newVal !== oldVal) {
            this.handleLegendMouseover(this.legendIndexOver)
          }
        },
       
      },
      legendIndexOutFlag: {
        handler (newVal, oldVal) {
          if (newVal !== oldVal) {
            this.handleLegendMouseout(this.legendIndexOut)
          }
        },
       
      },
    },
  data() {
    return {
      myChart: null,
      timer:null,
    }
  },
  methods: {
    handleChangeLegendSelected (params) {
  this.myChart.dispatchAction({
    type: "legendToggleSelect",
    // 图例名称
    name: params,
  });
},
handleLegendMouseover (legendIndex) {
  this.myChart.dispatchAction({
    type: "highlight",
    seriesIndex: 0,// 这个是数据系列,有的一个echarts里面放了多组数据,本案例只有一组所以是0
    dataIndex: legendIndex,// 第几个图例
  });
  this.myChart.dispatchAction({
    type: "showTip",
    seriesIndex: 0,
    dataIndex: legendIndex,
  });
},
handleLegendMouseout (legendIndex) {
  this.myChart.dispatchAction({
    type: "downplay",
    seriesIndex: 0,
    dataIndex: legendIndex,
  });
  this.myChart.dispatchAction({
    type: "hideTip",
    seriesIndex: 0,
    dataIndex: legendIndex,
  });
},
//lengend的文字长度过长,换行
 LineFeedLabel (data, length) {
  //data 要处理的字符串
  //length 每行显示长度
  let word = ''
  let num = Math.ceil(data.length / length) // 向上取整数
  // 一行展示length个
  if (num > 1)
  {
    for (let i = 1; i <= num; i++)
    {
      word += data.substr((i - 1) * length, length)
      if (i < num)
      {
        word += '\n'
      }
    }
  } else
  {
    word += data.substr(0, length)
  }
  return word
},

    isNullorEmpty(str) {
      return (
        str === '' ||
        str == '--' ||
        str === null ||
        str === undefined ||
        isNaN(str)
      )
    },
        // 自动播放函数
        autoPlay() {
  let index = 0;
  let that=this;
  if(this.timer){
    clearInterval(this.timer)
    this.timer=null;
  }
    this.timer = setInterval(function () {

        that.myChart.dispatchAction({
            type: 'hideTip',
            seriesIndex: 0,
            dataIndex: index,
        });
        // 显示提示框
        that.myChart.dispatchAction({
            type: 'showTip',
            seriesIndex: 0,
            dataIndex: index,
        });
        // 取消高亮指定的数据图形
        that.myChart.dispatchAction({
            type: 'downplay',
            seriesIndex: 0,
            dataIndex: index === 0 ? that.dataList.yData.length-1 : index - 1,
        });
        that.myChart.dispatchAction({
            type: 'highlight',
            seriesIndex: 0,
            dataIndex: index,
        });
        index++;
        if (index > that.dataList.yData.length-1 ) {
            index = 0;
        }
    }, 3000);
},
generateRandomColor(item) {
  // 生成三个随机数作为 RGB 值
  var r = Math.floor(Math.random() * 256);
  var g = Math.floor(Math.random() * 256);
  var b = Math.floor(Math.random() * 256);
  // 组装成颜色字符串
  var color = 'rgba(' + r + ',' + g + ',' + b  + ','+item+')';
  return color;
},
    init() {
      let dataList = this.dataList;
      let unit = this.unit;
      let chartData = this.dataList.yData;
      let sum = chartData.reduce((per, cur) => per + cur.value, 0)
      let colors=this.dataList.colorList;
      if(Number(chartData.length)>Number(colors.length)){
        for(let i=colors.length-1;i<chartData.length;i++){
          colors.push(this.generateRandomColor(0.5))
        }
      }
      let seriesList = [{
    name: this.dataList.name,
    type: 'pie',
    radius: this.dataList.radius,
    center: this.dataList.center,
    color: colors,
    hoverAnimation: true,
    label: {
      show: false,
    },
    labelLine: {
      show: false
    },
    data: chartData,
  },
  ];
  let optionsImg = [];
  if (this.dataList.imgArr.length > 0)
  {
    for (let i = 0; i < this.dataList.imgArr.length; i++)
    {
      let item = this.dataList.imgArr[i]
      let bgPatternImg = new Image();
      bgPatternImg.src = item.imgSrc
      let obj = {
        type: 'image',
        // z: 3,
        style: {
          image: bgPatternImg,
          width: item.width,
          height: item.height,
        },
        left: item.left,
        top: item.right,
      }
      optionsImg.push(obj)
    }
  }
 
  let option = {
    title: {
        text:this.dataList.title,
        left:'center',
        subtext:this.dataList.subtext,
        textStyle:{
            color:'#fff',
            fontSize:20,
            padding:[0,0],
            align:'center'
        },
        subtextStyle: {
            fontSize: 13,
            color: 'RGBA(163, 195, 215, 1)',
                   align:'center'
        },
        x: 'center',
        y: '40%',
    },
        grid: { left: '2%', right: '2%', top: '15%', bottom: '5%', containLabel: true },
        legend: {
      show: false
    },
    graphic: {
      elements: optionsImg
    },
    tooltip: {
      trigger: 'item',
      // 下面的函数,用于tip可以完整显示,不用被盒子挡住
      position: function (point, params, dom, rect, size) {
        // 鼠标坐标和提示框位置的参考坐标系是:以外层div的左上角那一点为原点,x轴向右,y轴向下
        // 提示框位置
        var x = 0; // x坐标位置
        var y = 0; // y坐标位置

        // 当前鼠标位置
        var pointX = point[0];
        var pointY = point[1];

        // 外层div大小
        // var viewWidth = size.viewSize[0];
        // var viewHeight = size.viewSize[1];

        // 提示框大小
        var boxWidth = size.contentSize[0];
        var boxHeight = size.contentSize[1];

        // boxWidth > pointX 说明鼠标左边放不下提示框
        if (boxWidth > pointX)
        {
          x = 5;
        } else
        {
          // 左边放的下
          x = pointX - boxWidth;
        }

        // boxHeight > pointY 说明鼠标上边放不下提示框
        if (boxHeight > pointY)
        {
          y = 5;
        } else
        {
          // 上边放得下
          y = pointY - boxHeight;
        }

        return [x, y];
      }, // 设置显示位置
      // axisPointer: { type: 'shadow' },
      formatter (params) {
        let tip = dataList.name;
        const unitShow = seriesList[0].yAxisIndex === 1 ? unit[1] : unit[0]
        tip
          += `<br/>${params.marker}${params.name}:${params.value && params.value != '--' ? Number(params.value).toFixed(2) : params.value}${unitShow}`

        return tip
      },
    },
    
        series: seriesList,
      }

      let myChart2 = echarts.init(this.$refs.chartRef)
      myChart2.clear()
      option && myChart2.setOption(option)
      window.addEventListener('resize', () => {
        myChart2.resize()
        myChart2.setOption(option)
      })
      this.myChart=myChart2
    },
  },
  mounted() {
    
      this.init()
      this.autoPlay()
    
  },
  beforeDestroy() {
      // 清除定时器
      if(this.timer){
    clearInterval(this.timer)
    this.timer=null;
  }
}
}
</script>
<style scoped>
.bar-line {
  width: 100%;
  height: 100%;
}
</style>

③封装饼图组件-右侧饼图

(1)radius为0不显示饼图

<template>
  <div class="lineContent">
    <div class="unit" :style="{ right: `${rightDistance}`, top: `${topDistance}` }">万tce</div>
    <div class="bar-line"
    ref="chartRef"></div>
  </div>

</template>
<script>
import * as echarts from 'echarts'
export default {
  props: {
    dataList: {
      required: false,
      type: Object,
    },
  },

  data() {
    return {
      myChart: null,
      rightDistance: '10%', // 初始左侧距离,可以根据需要设置
      topDistance: '20%', // 初始顶部距离,可以根据需要设置
 
    }
  },
  methods: {
    handleLegendClick(params) {
      this.$emit('changeLegendSelected', params.name)
    },
    handleLegendMouseover(event) {
      // 获取鼠标现在所在的位置
      const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
      // 因为不仅仅是图例可以触发鼠标经过事件,不是图例的没用index,所以要过滤一下
      if (legendDataIndex != undefined && legendDataIndex >= 0) {
        this.$emit('legendMouseover', legendDataIndex)
      }
    },
    handleLegendMouseout(event) {
      // 有的鼠标移出触发的事件没用parent属性所以要加?为可选的
      if (event.topTarget?.parent != undefined) {
        const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
        // 该监听器正在监听一个`echarts 事件`。
        if (legendDataIndex != undefined && legendDataIndex >= 0) {
          this.$emit('legendMouseout', legendDataIndex)
        }
      }
    },
    //lengend的文字长度过长,换行
    LineFeedLabel(data, length) {
      //data 要处理的字符串
      //length 每行显示长度
      let word = ''
      let num = Math.ceil(data.length / length) // 向上取整数
      // 一行展示length个
      if (num > 1) {
        for (let i = 1; i <= num; i++) {
          word += data.substr((i - 1) * length, length)
          if (i < num) {
            word += '\n'
          }
        }
      } else {
        word += data.substr(0, length)
      }
      return word
    },

    isNullorEmpty(str) {
      return (
        str === '' ||
        str == '--' ||
        str === null ||
        str === undefined ||
        isNaN(str)
      )
    },
    generateRandomColor(item) {
  // 生成三个随机数作为 RGB 值
  var r = Math.floor(Math.random() * 256);
  var g = Math.floor(Math.random() * 256);
  var b = Math.floor(Math.random() * 256);
  // 组装成颜色字符串
  var color = 'rgba(' + r + ',' + g + ',' + b  + ','+item+')';
  return color;
},
    init() {
      let chartData = this.dataList.yData
      let sum = chartData.reduce((per, cur) => per + cur.value, 0)
      let colors=this.dataList.colorList;
      if(Number(chartData.length)>Number(colors.length)){
        for(let i=colors.length-1;i<chartData.length;i++){
          colors.push(this.generateRandomColor(0.5))
        }
      }
      let seriesList = [
        {
          name: '',
          type: 'pie',
          radius: '0%',

          center: ['50%', '50%'],
          color:  colors,
          hoverAnimation: true,
          label: {
            show: false,
          },
          labelLine: {
            show: false,
          },
          data: chartData,
        },
      ]
      const option = {
        grid: {
          left: '2%',
          right: '2%',
          top: '15%',
          bottom: '5%',
          containLabel: true,
        },

        legend: {
          type: 'scroll',
          orient: 'vertical',
          align: 'left',
          // left: '63%',
          left: 'center',
          top: 'middle',
          itemHeight: 12,
          itemWidth: 12,
          lineStyle: {
            type: 'solid',
          },
          textStyle: {
            color: '#ffffff',
          },
          textStyle: {
            // 个
            color: '#D8DDE3',
            rich: {
              name: {
                verticalAlign: 'right',
                align: 'left',
                // width: 80,
                width: this.dataList.legendWidth,
                fontSize: 14,
                color: 'rgba(196, 233, 255, 1)',
              },
              number: {
                padding: [0, 0, 0, 8],
                color: '#fff',
                fontWeight: 700,
              },
            },
            borderWidth: 53, // 间距的宽度
          },
          formatter: (name) => {
            const item = chartData.find((i) => {
              return i.name === name
            })
            if (name) {
              name = this.LineFeedLabel(name, this.dataList.legendSlice)
            }
            return '{name|' + name + '}' + '{number|' + item.value + '}'
          },
          pageIconColor: 'rgba(188, 224, 246, 1)',
          pageTextStyle: {
            color: 'rgba(188, 224, 246, 1)',
          },
          pageIconColor: 'rgba(188, 224, 246, 1)',
          pageTextStyle: {
            color: 'rgba(188, 224, 246, 1)',
          },
        },
        tooltip: {
          show: false,
        },
        series: seriesList,
      }

      let myChart2 = echarts.init(this.$refs.chartRef)
      myChart2.clear()

      option && myChart2.setOption(option)
      window.addEventListener('resize', () => {
        myChart2.resize()
        myChart2.setOption(option)
      })
      //图例点击事件
      myChart2.on('legendselectchanged', this.handleLegendClick)
      //图例鼠标移入事件
      myChart2.getZr().on('mouseover', this.handleLegendMouseover)
      //图例鼠标移出事件
      myChart2.getZr().on('mouseout', this.handleLegendMouseout)
      this.myChart = myChart2
    },

  },
  mounted() {
    this.rightDistance=this.dataList?.rightDistance,
    this.topDistance=this.dataList?.topDistance
    this.init()

  },

  beforeUnmount() {
    this.myChart.off('legendselectchanged', this.handleLegendClick)
    this.myChart.getZr().off('mouseover', this.handleLegendMouseover)
    this.myChart.getZr().off('mouseout', this.handleLegendMouseout)
    this.myChart.dispose() // 销毁图表对象
    this.myChart = null // 清空对图表对象的引用
  },
}
</script>
<style scoped>
.lineContent{
  width: 100%;
  height: 100%;
  position: relative;
}
.unit{
  position: absolute;
  right:17%;
  top:15%;
  color:RGBA(163, 195, 215, 1);
  font-size:12px;
}
.bar-line {
  width: 100%;
  height: 100%;
}
</style>

⑤为饼图赋值.

 rightCenterList: {
        yData: [
          {
            value: 0,
            name: 'xx1',
          },
          {
            value: 0,
            name: 'xx2',
          },
          {
            value: 0,
            name: 'xx3',
          },
          {
            value: 0,
            name: 'xx4',
          },
        ],
        name: 'xxxseries名称',
        title: 'xxx主标题',
        subtext: 'xxx副标题',
        radius: ['50%', '70%'],
        center: ['50%', '50%'],
        legendWidth: 120,
        legendSlice: 10,
        imgArr: [
          {
            imgSrc: '/screen/环图背景.png',
            width: 180,
            height: 200,
            left: 'center',
            right: 'center',
          },
        ],
        colorList: [
          'rgba(74, 182, 76, 1)',
          'rgba(40, 174, 232, 1)',
          'rgba(215, 189, 94, 1)',
          'rgba(161, 200, 227, 1)',
        ],
      },

⑥左右饼图进行关联,鼠标点击或划入legend的某项,左侧饼图tooltip要定位到该项

(1)右侧饼图echarts监听事件,并传给父亲

      //图例点击事件
      myChart2.on('legendselectchanged', this.handleLegendClick)
      //图例鼠标移入事件
      myChart2.getZr().on('mouseover', this.handleLegendMouseover)
      //图例鼠标移出事件
      myChart2.getZr().on('mouseout', this.handleLegendMouseout)
 handleLegendClick(params) {
      this.$emit('changeLegendSelected', params.name)
    },
    handleLegendMouseover(event) {
      // 获取鼠标现在所在的位置
      const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
      // 因为不仅仅是图例可以触发鼠标经过事件,不是图例的没用index,所以要过滤一下
      if (legendDataIndex != undefined && legendDataIndex >= 0) {
        this.$emit('legendMouseover', legendDataIndex)
      }
    },
    handleLegendMouseout(event) {
      // 有的鼠标移出触发的事件没用parent属性所以要加?为可选的
      if (event.topTarget?.parent != undefined) {
        const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
        // 该监听器正在监听一个`echarts 事件`。
        if (legendDataIndex != undefined && legendDataIndex >= 0) {
          this.$emit('legendMouseout', legendDataIndex)
        }
      }
    },

(2)父盒子接受子盒子传递的数据,传给左侧饼图

 data() {
    return {
   pieParams: '',
      changeParams: false,
      //饼图 legend与饼图的交互
      legendIndexOver: '',
      legendIndexOut: '',
      legendIndexOverFlag: false,
      legendIndexOutFlag: false,

}
}
 changeLegendSelected(params) {
      this.pieParams = params
      this.changeParams = !this.changeParams
    },
    legendMouseover(legendIndex) {
      this.legendIndexOver = legendIndex
      this.legendIndexOverFlag = !this.legendIndexOverFlag
    },
    legendMouseout(legendIndex) {
      this.legendIndexOut = legendIndex
      this.legendIndexOutFlag = !this.legendIndexOutFlag
    },

(3)左侧饼图监听参数改变触发legend事件

watch: {
    changeParams: {
        handler (newVal, oldVal) {
          if (newVal !== oldVal) {
            this.handleChangeLegendSelected(this.pieParams)
          }
        },
       
      },
      legendIndexOverFlag: {
        handler (newVal, oldVal) {
          if (newVal !== oldVal) {
            this.handleLegendMouseover(this.legendIndexOver)
          }
        },
       
      },
      legendIndexOutFlag: {
        handler (newVal, oldVal) {
          if (newVal !== oldVal) {
            this.handleLegendMouseout(this.legendIndexOut)
          }
        },
       
      },
    },

    handleChangeLegendSelected (params) {
  this.myChart.dispatchAction({
    type: "legendToggleSelect",
    // 图例名称
    name: params,
  });
},
handleLegendMouseover (legendIndex) {
  this.myChart.dispatchAction({
    type: "highlight",
    seriesIndex: 0,// 这个是数据系列,有的一个echarts里面放了多组数据,本案例只有一组所以是0
    dataIndex: legendIndex,// 第几个图例
  });
  this.myChart.dispatchAction({
    type: "showTip",
    seriesIndex: 0,
    dataIndex: legendIndex,
  });
},
handleLegendMouseout (legendIndex) {
  this.myChart.dispatchAction({
    type: "downplay",
    seriesIndex: 0,
    dataIndex: legendIndex,
  });
  this.myChart.dispatchAction({
    type: "hideTip",
    seriesIndex: 0,
    dataIndex: legendIndex,
  });
},

至此完成饼图的graphic全屏布局会错乱问题


参考链接
​​echarts组件封装-动画效果_chart组件封装 动态id-CSDN博客文章浏览阅读794次。echarts组件封装-动画效果。_chart组件封装 动态idhttps://blog.csdn.net/weixin_46253839/article/details/126015918?fromshare=blogdetail&sharetype=blogdetail&sharerId=126015918&sharerefer=PC&sharesource=Primary_Insist&sharefrom=from_link

vue3实现无缝滚动列表-vue3-seamless-scroll-CSDN博客文章浏览阅读2.5k次,点赞20次,收藏19次。vue3实现无缝滚动列表-vue3-seamless-scroll_vue3-seamless-scrollhttps://blog.csdn.net/weixin_48594833/article/details/139901578?fromshare=blogdetail&sharetype=blogdetail&sharerId=139901578&sharerefer=PC&sharesource=Primary_Insist&sharefrom=from_link

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytest入门九:feature

fixture是pytest特有的功能&#xff0c;用以在测试执行前和执行后进行必要的准备和清理工作。使用pytest.fixture标识&#xff0c;定义在函数前面。在你编写测试函数的时候&#xff0c;你可以将此函数名称做为传入参数&#xff0c;pytest将会以依赖注入方式&#xff0c;将该函数…

C# 中的闭包

文章目录 前言一、闭包的基本概念二、匿名函数中的闭包1、定义和使用匿名函数2、匿名函数捕获外部变量3、闭包的生命周期 三、Lambda 表达式中的闭包1、定义和使用 Lambda 表达式2、Lambda 表达式捕获外部变量3、闭包的作用域 四、闭包的应用场景1、事件处理2、异步编程3、迭代…

ChatGPT客户端安装教程(附下载链接)

用惯了各类AI的我们发现每天打开网页还挺不习惯和麻烦&#xff0c;突然发现客户端上架了&#xff0c;懂摸鱼的人都知道这里面的道行有多深&#xff0c;话不多说&#xff0c;开整&#xff01; 以下是ChatGPT客户端的详细安装教程&#xff0c;适用于Windows和Mac系统&#xff1a…

GRE over IPSec 如何应用?如何在ensp上配置GRE over IPSec 实验?

GRE over IPSec应用场景 IPSec VPN本端设备无法感知对端有几个设备 &#xff0c;本端共用一个IPSec SA 。报文封装中没有对端设备的下一跳 &#xff0c;所以无法传输组播、广播和非IP报文 &#xff0c;比如OSPF协议 &#xff0c;导致分支与总部的内部网络之间无法使用OSPF路由…

概率论得学习和整理29: 用EXCEL 描述二项分布

目录 1 关于二项分布的基本内容 2 二项分布的概率 2.1 核心要素 2.2 成功K次的概率&#xff0c;二项分布公式 2.3 期望和方差 2.4 具体试验 2.5 概率质量函数pmf 和cdf 3 二项分布的pmf图的改进 3.1 改进折线图 3.2 如何生成这种竖线图呢 4 不同的二项分布 4.1 p0.…

leetcode 面试经典 150 题:三数之和

链接三数之和题序号11类型数组解题方法排序双指针法难度中等 题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c; 同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三…

【Linux】Nginx一个域名https一个地址配置多个项目【项目实战】

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

【线性代数】理解矩阵乘法的意义(点乘)

刚接触线性代数时&#xff0c;很不理解矩阵乘法的计算规则&#xff0c;为什么规则定义的看起来那么有规律却又莫名其妙&#xff0c;现在参考了一些资料&#xff0c;回过头重新总结下个人对矩阵乘法的理解&#xff08;严格来说是点乘&#xff09;。 理解矩阵和矩阵的乘法&#x…

HTML、CSS表格的斜表头样式设置title 画对角线

我里面有用到layui框架的影响&#xff0c;实际根据你自己的框架来小调下就可以 效果如下 上代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…

29. Three.js案例-自定义平面图形

29. Three.js案例-自定义平面图形 实现效果 知识点 WebGLRenderer WebGLRenderer 是 Three.js 中用于渲染 3D 场景的核心类。它利用 WebGL 技术在浏览器中渲染 3D 图形。 构造器 THREE.WebGLRenderer(parameters : object) 参数类型描述parametersobject可选参数对象&…

一条线上的点

给你一个数组 points &#xff0c;其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 提示&#xff1a; 1 < points.length < 300points[i].length 2-104 < xi, yi < 104points 中的所有点 互不相同 解析&#xff1a;使用斜…

WebRTC服务质量(05)- 重传机制(02) NACK判断丢包

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

八股—Java基础(二)

目录 一. 面向对象 1. 面向对象和面向过程的区别&#xff1f; 2. 面向对象三大特性 3. Java语言是如何实现多态的&#xff1f; 4. 重载&#xff08;Overload&#xff09;和重写&#xff08;Override&#xff09;的区别是什么&#xff1f; 5. 重载的方法能否根据返回值类…

linux ibus rime 中文输入法,快速设置为:默认简体 (****)

本文环境&#xff1a; ubuntu 22.04 直接 apt install ibus-rime 输入法的安全性&#xff0c;人们应该关注吧&#xff01;&#xff01;&#xff1f;&#xff1f; 云输入法&#xff1f;将用户的输入信息传输到云端吗&#xff1f;恐怕很多人的银行账户和密码&#xff0c;早就上…

uniapp使用百度地图配置了key,但是显示Map key not configured

搞了我两天的一个问题。 hbuilderx版本&#xff1a;4.36 问题介绍&#xff1a; 我的项目是公司的项目&#xff0c;需要在H5端使用百度地图&#xff0c;使用vue-cli创建的uniapp&#xff0c;就是uni代码在src里的目录结构。就是使用这种方式才会遇到这个问题。 问题原因&#xf…

ensp 静态路由配置

A公司有广州总部、重庆分部和深圳分部3个办公地点&#xff0c;各分部与总部之间使用路由器互联。广州、重庆、深圳的路由器分别为R1、R2、R3&#xff0c;为路由器配置静态路由&#xff0c;使所有计算机能够互相访问&#xff0c;实训拓扑图如图所示 绘制拓扑图 给pc机配置ip地址…

3分钟读懂数据分析的流程是什么

数据分析是基于商业目的&#xff0c;有目的地进行收集、整理、加工和分析数据&#xff0c;提炼出有价值的 信息的一个过程。整个过程大致可分为五个阶段&#xff0c;具体如下图所示。 1.明确目的和思路 在开展数据分析之前&#xff0c;我们必须要搞清楚几个问题&#xff0c;比…

Python-基于Pygame的小游戏(坦克大战-1.0(世界))(一)

前言:创作背景-《坦克大战》是一款经典的平面射击游戏&#xff0c;最初由日本游戏公司南梦宫于1985年在任天堂FC平台上推出。游戏的主题围绕坦克战斗&#xff0c;玩家的任务是保卫自己的基地&#xff0c;同时摧毁所有敌人的坦克。游戏中有多种地形和敌人类型&#xff0c;玩家可…

认识漏洞-GIT泄露漏洞、APP敏感信息本地存储漏洞

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 [GIT泄露漏洞&#xff0c;你检查了吗&#xff1f;](https://mp.weixin.qq.com/s/I69Jsu8GfX9FJIhMVFe_fA)02 [APP客户端评估- 敏感信息本地存储]( https://mp.weixin.qq.com/s/IrTLZp_lslvGaD4Xhlk…

《Kali 系统中 Docker 镜像加速器安装指南:加速容器镜像拉取》

在 Kali 中配置 Docker 镜像加速器可以显著提高拉取 Docker 镜像的速度&#xff0c;以下是具体步骤&#xff1a; 一、获取镜像加速器地址 国内有许多云服务提供商提供镜像加速器服务&#xff0c;例如阿里云、腾讯云、网易云等。以阿里云为例&#xff0c;你需要先在阿里云容器镜…