[本文出自天外歸雲的博客園]
需求
1. Vue中使用ECharts畫散點圖
2. 在圖中加入加均值線
3. 在圖中標注出陰影區域
實現
實現這個需求,要明確兩點:
1. 知道如何在vue中使用echarts
2. 要知道如何在echarts散點圖中畫均值線和陰影區域
在下面的代碼option對象的series屬性中用到了markLine和markArea,標注最值用到了markPoint。
所以去官方文檔搜索標線、標點、標圖的關鍵字要搜mark。
如何在vue中使用echarts見文末。
需要注意的是vue的渲染時序,不要在頁面沒有渲染完就開始畫圖,那會找不到你定位id的元素。
如何解決找不到元素的問題呢?網上說是在mounted函數中調用nextTick,這種方法可以試試,我是沒成功。所以我自己發明的解法如下:
以下的方法要放到vue文件的watch中,目的是監控showInfo和findAll兩個變量的值的變化,一旦變量值變化則執行調用:
showInfo: function() { // 元素顯示了開始畫線,待優化,可以細分到不用showInfo控制,用每個chart的v-if分別進行控制,因為有時候可能沒有圖表數據 if (this.showInfo === true) { this.findAll = false this.timer = setInterval(() => { this.findElements() }, 1000) } }, findAll: function() { if (this.findAll === true) { console.log('Timer stop.') clearInterval(this.timer) } }
其中用到的變量都要在data函數中聲明賦適當的初始值:
1. this.showInfo控制頁面元素的v-if顯示開關,而showInfo變量的初始值一般為false,在mounted函數中我們可以把它的值設置為true,等頁面加載完后打開顯示開關
2. this.timer是定時器,這里用到setInterval函數做一個定時的查詢,用來定時查找頁面上用來畫echarts的div是否已經出現在頁面,都找到了就停止定時查找
3. this.findAll是一個signal,一旦為true說明所有元素都已找到,立即清空定時器this.timer,不再定時查詢
以下代碼放到vue文件的methods中,是watch變量所用到的一些輔助函數:
findElements() { if (this.findEleById('test_id_1')) { this.if_find_test_img_1_id = true } if (this.findEleById('test_id_2')) { this.if_find_test_img_2_id = true } if (this.findEleById('test_id_3')) { this.if_find_test_img_3_id = true } if (this.if_find_test_img_1_id && this.if_find_test_img_2_id && this.if_find_test_img_3_id) { this.findAll = true } }, findEleById(ele_id) { var ele = document.getElementById(ele_id) if (ele !== null) { console.log('發現id為' + ele_id + '的元素') return true } return false }
ECharts設置相關的核心代碼如下:
<template> <el-row> <el-col :span="24"> <div id="chartDivId" :style="{width: '100%', height: '500px'}"></div> </el-col> </el-row> </template> <script> export default{ data() { return { imgData : { 'columns': ['c1','c2','c3'], 'rows': [ { 'c1': 'v1', 'c2': 'v2', 'c3': 'v3' }, ], 'mean': 2, 'y_top': 3, 'y_bottom': 1 }, methods: { // 畫圖函數,傳入散點圖所在div的id和圖表數據 drawImgChart(chartDivId, imgData) { // 基於准備好的dom,初始化echarts實例 const myChart = this.$echarts.init(document.getElementById(chartDivId))// 繪制圖表 var option = { tooltip: { trigger: 'axis', showDelay: 0, axisPointer: { show: true, type: 'cross', lineStyle: { type: 'dashed', width: 1 } } }, visualMap: { min: 0, max: imgData.mean, // 漸變色最深色對應的y軸坐標 dimension: 1, precision: 3, orient: 'vertical', right: 10, top: 'center', text: ['HIGH', 'LOW'], calculable: true, inRange: { color: ['#f2c31a', '#24b7f2'] } }, xAxis: [ { type: 'category', show: false, scale: true, splitLine: { show: true }, data: imgData.columns, // x軸的數據 axisLabel: { interval: 0, rotate: 70 } } ], yAxis: [ { type: 'value', scale: true, splitLine: { show: true }, axisLabel: { formatter: '{value} s' // y軸數據的格式 xx s } } ], series: [ { type: 'scatter', symbolSize: 5, data: imgData.rows, // 設置最大值點和最小值點 markPoint: { data: [ { type: 'max', name: '最大值' }, { type: 'min', name: '最小值' } ] }, // 設置平均值線 markLine: { lineStyle: { normal: { type: 'solid' } }, data: [ { name: '平均值線', yAxis: imgData.mean // 數值類型,對應y軸坐標 } ] }, // 設置陰影區域 markArea: { silent: true, itemStyle: { normal: { color: '#E8E8E8', borderWidth: 1, borderType: 'dashed' } }, data: [[{ name: '正常值范圍區間', yAxis: imgData.y_top // 陰影區域上邊界 }, { yAxis: imgData.y_bottom // 陰影區域下邊界 }]] } } ] } myChart.setOption(option) } } } } }
談談封裝
有時候封裝的不好不如不封裝,封裝對外的接口如果不是大家需要的功能,反而相當於給被封裝的對象關上了一道門,拒人於封裝之外。
比如eleme和baidu維護的兩版針對vue使用的vchart,我覺得現在的封裝程度就是能用一些echart的基本功能,很多細節都沒有封裝好。
所以,即使vue不推薦直接操作dom元素,但是這次要在散點圖中畫線我還是選擇用原裝的echarts。