ECharts
最近2个月没有写东西,因为准备了个考试,周六刚刚解脱,回过头来把这两个月做的东西发一下。主要把用到的一部分Echarts的可视化展示,结合之前用G2做的来把做过的图形化的东西分享一下。(本文取自实际代码结合重点注释)
Echarts
ECharts部分的话大概有4部分:
- ECharts地图使用;
- 柱状图滚动和点击选中事件;
- 平行坐标系使用;
- 外加一个得分条;
地图
首先看一下一个简单的测试数据(地区得分为0会是深蓝色,所以地图有几块深蓝色不好看,数据比较简单)1、首先看一看地图,先说在Echarts中地图的使用,有注释的代码一定很舒服~
<div id="echartMap" style="width: 100%;height:100%"></div> //宽高100%是必要的
methods:{
initMapCharts(data, init) {
const that = this
that.chartMap = echarts.init(document.getElementById('echartMap'))
that.chartMap.clear() //用来清除图像示例,无论是初始化或者是切换项目都会清除map示例,不清楚示例多次setoption你的地图会有问题的
let sum = 0//这是右上角所有地区的一个总分,直接绑定在了html模板上
for (let i of that.regionScore.scores) {
sum += i.score
}
that.regionScore.scores.forEach(e => {
let num = e.score / sum
e.itemStyle = {}
e.itemStyle.areaColor = `rgba(24,${num * 500}, 255,1)`
if (that.isActiveCity == e.regionCode) {
e.selected = true
} else {
e.selected = false
}
})//这个方法很重要,根据不同地区的得分,做一个颜色值的比例得出一个基于得分百分比的蓝色rgba,同事为每个地区对象添加一个selected属性
axios.get('/echarts/chengdu.json').then(function(res) { //这里调用了本地的成都地区json文件用来渲染地图
echarts.registerMap('chengdu', res.data)
that.chartMap.setOption({ //地图配置项
backgroundColor: 'rgba(182,216,255,0)',
selectedMode: 'single', //单击模式
//对移动到地区上显示当前地区名字得分和排名的tooltip使用formatter来return字符串改造
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(4,35,134,0.38)',
borderWidth: 1,
borderColor: '#0e6de9',
showDelay: 0,
hideDelay: 0,
enterable: true,
transitionDuration: 0,
extraCssText: 'z-index:100',
formatter: function(params) {
let res = `<div style="padding: 5px">
<span>${params.name}</span><br/>
<span style="font-size: 10px;line-height: 30px">综合排名 </span>
<span style="color: #00FFED;font-size: 18px;line-height: 30px">NO.${that.regionScore.scores[params.dataIndex].rank}</span><br/>
<span style="font-size: 10px;line-height: 30px">综合得分 </span>
<span style="color: #00FFED;font-size: 18px;line-height: 30px">${that.regionScore.scores[params.dataIndex].score}</span>
</div>`
return res
}
},
geo: {
…………
//省略,配置项比较简单
},
series: [
{
type: 'map',
mapType: 'chengdu',
roam: true,
scaleLimit: {
min: '1'
},
zoom: 1.2,
label: {
normal: {
show: true,
color: '#fff',
fontWeight: '100',
fontSize: '8',
formatter: text => {
if (text.name === '崇州市') {
return ' 崇州市'
}
//崇州市坐标有问题,通过格式化加空行,把文字向右推解决,感觉是json里面的文字坐标不太准
}
},
emphasis: {
show: true
}
},
itemStyle: {
normal: {
areaColor: '#999',
borderColor: '#0a003e',
borderWidth: 0.4,
shadowColor: 'rgba(19,57,108,0.45)'
},
emphasis: {
areaColor: '#05C5FF'
}
},
data: that.regionScore.scores
}
]
})
})
if (init) {//init代表着重置地图,初始化的时候使用,切换项目不会重新执行,为什么呢?
that.chartMap.on('click', function(params) { //因为这里面是给地图设置了监听事件,假设你第二次渲染再执行一次这个对map的监听,你的所有点击事件会触发两遍
// 点击地图事件,其他3部分数据联动
if (that.regionCode == that.regionScore.scores[params.dataIndex].regionCode) {
that.regionCode = that.regionScore.regionCode
that.isActiveCity = null
that.isActiveDepart = null
that.title = ''
} else {
that.regionCode = that.regionScore.scores[params.dataIndex].regionCode
that.isActiveCity = that.regionScore.scores[params.dataIndex].regionCode
that.isActiveDepart = null
let item = that.regionScore.scores.find(
v => v.regionCode == that.regionScore.scores[params.dataIndex].regionCode
)
if (item) {
that.title = item.name
} else {
that.title = ''
}
}
that.componentIndex = null
that.dataIndex = null
that.getRegionIndicatorScore()
that.getDepartScore()
})
}
},
}
复制代码
看完带有注释的代码应该就很清晰明了,然后再加几个小tip:
首先,地图json数据,可以在datav.aliyun.com/tools/atlas…中获取到,这里也可以写一个接口用来调用获取不同区域的json,当然我只用到了成都,所以把成都本地存储了一份。
第二点,地图最后的data,data在setoption到地图期间我对对象处理了一下,先看一格处理完的对象示例:
{
itemStyle: {areaColor: "rgba(24,290.4191616766467, 255,1)"} //单独定义每个地区区块的颜色值
name: "青羊区" //地图比较特殊,其而地图通过name的中文地区名来判断渲染
rank: 1
regionCode: "510104"
score: 97
scores: null
selected: false //这个传入为true会让当前地区默认选中状态,用来做地图下面的城市排名点击选中,地图也切换到对应地区选中状态
}
复制代码
所以在设置selected的时候做了判断 if (that.isActiveCity == e.regionCode),isActiveCity 就是我选中的下方的城市排名里的城市code。
但是这个问题是当你每点击一次下方的城市排名,要想做到上方的地图也选中对应城市就只能刷新一遍地图,地图会闪烁(求教别的更完美的方法……)
然后,因为地图通过name的中文地区名来判断渲染,也就要求后台传过来的地区名字一定要和你的地区中文名统一,我还真碰上了后台传过来一个地区,我地图无法渲染进去数据,后来一查这个地区早就改名了,后台专门改库……
哦,对,还有放大缩小是roam,这个是配置项,很简单自己配。
柱状图滚动加点击
这个柱状图我就上一下滚动的配置项和点击事件吧:{
that.chartBar.setOption({
backgroundColor: 'rgba(61,93,255,0)',
legend: {
bottom: 20,
right: 20,
textStyle: {
color: '#fff'
},
itemHeight: 10,
itemWidth: 10,
selectedMode: true, //图例选择的模式,控制是否可以通过点击图例改变系列的显示状态。(打开样式有bug)
data: ['政府填答', '企业填答', '系统接入'] //图例数组,每条数据的3种类新
},
grid: {
……
//省略
},
tooltip: {
show: true,
……
//省略一些,跟上一个差不多
formatter: function(params) {
let res = `<div style="padding: 5px">
<span>${params.name} -- ${params.seriesName}</span><br/>
<span style="font-size: 10px;line-height: 30px">指标得分 </span><span style="color: #00FFED;font-size: 18px;line-height: 30px">${params.value}</span>
</div>`
return res
}
},
yAxis: {
……
//省略
},
xAxis: [
{
……
//省略
axisLabel: {
inside: false,
interval: '0',
showMinLabel: 'false',
textStyle: {
color: function(value, index) {
//判断type,来设置字体选中颜色
if (that.cityData[0].type) {
return that.cityData[index].type == 1 ? 'rgb(255, 39, 124)' : 'rgb(0, 193, 170)'
} else {
return index == that.dataIndex ? '#00fff0' : 'rgb(224,224,224)'
}
},
fontWeight: that.cityData[0].type ? 'bold' : 'normal',
fontSize: '12'
},
formatter: function(value) {
//解决x轴单位名称太长,每4个字换一下行
if (value.length > 4) {
return value.substring(0, 4) + '\n' + value.substring(4, value.length)
} else {
return value
}
}
},
data: that.cityData.map(i => i.indicatorName) //x轴背景数组
},
{
type: 'category',
……
//省略
data: that.cityData.map(i => i.indicatorName) //x轴显示数组
}
],
dataZoom:
that.cityData.length > 9 //数据量多余9条设置滚动
? {
show: true,
realtime: true,
fillerColor: 'rgba(6, 123, 201)',
backgroundColor: '#020254',
borderColor: 'rgba(197,197,197,0.3)',
left: 150,
bottom: 30,
showDetail: false,
height: 8,
width: '60%',
start: 0,
end: 60
}
: {
show: false
},
series: [
{
name: '政府填答',
type: 'bar',
……
//省略
data: that.cityData.map(i => i.governmentScore)
},
{
name: '企业填答',
type: 'bar',
……
//省略
data: that.cityData.map(i => i.businessScore)
},
{
name: '系统接入',
type: 'bar',
……
//省略
data: that.cityData.map(i => i.otherScore)
}
]
})
}
复制代码
这个重点就没有太多,代码几乎都看清楚了,设置滚动,和点击变色。 然后点击时事件:
if (init) {
that.chartBar.on('click', function(params) {
if (that.isActiveDepart) {
// 选中部门的时候不能点击指标
} else {
if (that.componentIndex == params.componentIndex && that.dataIndex == params.dataIndex) {
that.componentIndex = null
that.dataIndex = null
return
} else {
that.componentIndex = params.componentIndex //选中的数据分类 0政府/1企业/2外部
that.dataIndex = params.dataIndex //选中的x轴指标选项index
that.indicatorTitle = params.name//这是标题名字
that.getDepartScore(that.cityData[params.dataIndex].indicatorId)
that.getIndicatorRank(that.cityData[params.dataIndex].indicatorId)
}
that.initBarCharts()
myChart.off('click')
}
})
}
复制代码
没有太多花哨,主要通过缩放dataZoom吧宽度固定一定比例,去掉detail来达到柱状图滚动效果。代码很详细了~
进度条
进度条不是用的echarts是Ant Design进度条组件,小技巧: 通过判断值来切换进度条颜色达到不同排名不同颜色
<a-progress
v-if="item.score!==null"
:strokeColor="(item.score < 60 ? '#FF5543' : '') || (item.score > 80 ? '#00C277' : '')"
:strokeWidth="4"
status="active"
:percent="item.score"
:format="percent => `${percent}`"
/>
复制代码
平行坐标系
这个图算基本图形里面比较异类的,数据结构上也很异类: initParallelCharts() {
const that = this
var data = []
let dataItem = that.parallelData.find(v => {
let objLength = Object.keys(v.data)
if (objLength.length > 0) {
return v
}
}) //对象标准,用来补充data为空的情况
if (dataItem == undefined) {
that.noData = true
return
} else {
that.noData = false
}
//没有数据不渲染
let lineStyle = {
width: 2,
opacity: 1
} //线条样式配置
var series = [] //series 配置项
for (let i of that.parallelData) {
let arr = [[]]
let seriesObj = {}
let hasData = JSON.stringify(i.data) != '{}'
// 该区域存在数据,不存在数据后台返回空对象,前台默认添加对象属性,值为0
if (hasData) {
for (let j in i.data) {
arr[0].push(i.data[j])
}
} else {
for (let j in dataItem.data) {
arr[0].push(0)
}
}
seriesObj = { name: i.region.name, type: 'parallel', lineStyle, data: arr }
series.push(seriesObj)
data.push(arr)
}
var schema = []
let num = 0
for (let i in dataItem.data) {
let obj = { name: i, text: i, index: num }
num += 1
schema.push(obj)
}
let parallelAxis = [] //parallelAxis配置项
for (let i = 0; i < schema.length; i++) {
parallelAxis.push({
dim: i,
name: schema[i].text
})
}
let legendName = []
for (let i of that.parallelData) {
legendName.push(i.region.name)
}
that.echartParallel = echarts.init(document.getElementById('echartParallel'))
that.echartParallel.setOption({
legend: {
bottom: 0,
data: legendName,
itemGap: 15,
itemWidth: 5,
itemHeight: 5,
textStyle: {
color: '#F0F2F5',
fontSize: 13
}
},
parallelAxis: parallelAxis,
parallel:……,//省略
series
})
},
复制代码
看一下处理完的数据示例:
legendName:["金牛区","武侯区","青羊区","锦江区"],
parallelAxis:[{dim: 0, name: "成本"},{dim: 1, name: "时间"},{dim: 2, name: "环节"}],
series:[{
data: [[89.2, 70, 70]]//这个地方要是二维数组,可以存在多数值线
lineStyle: {width: 2, opacity: 1}
name: "青羊区"
type: "parallel"
} ………]
复制代码
这个平行坐标系主要特殊在他是用数组对应的index来取值渲染,legendName存放纯字符串,parallelAxis存放对应的Y轴坐标单位名称,series的数组每个对象对应一个legend分类。
假设series的data里面存放一个数组,那就是一个legend对应一条线,data里面存放多个数组就是一个legend对应多条数据,而data里面的数组存放的是y轴的值按parallelAxis的index对应。其实看看也就懂了~
G2
G2 是蚂蚁金服数据可视化解决方案 AntV 的一个子产品,是一套数据驱动的、高交互的可视化图形语法。
看起来更好看一些,用法类似,但是概念有差距,社区略鸡肋,具体可以自己了解一下。 然后主要分享一下之前记录的几个G2问题:
- legend图例使用滚动条;
- 南丁格尔图添加中心文字以及legend百分比;
- G2封装组件重载问题;
legend图例使用滚动条
先看效果:
this.chart.legend({
// marker: 'square',
position: 'right-center',
// textStyle: { fill: '#a3a9c1' },
// offsetX: 20,
itemFormatter(val) {
let labe = data.find(v => v.content == val).percent
return `${val} ${parseInt(labe / sum * 100)}%` // val 为每个图例项的文本值
}else{
},
useHtml: true,//使用Html绘制图例
flipPage: true,//图例超出容器是否滚动
containerTpl: '<div class="g2-legend" style="position:absolute;top:20px;right:60px;width:auto;">'
+ '<h4 class="g2-legend-title"></h4>'
+ '<ul class="g2-legend-list" style="list-style-type:none;margin:0;padding:0;"></ul>'
+ '</div>',//图例容器
itemTpl: '<li class="g2-legend-list-item item-{index} {checked}" style="margin-right: 24px" data-color="{originColor}" data-value="{originValue}">' +
'<i class="g2-legend-marker" style="background-color:{color};"></i>' +
'<span class="g2-legend-text">{value}</span></li>'//图例
})
复制代码
南丁格尔图添加中心文字以及legend百分比
先看效果:
- 中心文字设置
this.chart.guide().html({
position: ['50%', '50%'],
html: `<div style="color:#fff;font-size: 25px;text-align: center;width: 10em;">${total}<br><div style="color:#fff;font-size:14px;text-align: center">总得分</div></div>`,
alignX: 'middle',
alignY: 'middle'
})
复制代码
- 百分比设置
//通过legend的itemFormatter参数进行格式化
this.chart.legend({
marker: 'square',
position: 'right-center',
textStyle: { fill: '#a3a9c1', fontSize: '12' },
itemFormatter(val) {
let labe = data.find(v => v.title == val).population
return `${val}
${parseInt(labe / sum * 100)}%` // val 为每个图例项的文本值
}
})
复制代码
chart.guide().html(cfg)
辅助 html。
chart.guide().html({
position: { object } | { function } | { array }, // html 的中心位置
htmlContent: { string }, // html 代码
alignX: { string }, // html 水平方向的布局,可取值为 'left','middle','right'
alignY: { string }, // html 垂直方向的布局,可取值为 'top','middle','bottom'
offsetX: { number },
offsetY: { number },
zIndex: { number },
});
复制代码
G2封装组件重载问题
G2 更新数据的方式主要有三种:
- 仅仅是更新图表的数据
- 清理所有,重新绘制
- 使用 DataView 时的更新
如果需要马上更新图表,使用 chart.changeData(data) 即可:
chart.changeData(newData);
复制代码
view 也支持 view.changeData(data),如果仅仅是更新数据,而不需要马上更新图表,可以调用 chart.source(data),需要更新图表时调用 chart.repaint()
chart.source(newData);
chart.guide().clear();// 清理guide
chart.repaint();
复制代码
清理图形语法: 更新数据时还可以清除图表上的所有元素,重新定义图形语法,重新绘制
chart.line().position('x*y');
chart.render();
chart.clear(); // 清理所有
chart.source(newData); // 重新加载数据
chart.interval().position('x*y').color('z');
chart.render();
复制代码
关键点在于:
- 不要将chart实例化在初始化方法里
- 将这个实例保存下来
- 重载方法不要去new Chart
- 直接执行chart.changeData()
本人写文章一般都是分享自己学习的探索路程,可能对更多刚入门前端或者正从入门到进阶前端开发者更有用些,欢迎点赞鼓励和评论指正:v::v:
觉得有用求赞 哈哈哈~~