前言
玩转ECharts系列,主要为大家讲解我们基于ECharts如何实现企业级大屏项目中较为常用的各种奇奇怪怪的图表,接下来我们将通过几篇文章从基础图表开始为大家详细讲解实现这些图表样式的一些“小心机”。导读
阅读完此文,你会了解基于ECharts:
1、如何实现能够动态配置颜色的图标
2、如何通过自定义图标写条形图
背景
在使用ECharts绘制折线图时,为了丰富图表的视觉表达,设计师们常常会为视图搭配上不同的图标。但ECharts官方所提供的预置图标样式却是有限的,无法匹配设计师们的创造性。如下图所示,通常这样的需求需要使用3个不同颜色的icon资源,设计师需要提供多个图标切图,当数据颜色序列数量不固定时,需要的图标数量不可控,研发同学又控制不了颜色,由此可见,当形状相同颜色不同时,我们可以使用一个icon资源来解决这件事,接下来,笔者将说明如何使用自定义图标来解决上述问题,需求可以拆解为:1.图标颜色需要能够动态与数据相匹配,并能够在指定位置自定义(不同的位置颜色不同);2.在折线图节点、legend和tooltip中,图标能够统一。
知识点
查阅ECharts官方文档可知,自定义图标的返回值有2种(series.line中可以使用函数的形式返回,但返回的字符串还是需要符合以下格式):
URL
通过 'image://url'
设置为图片,其中 URL 为图片的链接,或者 dataURI
。
URL 为图片链接,例如:
'image://http://xxx.xxx.xxx/a/b.png'
URL 为 dataURI
,一般为base64格式,例如:
'image://data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'
path
通过 'path://'
将图标设置为任意的矢量路径,例如:
'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z'
比较这两种方法(如下表所示),可以发现它们各自的优缺点都十分明显,但似乎没有哪一种方式能够满足我们的需求。
URL | path | ||
URL-图片链接 | URL-dataURI | ||
优点 | 1.可以直接使用,在合适的场景中较为方便。 | 1.易于复制,不需要担心使用场景。 | 1.易于复制,不需要担心使用场景 2.不需要担心因为缩放而产生锯齿或模糊; 3.颜色与数据相匹配; 4.路径图形会自适应调整为合适的大小。 |
缺点 | 1.需要请求图片文件,对于在线编辑、无法使用外链、代码与资源跨域等使用场景不太友好; 2.难以在代码中动态编辑; 3.需要提供与给定颜色集同等数量的源文件以匹配不同颜色; 4.legend项selected为false时,颜色无法改变。 | 1.难以构建、阅读、编辑修改; 2.需要提供与给定颜色集同等数量的副本以匹配不同颜色; 3.legend项selected为false时,颜色无法改变。 | 1.难以构建、阅读、编辑修改; 2.无法独立控制颜色,比如无法在不同的部分指定不同的颜色、当数据的颜色为渐变色时,无法将icon设置为纯色。 |
但是实际上,dataURI存在一个不同于常见的“使用base64转码”的“隐藏”配置方式,不仅好用、还可读。也就是笔者在本篇文章中要重点介绍的:将svg格式文件转换为CSS的url()可以内联的svg代码。
基础实现
单折线示例
首先,我们准备一张svg格式的图标(可以直接去各大图标库里找一找)。
示例图标:
CSS的url()可用的svg内联代码配置方式为:
'data:image/svg+xml;utf8,'
加上经过encodeURIComponent()转译的完整的SVG代码主体(即整个节点)。
(你也可以阅读这篇文章了解更多关于如何将svg转换为内联url的细节:https://www.zhangxinxu.com/wordpress/2018/08/css-svg-background-image-base64-encode/)
然后,为了能够在ECharts中使用,还需要按照ECharts的需要在这段代码之前加上image://。
let icon = ''icon = encodeURIComponent(icon) // 转译icon = 'data:image/svg+xml;utf8,' + icon // 添加url前缀icon = 'image://' + icon // 添加ECharts需要的前缀
将我们拼接好的图标运用到折线图中,得到如下效果。
参考示例:https://gallery.EChartsjs.com/editor.html?c=x_1lidFKm9&v=3

可以看到,这种方式最大限度的保留了svg的文件格式,也就一定程度上保证了图片文件的易读和易写性。
多折线示例
当然,仅仅是这样并不能满足我们的需求,在有多组数据时,我们提出要求:图标外圈的光晕部分,颜色与数据保持一致,但内圈的颜色不变。这就显出了保持原本svg格式的好处:我们可以很容易的找到外圈的颜色样式信息 :fill="#FF6E6E",并在代码中动态的修改它。
为了方便修改,将代码块中需要替换的颜色部分用 __color标识出来,即,将fill="#FF6E6E" 变成 fill="#__color",之后在代码中动态替换为对应的颜色。
需要注意的是,我们使用的图标字符串是经过转码的,因此,这里的 #__color 以及用来替换的颜色也需要转码。
let myTag = '#__color'myTag = encodeURIComponent(myTag)let currentColor = '#FF0' // 当前颜色currentColor= encodeURIComponent(currentColor)const symbol = icon.replace(myTag, currentColor)
为了保证在视图、legend和tooltip中图标样式的统一,还需要配置一下tooltip中的formatter,将自定义的图标配置到节点上。
参考示例:https://gallery.EChartsjs.com/editor.html?c=xTsMpbu75i&v=3
扩展实现
实际上,这样的自定义节点除了可以帮助我们让视图变得更好看,还能用在很多其他的地方。比如,在legend中动态绘制长条图。
通过手写svg,并将其转换为自定义图标添加到legend中,可以做到图例项的选取交互和数据表达两不误。
参考示例:https://gallery.EChartsjs.com/editor.html?c=xIe7R-LIBf
核心代码
newData.forEach(item => { const _num = Math.log10(Math.round(item.max)) const _max = Math.log10(Math.round(_lengedMax)) const _numWidth = Math.round(_num / _max * maxWidth) const _dataWidth = _numWidth > 20 ? _numWidth : 20 item.textStyle = { color: countryMap[item._name].color } // 在这里对 legend 的 icon 做了自定义 动态的修改了他的${_dataWidth}属性 item.icon = `image://data:image/svg+xml,` + `%3Csvg xmlns='http://www.w3.org/2000/svg'%3E` + `%3Cpath d='M0 260h${_dataWidth}v270H0z' fill='${countryMap[item._name].color}'/%3E` + `%3Cpath fill='rgba(0, 0,0,0)' d='M${_dataWidth} 60h${maxWidth - _dataWidth}v100H100z'/%3E` + `%3Ctext x='${_dataWidth}' y='0' style='color: red' %3E${item.max}%3C/text%3E` + `%3C/svg%3E` legendData.push(item)})
实例展示
这里有一些我们在实际应用中使用的的效果,抛砖引玉,为大家设计时提供一个思路。