侧边栏壁纸
博主头像
M酷博主等级

一帆风顺 ⛵️⛵️⛵️

  • 累计撰写 42 篇文章
  • 累计创建 37 个标签
  • 累计收到 351 条评论

目 录CONTENT

文章目录

Echarts常见问题及解决方法

M酷
2021-06-29 / 1 评论 / 10 点赞 / 3,142 阅读 / 11,841 字 / 正在检测是否收录...

前言

❇ 本篇文章主要整理了在日常使用 Echarts 图表库的过程中遇到的一些问题及常用技巧,希望对大家有所帮助,相关内容会持续更新。

以下接正文 🎉

1、按需引入 Echarts

众所周知,Echarts 的包体积很大,项目中往往只用到部分功能,如果不按需引入,打包后体积将很大。我们可以用下面的方法实现按需引入。
网上有如下方法,但最终发现无效,还是全量引入的。

// 引入基本模板
let Echarts = require("echarts/lib/echarts");
// 按需引入需要的组件模块
require("echarts/lib/chart/line");
require("echarts/lib/component/title");
require("echarts/lib/component/legend");
require("echarts/lib/component/tooltip");

需要用以下方法:

1、安装 babel-plugin-equire

npm i babel-plugin-equire -D

2、在项目的 .babelrc 文件中添加该插件。

{
  "plugins": [
    // other plugins
    "equire"
  ]
}

3、新建一个文件 echarts.js。

const echarts = equire([
  'bar',
  'line',
  'grid',
  'legend',
  'tooltip',
  'title'
  ])
  export default echarts

4、组件中直接引入使用即可。(4.8.0之后的版本,控制台可能会提示 "import echarts from 'echarts/lib/echarts'" is not supported anymore. Use "import * as echarts from 'echarts/lib/echarts'" instead;),不用理会,生产环境是不会有这个错误提示的,如果介意,降级到4.8.0即可)

import echarts from '@/assets/echarts'

2、动态更新 Echarts 配置

// 初始创建
var myChart = echarts.init(document.getElementById('main'));
var option = {
    ........
}

myChart.setOption(option);

function refreshData(data) {
    // 刷新数据
    var option = myChart.getOption();
    option.series[0].data = data;
    myChart.setOption(option);
}

refreshData(data); //自定义刷新的时候调用

3、动态更新 Echarts 配置

1、 tooltip有一个confine属性,设置为true就可以

tooltip: {
    trigger: 'axis',
    confine: true, // tooltip限制在图表内
    axisPointer: {
        type: 'shadow',
        crossStyle: {
            color: '#999'
        }
    },
    backgroundColor: 'rgba(255,255,255,0.9)',
    formatter: function (params) {
        if (!params.length) return defaultTpl
        let ret = ''
        params.forEach((itm, idx) => {
            ret +=
                `<p class="line${itm.seriesIndex}">${MapField[itm.seriesIndex]}:<span>${itm.value}</span></p>`
        })
        return `<div class="tooltip-chart">${ret}</div>`
    }
}

2、手动指定位置

  tooltip: {
      position: function (point, params, dom, rect, size) {
          dom.style.transform = "translateZ(0)";
      }
  }

4、多页面情况下,图 表的resize事件污染导致图表消失

一般出现在使用了多标签页面,或者开启了类似keep-alive这种缓存组件情况下,只需要注意根据页面来区分即可。

拿vue项目来说,在开启keep-alive情况下,多个页面同时引入了同一个图表组件,在resize事件里做判断即可,可以配合activated生命周期来同步resize状态,确保图表正常展示,如下:

<chart-trend ref="chartTrend" slot="content" page="/overviewBoard" />
renderChart(config) {
      this.chart && this.chart.dispose()
      const el = document.getElementById(config.id)
      this.chart = echarts.init(el)
      this.chart.setOption(config.option)
      // 监听resize事件
      window.addEventListener('resize', this.resizeFn)
      this.$on('hook:beforeDestroy', () => {
        window.removeEventListener('resize', this.resizeFn)
      })
    },
    resizeFn() {
      // console.log(this.$route.path, this.page)
      // 注意:在使用keep-alive时,处于其他页面时需要屏蔽,只在当前页面执行 resize,不然会导致图表消失
      this.$route.path === this.page && this.chart.resize()
    },
  activated() {
    this.chart && this.chart.resize()// 页面切回来重新计算图表,避免在其他页面resize之后,这边图表还是之前的尺寸
  }

5、X轴文字超长处理、X轴文字点击事件绑定

1、首先对formatter显示的文字长度做个截断展示。

axisLabel: {
    margin: 18,
    rotate: !that.isEmpty ? 45 : 0,
    // width: 60,
    // overflow: 'truncate',
    // ellipsis: '...',
    formatter: function (value) {
        return (value.length > 5 ? (value.slice(0, 5) + '...') : value)
    },
    color: '#666'
}

2、然后监听图表的mouseover事件,在鼠标悬浮到文字上时展示完整文字,点击事件同理。

// 扩展echart功能,[X轴文字点击、超长显示...、图表自适应]
extChart(mychart) {
    const that = this
    // 判断是否创建过div框
    const el = document.getElementById('xaxis-hover')
    if (!el) {
        const el = document.createElement('div')
        el.id = 'xaxis-hover'
        document.body.appendChild(el)
    }
    // 监听mouse移入移出事件
    mychart.on('mouseover', function (params) {
        if (params.componentType === 'xAxis' && params.value !== '-') {
            const target = document.querySelector('#xaxis-hover')
            target.textContent = params.value
            target.style.display = 'inline-block'
            document.querySelector('html').onmousemove = function (event) {
                var xx = event.pageX - 30
                var yy = event.pageY + 20
                target.style.left = xx + 'px'
                target.style.top = yy + 'px'
            }
        }
    })
    mychart.on('mouseout', function (params) {
        document.querySelector('#xaxis-hover').style.display = 'none'
    })
    // 监听click事件
    mychart.on('click', function (params) {
        document.querySelector('#xaxis-hover').style.display = 'none'
        if (params.targetType === 'axisLabel' && params.value !== '-') {
            const picker = that.$parent.$parent.$refs.datePicker
            that.$store.commit('app/SET_DATA', {
                key: 'queryParam',
                value: {
                    customerName: params.value,
                    picker: {
                        typeIdx: picker.typeIdx,
                        curYear: picker.curYear,
                        // checkQuarterly: 0,
                        checkMonth: picker.month,
                        checkWeek: picker.week,
                        checkDate: picker.date
                    }
                }
            })
            that.$router.push('/customerSituation')
        }
    })
    // 监听resize事件
    window.addEventListener('resize', this.resizeFn)
    this.$on('hook:beforeDestroy', () => {
        window.removeEventListener('resize', this.resizeFn)
    })
}

6、X轴文字hover变色

监听图表实例的 mouseover 和 mouseout 事件,然后动态的修改对应轴里的data配置(通过打印图表配置,可以发现data里是可以支持对象形式的),y 轴同理。

// X轴文字hover高亮
chartHoverXlabel(value, color, isMouseout) {
        var option = JSON.parse(JSON.stringify(this.chart.getOption()))
        // 深拷贝一份原有配置
        const newItem = {
            value: value
        }
        if (!isMouseout) {
            newItem.textStyle = {
                color
            }
        }
        const index = option.xAxis[0].data.findIndex(item => item === value || item.value === value)
        option.xAxis[0].data.splice(index, 1, newItem)
        this.chart.setOption(option)
    },
    // 扩展echart功能,[X轴文字hover颜色]
    extChart(mychart) {
        const that = this
        // 监听mouse移入移出事件
        mychart.on('mouseover', function (params) {
            if (params.targetType === 'axisLabel' && params.value !== '-') {
                that.chartHoverXlabel(params.value, '#1863FB')
            }
        })
        mychart.on('mouseout', function (params) {
            if (params.targetType === 'axisLabel') {
                that.chartHoverXlabel(params.value, '#666666', true)
            }
        })
    }

7、X轴文字过长时,有的会被隐藏

设置如下 axisLabel 的 interval 属性为 0 即可,注意配合 width 属性。

axisLabel: {
    margin: 18,
    interval: 0,
    width: 40,
    fontSize: 10,
    color: '#666',
    formatter: function (value) {
        return value.length > 5 ? (value.slice(0, 5) + '...') : value
    }
},

相关参考:
Echart X轴文字旋转最全处理方案
Echart 折线图 鼠标悬浮 拐点增加圆圈强调

8、Y轴数值间距等分问题

Eacharts 中Y轴默认的分段为5,但在渲染的时候会根据数值动态计算,有时候不一定是我们想要的,所以需要我们自己去计算,主要配合的属性为 yAxis 下的 min、max、interval 这几个属性。(主要需求中有多个图表并列展示,数据范围相差比较大,为了统一样式必须统一分段)

  1. 首先通过 Y 轴的数值获取最小/最大值(注意负数的情况);
  2. 然后通过分段数计算出Y轴对应的实际最大值边界,这里统一通过函数 getRangeData 处理;
  3. 最后设置 yAxis 中对应的属性即可。

tips:如果有些特殊情况,需要单独处理,比如Y轴为百分比数值的情况。

如下代码:

// 获取数据范围相关数据,[最小值,最大值,间隔值,范围数组]
getRangeData(data, gap = 5) {
    if (data.length) {
        if (this.config.title.includes("时效达成率")) {
            // 时效达成率相关分类为百分比单位,不需要计算,保证最大值为100%即可
            return {
                min: 0,
                max: 100,
                interval: null,
                range: null
            };
        }
        const dataMin = Math.min(...data); // 获取数据中最小值
        const dataMax = Math.max(...data); // 获取数据中最大值
        const min = dataMin > 0 ? 0 : Math.floor(dataMin); // 这里主要是为了处理0.0的问题
        // 有如下情况要处理
        // dataMin = 0 && dataMax = 0
        // dataMin >= 0 && dataMax > 0
        // dataMin < 0 && dataMax < 0
        // dataMin <= 0 && dataMax >= 0
        let interval = null; // 默认间隔
        let range = [0]; // 默认初始值为0
        if (dataMin === 0 && dataMax === 0) {
            return {
                min: 0,
                max: 100,
                interval: null,
                range: null
            };
        }
        if (dataMin >= 0 && dataMax > 0) {
            interval = dataMax >= 10 ? Math.ceil(dataMax / gap) : dataMax / gap; // 最大值小于10情况下允许小数
        }
        if (dataMin <= 0 && dataMax >= 0) {
            range = [min]; // 最小值为负数,则初始值为向下取整后的负数
            const allLen = Math.abs(dataMin) + dataMax;
            // interval = allLen >= 10 ? Math.ceil(allLen / gap) : allLen / gap;
            interval = Math.ceil(allLen / gap);
 // 为了保证范围包含数值,这里使用 Math.ceil 向上取整
        }
        // 根据最大值和分段等距划分数值
        for (let i = 0; i < gap; i++) {
            range.push(range[i] + interval);
        }
        const max = range[range.length - 1];
        return {
            min,
            max,
            interval,
            range
        };
    }
    return {
        min: 0,
        max: null,
        interval: null,
        range: null
    };
}

计算相关数据

const { min: minValue, max: maxValue, interval: curInterval } = this.getRangeData(that.config.data);

设置 yAxis 对应属性

yAxis: {
    // name: that.config.title, // 这里title位置会跟着Y轴数值移动,已放弃
    type: 'value',
    min: minValue, // 自己算的最小值
    max: maxValue, // 自己根据数据算的上限,设为'dataMax'将以y轴最大值为上限
    interval: curInterval, // 自己根据上限和分段数算的间隔值
    splitLine: {
        show: true,
        lineStyle: {
            type: 'dashed'
        }
    }
}

🚀附上完整文件
chart-bar.vue

最终图表效果:
echart-ranking

9、X轴左右留白

有时候需要在X轴两侧留有空白,那么设置 xAxis 的 boundaryGap 属性为 true 即可。

xAxis: {
    type: 'category',
    boundaryGap: true,
    offset: 10,
    axisLine: {
        lineStyle: {
            color: '#ccc'
        }
    },
    axisLabel: {
        rotate: 45,
        fontSize: 12,
        color: '#666'
    },
    data: axisXData
}

如图:
chart-trend

10、多份数据共用Y轴时,动态设置Y轴刻度范围(X轴同理)

  • 【背景】:有时项目中会在同一个图表中使用多个数据,比如多个折线图,并且他们是共用X轴和Y轴的,在这种情况下,如果都是正数是没问题的,切换 legend(图例) 也可以。

  • 【问题】:但是当我们的数据中有一部分包含负数的时候,图表为了考虑其他数据的展示,会把 0 刻度线往上推,导致含负数的部分显示在X轴以上,含0值的没有贴在X轴上,和其他图表对比起来看就有问题。
    chart01

  • 【分析】:经过查阅 api,发现可以给 yAxis 的配置里添加 min 和 max字段, 手动指定 Y轴的刻度范围,于是我们就可以根据数据提前计算好最大、最小值,然后加入配置即可;还有就是顶部的图例是可以切换的,由于我们给 Y轴配置了固定的大小区间,这里需要监听 legendselectchanged 事件,然后根据选中的数据重新计算最大、最小值,并渲染图表。修改完后如下图:
    chart02

  • 【核心代码】

// 获取Y轴最小/最大值
const { min: baseMinValue, max: baseMaxValue } = this.getBaseVal(seriesData);  

// 数值转单位(万)
toWan(data) {
    if (data !== null && data !== "") {
        if (data >= 10000) {
            return (data / 10000).toFixed(1);
        }
        if (Number(data) !== 0) {
            const wanVal = data / 10000;
            const strVal = wanVal.toString();
            let idx = 0;
            if (/\d?\.\d+/.test(strVal)) {
                idx = [...strVal.split(".")[1]].findIndex((c) => c > 0);
            }
            return wanVal.toFixed(idx + 1);
        }
        return "0.0";
    }
    return "-";
}
// @获取Y轴最小/最大值
getBaseVal(data) {
    const min = Math.min(...data.map(item => {
        return Math.min(...item.data)
    }))
    const max = Math.max(...data.map(item => {
        return Math.max(...item.data)
    }))
    return {
        min,
        max
    }
}
 // 监听图例切换事件
 that.chart.on('legendselectchanged', (param) => {
     const selected = param.selected
     const selectedLegend = []
     const selectedData = []
     const options = that.chart.getOption()
     // 1.获取当前选择的数据
     for (const key in selected) {
         if (selected[key] === true) {
             selectedLegend.push(key)
         }
     }
     that.seriesData.forEach(item => {
         selectedLegend.forEach(itm => {
             if (item.name === itm) {
                 selectedData.push(item)
             }
         })
     })
     // 2.更新最大最小值配置
     const {
         min,
         max
     } = that.getBaseVal(selectedData)
     options.yAxis.forEach(item => {
         item.min = min
         item.max = max
     })
     that.chart.setOption(options)
 })
  • 【最后】:为了美观,我去掉了Y轴刻度线,附上完整代码:chart-trend.vue

😄 要是每组数据都用单独的Y轴,那简单一些,直接计算出每组数据的最小值和最小值所在索引,然后放入各自的Y轴配置即可。

// @获取数据组中最小值及索引
findMinData(data) {
    const innerMinArr = data.map(item => {
        return Math.min(...item.data)
    })
    const min = Math.min(...innerMinArr)
    const minIndex = innerMinArr.findIndex(c => c === min)
    return {
        min,
        minIndex
    }
}

完整代码附上:chart-trend.vue

11、图表默认勾选特定的图例(legend)

有时候图表默认进来只需要勾选指定的图例,这个我们可以直接通过配置 legend 字段中的 selected 属性实现这个效果,如图:
legend

// >图例数据
const LegendData = ["降水量", "温度", "湿度"];

// >生成默认勾选的图例
const SelectedLegend = {"降水量": false, "温度": false};

// >配置 legend
legend: {
    data: LegendData,
    selected: SelectedLegend
}

12、Echart 柱状图中正负数值显示不同颜色

有时候,我们需要控制柱状图的条形的颜色,比如正数显示蓝色,负数显示红色。只需要在 series 中把 color 配置为动态的即可。

echart_negative

series: [
   {
       data: seriesData,
       type: "bar",
       barMaxWidth: 30,
       itemStyle: {
           color: fucntion(params) {
           	return params.value >= 0 ? "#1863FB" : "#ef4b4b";
           },
       },
    },
]

13、取消 Echarts 饼图的动画效果

有时候,我们不需要饼图悬浮时的动画效果。只需要在 series 中把 hoverAnimation 配置为动态的即可。

series: [
   {
       data: seriesData,
       type: "bar",
       hoverAnimation: false, // 关闭动画效果
    },
]

14、Echarts 饼图设置默认选中项并在中间显示文字

很多时候,我们需要 Echart 饼图中间显示指定的文字,只需要配置 series 下的的 formatter 即可,但是饼图初始状态下并不会展示中间的文字,需要我们悬浮到相关饼图上才会显示,这时需要我们手动通过 setOption 方法给它指定一个默认值。

echart_pie_text

var myChart = echarts.init(document.getElementById('pie-chart'));
var index = 0;
var option = {
    grid: { //设置图表撑满整个画布
        top: "12px",
        left: "12px",
        right: "16px",
        bottom: "12px",
        containLabel: true
    },
    series: [
        {
            name: '访问来源',
            type: 'pie',
            radius: ['50%', '70%'],
            avoidLabelOverlap: false,
            label: {
                normal: {
                    show: false,
                    position: 'center',
                    formatter: function (data) { // 设置圆饼图中间文字排版
                        return data.value + "\n" + "用户数"
                    }
                },
                emphasis: {
                    show: true, //文字至于中间时,这里需为true
                    textStyle: { //设置文字样式
                        fontSize: '12',
                        color: "#333"
                    }
                }
            },
            labelLine: {
                normal: {
                    show: false
                }
            },
            data: [
                {
                    value: 335,
                    name: '优秀',
                    itemStyle: {
                        color: "#3de16b"
                    }
                },
                {
                    value: 310,
                    name: '良好',
                    itemStyle: {
                        color: "#27baff"
                    }
                },
                {
                    value: 234,
                    name: '一般',
                    itemStyle: {
                        color: "#5865e5"
                    }
                },
                {
                    value: 135,
                    name: '较差',
                    itemStyle: {
                        color: "#fea51a"
                    }
                },
                {
                    value: 1548,
                    name: '糟糕',
                    itemStyle: {
                        color: "#ef5e31"
                    }
                }
          ]
        }
      ]
};
myChart.setOption(option);
//设置默认选中高亮部分
myChart.dispatchAction({
    type: 'highlight',
    seriesIndex: 0,
    dataIndex: 0
});

// 当鼠标移入时,如果不是第一项,则把当前项置为选中,如果是第一项,则设置第一项为当前项
myChart.on('mouseover', function (e) {
    myChart.dispatchAction({
        type: 'downplay',
        seriesIndex: 0,
        dataIndex: 0
    });
    if (e.dataIndex != index) {
        myChart.dispatchAction({
            type: 'downplay',
            seriesIndex: 0,
            dataIndex: index
        });
    }
    if (e.dataIndex == 0) {
        myChart.dispatchAction({
            type: 'highlight',
            seriesIndex: 0,
            dataIndex: e.dataIndex
        });
    }
});

//当鼠标离开时,把当前项置为选中 
myChart.on('mouseout', function (e) {
    index = e.dataIndex;
    myChart.dispatchAction({
        type: 'highlight',
        seriesIndex: 0,
        dataIndex: e.dataIndex
    });
});

15、取消/禁用 Echarts 图例的点击事件

有时候,我们需要禁用图例的点击切换功能。只需要在 legend 中把 selectedMode 配置为 false 的即可。

legend: {
    selectedMode: false, // 是否允许点击
}
10
广告 广告

评论区