swiper中使用echarts

2,057 阅读2分钟

前言:大屏数据可视化开发,有一个模块需要在swiper中使用echarts的饼图,由于是第一次使用这种搭配,所以记录一下踩坑

swiper设置的property不生效

swiper的实例化要放在dom挂载完成之后,首次初始化可以放在生命周期钩子mounted中,如果是放在异步请求中的话请使用nextTick包裹

echarts在slide中不显示

该问题可能由两个原因造成:

  • echarts实例化的时候是通过id绑定的

    swiper在开启loop模式后会在复制第一个slide放到队列的最后,复制最后一个slide放到队列的最前面,形成无缝轮播,如果slide上面有id的话,页面中就会存在两个id相同的dom元素,所以这里建议使用class去绑定dom

  • echarts实例化的的时机

    在swiper的实例化属性init中使用nextTick包裹echarts初始化即可

There is a chart instance already initialized on the dom.

出现这种问题的原因是我们的页面中使用了websocket,后台推送数据后导致数据改变,重新走了echarts的实例化,echarts的源码中对传入的dom对象进行了校验,如果传入的echarts对象的话则不会重新再走实例化的流程且抛出一个warn

我们可以拿到这个dom后使用echarts提供的getInstanceByDom方法判断这个dom是不是一个echarts dom,决定是否init

swiper loop模式下,echarts会丢失第一个slide的复制体的数据

swiper的loop模式开启的时候,后台推动送数据导致echarts重新实例化,然后就会出现第一页echarts不显示的bug,在这里我使用了一个临时解决方案,在收到后台推动的数据后调用swiper.destroy()销毁swiper,再重新实例化swiper,再在实例化echarts的时候判断是不是echarts dom,如果是的话,调用dispose()销毁这个echarts实例,再重新实例化echarts

上面这种操作有个问题,就是每次后台推送数据后,swiper会从跳到第一页重新loop,但是我还没想到更好的解决方案,所以就先这样吧(手动捂脸)

下面是一个demo,可以直接拿到vue项目中引用测试,里面使用了setInterval模拟了websocket推送数据

<template>
  <div id="swiper" class="swiper-container">
    <div id="wrapper" class="swiper-wrapper">
      <div class="swiper-slide" v-for="item in arr" :key="item.id">
        <div :class="`class${item.id}`" style="width: 600px;height:400px;"></div>
      </div>
    </div>
  </div>
</template>

<script>
import Swiper from 'swiper'
import 'swiper/css/swiper.min.css'
import * as echarts from 'echarts'
export default {
  name: 'TestCharts',
  data () {
    return {
      swiper: null,
      arr: [],
      id: 0
    }
  },
  watch: {
    arr: {
      handler (val) {
        if (val.length) {
          this.$nextTick(() => {
            this.initSwiper()
          })
        }
      },
      deep: true,
      immediate: true
    }
  },
  created () {
    for (let index = 1; index <= 2; index++) {
      this.id = index
      this.arr.push({
        id: index
      })
    }
  },
  mounted () {
    const timerId = setInterval(() => {
      if (this.id >= 6) clearInterval(timerId)
      this.id++
      this.arr = []
      this.swiper.destroy(true, true)
      for (let index = 1; index <= this.id; index++) {
        this.arr.push({
          id: index
        })
      }
    }, 30000)
  },
  methods: {
    initSwiper () {
      const self = this
      this.swiper = new Swiper('#swiper', {
        autoplay: true,
        loop: true,
        on: {
          init: function () {
            self.$nextTick(() => {
              for (let index = 1; index <= self.id; index++) {
                self.initChart(index)
              }
            })
          }
        }
      })
    },
    initChart (index) {
      // 指定图表的配置项和数据
      const option = {
        title: {
          text: 'ECharts 入门示例' + index
        },
        tooltip: {},
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20].map(item => item * index)
          }
        ]
      }
      // 使用刚指定的配置项和数据显示图表。
      const divs = document.getElementsByClassName(`class${index}`)
      divs.forEach(div => {
        const myChart = echarts.getInstanceByDom(div)
        if (myChart) myChart.dispose()
        echarts.init(div).setOption(option)
      })
    }
  }
}
</script>

<style lang="less" scoped>
/deep/.swiper-slide {
  width: 600;
  height: 400px;
  background: skyblue;
}
</style>