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

如何实现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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【YashanDB知识库】如何处理yasql输入交互模式下单行字符总量超过限制4000字节

现象 在yasql执行sql语句后报错&#xff1a;YASQL-00021 input line overflow (>4000 byte at line 4) 原因 yasql在交互模式模式下单行字符总量限制4000字节&#xff0c;超出该限制即报错。 交互式模式下&#xff0c;yasql会显示一个提示符&#xff0c;通常是 SQL>…

DALL·E 2(内含扩散模型介绍)-生成式模型【学习笔记】

视频链接&#xff1a;DALLE 2&#xff08;内含扩散模型介绍&#xff09;【论文精读】_哔哩哔哩_bilibili&#xff08;up主讲的非常好&#xff0c;通俗易懂&#xff0c;值得推荐&#xff09; 目录 1、GAN模型 2、VAE模型 2.1、AE&#xff08;Auto-Encoder&#xff09; 2.2、…

FPGA 16 ,Verilog中的位宽:深入理解与应用

目录 前言 一. 位宽的基本概念 二. 位宽的定义方法 1. 使用向量变量定义位宽 ① 向量类型及位宽指定 ② 位宽范围及位索引含义 ③ 存储数据与字节数据 2. 使用常量参数定义位宽 3. 使用宏定义位宽 4. 使用[:][-:]操作符定义位宽 1. 详细解释 : 操作符 -: 操作符 …

使用 Vue3 实现摄像头拍照功能

参考资料:MediaDevices.getUserMedia() - Web API | MDN 重要: navigator.mediaDevices.getUserMedia 需要在安全的上下文中运行。现代浏览器要求摄像头和麦克风的访问必须通过 HTTPS 或 localhost&#xff08;被视为安全的本地环境&#xff09;进行,如果上传服务器地址是http…

2024安装hexo和next并部署到github和服务器最新教程

碎碎念 本来打算写点算法题上文所说的题目&#xff0c;结果被其他事情吸引了注意力。其实我之前也有过其他博客网站&#xff0c;但因为长期不维护&#xff0c;导致数据丢失其实是我懒得备份。这个博客现在部署在GitHub Pages上&#xff0c;github不倒&#xff0c;网站不灭&…

RTMP推流平台EasyDSS在无人机推流直播安防监控中的创新应用

无人机与低空经济的关系密切&#xff0c;并且正在快速发展。2024年中国低空经济行业市场规模达到5800亿元&#xff0c;其中低空制造产业占整个低空经济产业的88%。预计未来五年复合增速将达到16.03%。 随着科技的飞速发展&#xff0c;公共安防关乎每一个市民的生命财产安全。在…

java全栈day16--Web后端实战(数据库)

一、数据库介绍 二、Mysql安装&#xff08;自行在网上找&#xff0c;教程简单&#xff09; 安装好了进行Mysql连接 连接语法&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroot -p密码 方法二&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroo…

基于注意力的几何感知的深度学习对接模型 GAABind - 评测

GAABind 作者是苏州大学的生物基础与医学院, 期刊是 Briefings in Bioinformatics, 2024, 25(1), 1–14。GAABind 是一个基于注意力的几何感知蛋白-小分子结合模式与亲和力预测模型,可以捕捉小分子和蛋白的几何、拓扑结构特征以及相互作用。使用 PDBBind2020 和 CASF2016 作…

【CSS in Depth 2 精译_080】 13.1:CSS 渐变效果(中)——不同色彩空间的颜色插值算法在 CSS 渐变中的应用

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 13 章 渐变、阴影与混合模式】 ✔️ 13.1 渐变 ✔️ 13.1.1 使用多个颜色节点&#xff08;上&#xff09;13.1.2 颜色插值方法&#xff08;中&#xff09; ✔️13.1…

虚拟机VirtualBox安装最新版本Oracle数据库

https://www.oracle.com/database/technologies/databaseappdev-vm.html 如上所示&#xff0c;从Oracle官方网站上下载最新版本的VirtualBox虚拟机对应的Oracle数据库安装源文件。 如上所示&#xff0c;在VirtualBox中导入下载的Oracle安装源文件。 如上所示&#xff0c;导入…

热更新解决方案4——xLua热补丁

概述 运行时不在执行C#中的代码&#xff0c;而是执行Lua中的代码&#xff0c;相当于是打了个补丁。 1.第一个热补丁 2.多函数替换 3.协程函数替换 在原HotfixMain脚本中只加个协程函数即可&#xff08;和在Start中启动协程函数&#xff09; 4.索引器和属性替换 在HotfixMain中…

突破长链视觉推理瓶颈:Insight-V多智能体架构解析

GitHub 仓库&#xff1a;https://github.com/dongyh20/Insight-V HuggingFace 模型库&#xff1a;https://huggingface.co/THUdyh/Insight-V arXiv 技术论文&#xff1a;https://arxiv.org/pdf/2411.14432 模型&#xff1a;https://huggingface.co/THUdyh/Insight-V-Reason 今天…

IDEA 未启用lombok插件的Bug

项目中maven已引用了lombok依赖&#xff0c;之前运行没有问题的&#xff0c;但有时启动会提示&#xff1a; java: You arent using a compiler supported by lombok, so lombok will not work and has been disabled. Your processor is: com.sun.proxy.$Proxy8 Lombok support…

AI工具如何深刻改变我们的工作与生活

在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经从科幻小说中的概念变成了我们日常生活中不可或缺的一部分。从智能家居到自动驾驶汽车&#xff0c;从医疗诊断到金融服务&#xff0c;AI正以惊人的速度重塑着我们的世界。 一、工作方式的革新…

压力测试Jmeter简介

前提条件&#xff1a;要安装JDK 若不需要了解&#xff0c;请直接定位到左侧目录的安装环节。 1.引言 在现代软件开发中&#xff0c;性能和稳定性是衡量系统质量的重要指标。为了确保应用程序在高负载情况下仍能正常运行&#xff0c;压力测试变得尤为重要。Apache JMeter 是一…

手眼标定工具操作文档

1.手眼标定原理介绍 术语介绍 手眼标定&#xff1a;为了获取相机与机器人坐标系之间得位姿转换关系&#xff0c;需要对相机和机器人坐标系进行标定&#xff0c;该标定过程成为手眼标定&#xff0c;用于存储这一组转换关系的文件称为手眼标定文件。 ETH&#xff1a;即Eye To …

vue 自定义组件image 和 input

本章主要是介绍自定义的组件&#xff1a;WInput&#xff1a;这是一个验证码输入框&#xff0c;自动校验&#xff0c;输入完成回调等&#xff1b;WImage&#xff1a;这是一个图片展示组件&#xff0c;集成了缩放&#xff0c;移动等操作。 目录 一、安装 二、引入组件 三、使用…

CTFHUB-web(SSRF)

内网访问 点击进入环境&#xff0c;输入 http://127.0.0.1/flag.php 伪协议读取文件 /?urlfile:///var/www/html/flag.php 右击查看页面源代码 端口扫描 1.根据题目提示我们知道端口号在8000-9000之间,使用bp抓包并进行爆破 POST请求 点击环境&#xff0c;访问flag.php 查看页…

Mysql 深度分页查询优化

Mysql 分页优化 1. 问题根源 问题&#xff1a; mysql在数据量大的时候&#xff0c;深度分页数据偏移量会增大&#xff0c;导致查询效率越来越低。 问题根源&#xff1a; 当使用 LIMIT 和 OFFSET 进行分页时&#xff0c;MySQL 必须扫描 OFFSET LIMIT 行&#xff0c;然后丢弃前…

[LeetCode-Python版]21. 合并两个有序链表(迭代+递归两种解法)

题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#x…