Vue3+ECharts 点击按钮平移数据窗口

106 阅读1分钟

效果如图: 动画.gif

思路

  1. 随机生成120条数据,渲染折线图,引入内置型数据区域缩放组件
  2. 计算数据窗口15个大致对应的平移步长step,该值与dataZoom.start dataZoom.end一样,是百分比的形式
  3. 点击前移按钮触发数据区域缩放行为,数据窗口起点、终点同时减步长,后移同加。端点要进行特殊处理,否则触边再反向移动后,窗口宽度可能会改变。处理如下:前移起点不小于0%,起点为0%时终点为0+step%;后移终点不大于100%,终点为100%时起点为100-step%

代码

Vue函数、生命周期钩子等已通过unplugin-auto-import自动导入

<template>
  <div class="container">
    <button class="btn btn-prev" @click="toPrev">L</button>
    <div ref="chartRef" style="height: 100%"></div>
    <button class="btn btn-next" @click="toNext">R</button>
  </div>
</template>

<script setup>
import * as echarts from 'echarts'

const chartRef = shallowRef()
let chart
const option = {
  grid: { left: 50, right: 50, bottom: '1%', containLabel: true },
  xAxis: {
    type: 'category',
    data: []
  },
  yAxis: { type: 'value' },
  dataZoom: { type: 'inside', start: 0, end: 100 },
  series: [
    {
      data: [],
      type: 'line'
    }
  ],
  animation: false
}

let step = 0
const toPrev = () => {
  if (!chart) return
  const { start, end } = option.dataZoom
  if (start === 0) {
    alert('前无数据')
  } else {
    option.dataZoom.start = Math.max(0, start - step)
    option.dataZoom.end = option.dataZoom.start === 0 ? 0 + step : end - step
    chart.dispatchAction({
      type: 'dataZoom',
      start: option.dataZoom.start,
      end: option.dataZoom.end
    })
  }
}
const toNext = () => {
  if (!chart) return
  const { start, end } = option.dataZoom
  if (end === 100) {
    alert('后无数据')
  } else {
    option.dataZoom.end = Math.min(100, end + step)
    option.dataZoom.start = option.dataZoom.end === 100 ? 100 - step : start + step
    chart.dispatchAction({
      type: 'dataZoom',
      start: option.dataZoom.start,
      end: option.dataZoom.end
    })
  }
}

function renderChart() {
  if (chart) chart.clear()
  option.xAxis.data.length = 0
  option.series[0].data.length = 0
  option.dataZoom.start = 0
  option.dataZoom.end = 100
  /* 重置结束 */
  option.xAxis.data = Array.from({ length: 120 }, (_, i) => `Day${i + 1}`)
  option.series[0].data = Array.from({ length: 120 }, () => Math.random() * 400)
  step = Math.min(100, Math.floor((15 / option.series[0].data.length) * 100)) // 数据窗口15个左右
  option.dataZoom.end = step
  nextTick(() => {
    if (!chart) chart = echarts.init(chartRef.value)
    chart.setOption(option)
  })
}

onBeforeMount(() => {
  renderChart()
})

onBeforeUnmount(() => {
  if (chart) chart.dispose()
})
</script>

<style scoped lang="scss">
.container {
  position: relative;
  height: 400px;
  border: 1px solid #ccc;
}

.btn {
  position: absolute;
  top: 50%;
  z-index: 1;
  transform: translateY(-50%);
  cursor: pointer;
}

.btn-prev {
  left: 8px;
}

.btn-next {
  right: 8px;
}
</style>