可视化插件实战教程
1. 概述
在搭建图表时,面对不同的业务场景,我们经常会有一些特殊图表的需求。这种情况下,我们可以通过观远 BI 内的自定义图表功能,进行这些特殊图表的创建,满足我们的需求。事实上,对于这些创建好的“特殊图表”,我们还可以通过一些操作配置,将其封装为“可视化插件”,再安装入 BI 中,使其得到无限复用。
本文则会对这些操作配置,进行详细介绍。
2. 创建流程
可视化插件的创建流程大体分为以下三步:
生成自定义图表:通过观远 BI 平台内的自定义图表功能进行个性化展示样式的创建,具体步骤可参考《自定义图表》。
生成可视化插件:按照观远 BI 定义的格式内容以 JSON 文件的形式生成可视化插件,具体步骤可参考本文 2.2 生成可视化插件 。
封装图表配置项:设计前端界面配置项展示效果、将图表配置项进行封装,本文将重点介绍。
2.1 生成自定义图表
2.1.1 功能入口
仪表板-新建卡片-自定义图表
2.1.2 编辑区域
编辑区域共分为以下三个部分:
-
图库引用及容器定义(HTML):主要用于定义图表渲染的容器以及图表渲染时需要引用的图表库。
-
主体样式设置(CSS):静态样式设置。一般情况下不需要进行额外修改,容器的自适应可在此处进行设置。例如在引用 Amcharts 的图表时,其 CSS 中对容器“chartdiv”的height默认为500px。可以通过修改为95%来实现自定义图表随着卡片大小的调节而进行高度自适应。
-
图表渲染逻辑(JAVASCRIPT):图表数据的接入以及如何渲染都在此处进行定义。
2.1.3 绘制示例
下面我们以 Amcharts 的柱状折断图举例说明如何生成自定义图表。
温馨提示:Amcharts,是一家图表组件开发与扩展工具服务商,与观远数据产品与服务无关,其提供 JavaScript/HTML5 Charts、Javascript/HTML5 Stock Chart、JavaScript Maps 三种图表组件,如您需要使用其产品或服务,请自行了解其产品或服务相关价格等事宜。
Step1. 图表拷贝
首先,我们从 Amcharts官网 的 Demos 中选择柱状折断图,进入后选择以 CodePen 模式打开;
在 CodePen 模式下,复制 HTML、CSS 与 JS 的内容到观远自定义图表的对应区域;
-
JS的内容不要直接对自定义图表JAVASCRIPT中的内容进行替换,需要粘贴在 function renderChart 下的“/* ------ Custom Code Start ------ /”与“/ ------ Custom Code End ------ */”中间
-
如果是内网环境,还需要将 HTML 里的图表库引用地址替换为内网离线图表库地址
上述操作做完后,就完成了图表的拷贝。
Step2. 数据接入
接下来我们需要将观远BI中的数据接入自定义图表中进行渲染。
在“数据视图”中准备好数据。
在“图表视图”中可以通过右上角的“查看视图数据结构”对“数据视图”中传入的数据进行预览。
Demo中提供的样例数据如下所示,我们需要将chart.data数组中的数据替换为我们准备好的数据。
如下图所示,将BI系统中的数据灌入chart.data中。
此时我们就完成了数据的接入。
Step3. 自定义修改
最后,可以根据不同图表库的配置修改教程,对不同图表的配置项进行自定义调整。
- 图表样式修改
- 对图表工具提示的展示内容设置
2.2 生成可视化插件
创建好自定义图表后,为便于再次使用,我们可以将其封装为可视化插件。
2.2.1 文件格式
可视化插件是以约定格式存储的json文件,格式如下:
- 可视化插件JSON文件模版
{
"name": "", //可视化插件名称,自定义
"version": "", //可视化插件模版,自定义
"author": "", //可视化插件作者,自定义
"description": "", //可视化插件描述,自定义
"logo": "", //可视化插件图标,base64格式
"css": "", //同自定义图表
"html": "", //同自定义图表
"subType": "PLUGIN", //类型为插件,无需修改
"libs": [], //引用js依赖
"script": "" //同自定义图表
}
- 可视化插件JSON文件样例
{
"name": "柱状折断图",
"version": "V1.0",
"author": "Guandata-wangping",
"description": "簇状柱状折断图",
"logo": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/....../gAooooAKKKKACiiigAooooA//Z",
"css": "body
{\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n#chartdiv {\n width: 100%;\n height: 95%;\n}\n\n#container table {\n border-collapse: collapse;\n border-color: #E4E7F0;\n}\n\n#container table th {\n padding: 4px 0;\n}\n\n#container table td {\n min-width: 120px;\n padding: 4px 0;\n text-align: center;\n}\n",
"html": "\n\n\n",
"subType": "PLUGIN",
"libs": [],
"script": "function renderChart(data, clickFunc, config) {\n /* ------ Custom Code Start ------ */\n\n am4core.ready(function () {\n // Themes begin\n am4core.useTheme(am4themes_animated);\n // Themes end\n\n var chart = am4core.create(\"chartdiv\", am4charts.XYChart);\n chart.hiddenState.properties.opacity = 0; // this creates initial fade-in\n chart.colors.step = 2;\n function createSeries(value, name) {\n var series = chart.series.push(new am4charts.ColumnSeries());\n series.dataFields.categoryX = 'Cat';\n series.dataFields.valueY = value;\n series.name = name;\n series.columns.template.tooltipText = \"{categoryX} {name} : {valueY.value}\";\n series.columns.template.tooltipY = 0;\n series.columns.template.strokeOpacity = 0;\n \n // as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set\n series.columns.template.adapter.add(\"fill\", function (fill, target) {\n return chart.colors.getIndex(target.dataItem.index);\n });\n \n return series;\n }\n chart.data = [];\n data[0][0].data.forEach((v,i) => chart.data.push({\"Cat\":v,\"Value1\": data[0][1].data[i]}));\n\n let index;\n ValueList = [];\n for(index in chart.data) {\n ValueList.push(chart.data[index].Value1)\n\n }\n maxValue = Math.ceil(Math.max.apply(null,ValueList));\n console.log(maxValue);\n/*\n chart.data = [\n {\n country: \"USA\",\n visits: 23725,\n data1: 12340,\n },\n {\n country: \"China\",\n visits: 1882,\n data1: 1230,\n },\n {\n country: \"Japan\",\n visits: 1809,\n data1: 1230,\n }\n ];\n */\n // var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());\n // categoryAxis.renderer.grid.template.location = 0;\n // categoryAxis.dataFields.category = \"country\";\n // categoryAxis.renderer.minGridDistance = 40;\n // categoryAxis.fontSize = 11;\n\n var xAxis = chart.xAxes.push(new am4charts.CategoryAxis())\n xAxis.dataFields.category = 'Cat'\n xAxis.renderer.cellStartLocation = 0.1\n xAxis.renderer.cellEndLocation = 0.9\n xAxis.renderer.grid.template.location = 0;\n\n\n var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());\n valueAxis.min = 0;\n valueAxis.max = maxValue;\n valueAxis.strictMinMax = true;\n valueAxis.renderer.minGridDistance = 30;\n // axis break\n var axisBreak = valueAxis.axisBreaks.create();\n axisBreak.startValue = maxValue * 0.3;\n axisBreak.endValue = maxValue * 0.9;\n //axisBreak.breakSize = 0.005;\n\n // fixed axis break\n var d =\n (axisBreak.endValue - axisBreak.startValue) /\n (valueAxis.max - valueAxis.min);\n axisBreak.breakSize = (0.05 * (1 - d)) / d; // 0.05 means that the break will take 5% of the total value axis height\n\n // make break expand on hover\n var hoverState = axisBreak.states.create(\"hover\");\n hoverState.properties.breakSize = 1;\n hoverState.properties.opacity = 0.1;\n hoverState.transitionDuration = 1500;\n\n axisBreak.defaultState.transitionDuration = 1000;\n createSeries('Value1', 'Value1');\n }); // end am4core.ready()\n\n /* ------ Custom Code End ------ */\n}\nnew GDPlugin().init(renderChart);\n"
}
2.2.2 自定义图表内容引用
通过 Chrome浏览器控制台中的接口返回信息,获取自定义图表内容。
具体操作:【F12/鼠标右键选择“检查”,打开控制台】--> 【切换到“网络/Network”页签】 --> 【自定义图表编辑界面点击“保存”】 --> 【从接口列表中选中“save”接口】 --> 【选择“预览/Preview”页签】 --> 【复制 content 下 html、css 与 script 中的内容】 --> 【粘贴到可视化插件JSON文件对应区域】
2.2.3 Logo生成与转换
首先,从网上下载(推荐: https://www.iconfont.cn/)或自己制作(推荐:Axure RP9工具)一个Logo图标。
之后,为适应观远两种不同颜色主题,需要将制作好的图标的纯色背景替换为透明背景,可使用美图秀秀等工具实现期望效果。
替换为透明背景后,将Logo图片转换为Base64格式的编码(推荐 https://c.runoob.com/front-end/59/)。
最后,将转换后的Base64编码粘贴到可视化插件JSON文件对应区域,即可生成可视化插件。
2.2.4 可视化插件安装与应用
经过上述步骤后,我们的可视化插件就构建完成了,接下来需要将可视化插件导入观远BI平台。
在仪表板中,点击“新建卡片”,将弹窗滚动到最下方,点击“安装插件”。选择之前生成的可视化插件JSON文件,便会将插件导入与安装,新增的插件会显示在当前卡片列表最末尾。
点击插件,如同普通卡片一样,选择分析数据集,进入卡片编辑界面后,按照界面指示拖拽数据,即可完成可视化展示。
2.3 封装图表配置项
在上文中,我们向大家介绍了如何生成可视化插件,并且通过安装与应用这些插件,我们可以快速实现期望的可视化效果。但这样的实现方式依旧存在某些局限性,例如界面上缺少直接自定义图表配置项的能力,这就导致当我们想要修改颜色、标签显示、图例样式时,需要回到JSON文件中进行代码修改,然后再进行插件的更新。
为了解决这个问题,我们需要对一些常用的配置项进行界面创建和渲染时的应用。
2.3.1 图表配置项创建
内置的渲染组件类型如下所示:
-
STRING // 文本输入框
-
SELECT // 下拉选择器
-
NUMBER // 数字输入框
-
BOOLEAN // switch开关
-
DIVIDER // 分割线(用于美化)
-
MODEL // 灵活的Model定义
-
GROUP // 分组
若想要展示界面创建和图表配置项,需要在可视化插件JSON文件中增加“chartConfigurations”。“chartConfigurations”中的内容模版如下所示:
- 可视化插件JSON文件模版增加“chartConfigurations”
{
"name": "", //可视化插件名称,自定义
"version": "", //可视化插件模版,自定义
"author": "", //可视化插件作者,自定义
"description": "", //可视化插件描述,自定义
"logo": "", //可视化插件图标,base64格式
"css": "", //同自定义图表
"html": "", //同自定义图表
"subType": "PLUGIN", //类型为插件,无需修改
"libs": [], //引用js依赖
"script": "" //同自定义图表
"chartConfigurations": [
{
"fieldName": "", //必填,组件名称,作为图表绘制时config中传入的名称
"label": "", //必填,前端界面展示名称
"type": "", //必填,渲染组件类型
"defaultValue": "", //可选,默认值
"dependsOnMap": {}, //可选,依赖项
/*
示例
"dependsOnMap": {
"配置项fieldName1": [ true ]
},
*/
"model": {} //可选,模型
/*
示例
"model": {
"modelType": "SELECT",
"labels": [ "数据标签外", "内部居中" ],
"values": [ "outside", "center" ],
"size": "small"
}
*/
},
{
配置项2
},
...
]
}
以上文提到的柱状折断图举例,我们在可视化插件JSON文件中增加chartConfigurations,并且构建了两个配置项“startValue”与“endValue”,来定义截断区间的起始值与结束值。如下所示:
- 可视化插件JSON文件模版增加“chartConfigurations”样例
{
"name": "柱状折断图",
"version": "V1.1",
"author": "Guandata-wangping",
"description": "簇状柱状折断图",
"logo": "data:image/png;base64,iVBORw0KGgoAAAANSU....../g+iCTPPrp+uQAAAAABJRU5ErkJggg==",
"css": "body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n#chartdiv {\n width: 100%;\n height: 95%;\n}\n\n#container table {\n border-collapse: collapse;\n border-color: #E4E7F0;\n}\n\n#container table th {\n padding: 4px 0;\n}\n\n#container table td {\n min-width: 120px;\n padding: 4px 0;\n text-align: center;\n}\n",
"html": "\n\n\n",
"subType": "PLUGIN",
"libs": [],
"chartConfigurations":[{"fieldName":"startValue","label":"截断起始","type":"NUMBER","model":{"size":"small"}},{"fieldName":"endValue","label":"截断终点","type":"NUMBER","model":{"size":"small"}}],
"script": "const DEDAULT_COLORS = ['#2f7ed8', '#f28f43', '#1aadce', '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']\nconst DEFALUT_THEME = 'LIGHT'\nfunction renderChart(data, clickFunc, config) {\n /* ------ Custom Code Start ------ */\n if (!data || data[0].length < 2) {\n\t\treturn void(document.getElementById(\"chartdiv\")\n\t\t\t.innerHTML = \"
需要2列数据(1个维度,1个数值)
第1列为维度
第2列为数值
\")\n\t}\n const {\n\t\ttheme: i = DEFALUT_THEME,\n\t\tcolors: s = DEDAULT_COLORS,\n\t\tcustomOptions: o = {}\n\t} = config || {}, n = \"LIGHT\" !== i, {\n\t\tstartValue: a,\n\t\tendValue: l\n\t} = o;\n\n\n am4core.ready(function () {\n // Themes begin\n am4core.useTheme(am4themes_animated);\n // Themes end\n\n var chart = am4core.create(\"chartdiv\", am4charts.XYChart);\n chart.hiddenState.properties.opacity = 0; // this creates initial fade-in\n chart.colors.step = 2;\n function createSeries(value, name) {\n\n//change theme colors\n s.forEach((v)=>\n {chart.colors.list.push(am4core.color(v))}\n );\n\nconsole.log(am4core.color(s[0]));\n var series = chart.series.push(new am4charts.ColumnSeries());\n series.dataFields.categoryX = 'Cat';\n series.dataFields.valueY = value;\n series.name = name;\n series.columns.template.tooltipText = \"{categoryX} {name} : {valueY.value}\";\n series.columns.template.tooltipY = 0;\n series.columns.template.strokeOpacity = 0;\n \n // as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set\n series.columns.template.adapter.add(\"fill\", function (fill, target) {\n return chart.colors.getIndex(target.dataItem.index);\n });\n \n return series;\n }\n chart.data = [];\n value1Name = data[0][1].name;\n data[0][0].data.forEach((v,i) => chart.data.push({\"Cat\":v,[value1Name]: data[0][1].data[i]}));\n\n ValueList = [];\n data[0][0].data.forEach((v,i) => ValueList.push(data[0][1].data[i]));\n maxValue = Math.ceil(Math.max.apply(null,ValueList));\n/*\n chart.data = [\n {\n country: \"USA\",\n visits: 23725,\n data1: 12340,\n },\n {\n country: \"China\",\n visits: 1882,\n data1: 1230,\n },\n {\n country: \"Japan\",\n visits: 1809,\n data1: 1230,\n }\n ];\n */\n // var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());\n // categoryAxis.renderer.grid.template.location = 0;\n // categoryAxis.dataFields.category = \"country\";\n // categoryAxis.renderer.minGridDistance = 40;\n // categoryAxis.fontSize = 11;\n\n var xAxis = chart.xAxes.push(new am4charts.CategoryAxis())\n xAxis.dataFields.category = 'Cat'\n xAxis.renderer.cellStartLocation = 0.1\n xAxis.renderer.cellEndLocation = 0.9\n xAxis.renderer.grid.template.location = 0;\n\n\n var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());\n valueAxis.min = 0;\n valueAxis.max = maxValue;\n valueAxis.strictMinMax = true;\n valueAxis.renderer.minGridDistance = 30;\n // axis break\n var axisBreak = valueAxis.axisBreaks.create();\n axisBreak.startValue = a || maxValue * 0.3;\n axisBreak.endValue = l || maxValue * 0.9;\n //axisBreak.breakSize = 0.005;\n\n // fixed axis break\n var d =\n (axisBreak.endValue - axisBreak.startValue) /\n (valueAxis.max - valueAxis.min);\n axisBreak.breakSize = (0.05 * (1 - d)) / d; // 0.05 means that the break will take 5% of the total value axis height\n\n // make break expand on hover\n var hoverState = axisBreak.states.create(\"hover\");\n hoverState.properties.breakSize = 1;\n hoverState.properties.opacity = 0.1;\n hoverState.transitionDuration = 1500;\n\n axisBreak.defaultState.transitionDuration = 1000;\n createSeries(value1Name, value1Name);\n }); // end am4core.ready()\n\n /* ------ Custom Code End ------ */\n}\nnew GDPlugin().init(renderChart);\n"
}
JSON文件中增加了 chartConfigurations 内容后,更新插件。我们会发现在通过可视化插件创建图表时,右侧的图表配置区域增加了“配置扩展”模块,并且我们刚刚定义的“startValue”与“endValue”两个配置项都已经展示出来了。至此我们已经完成了图表配置项的前端界面准备工作。
JS区域绘制图表的函数 renderChart 中共有三个入参:data、clickFunc 以及 config。
-
data 为数据数组,负责传递我们已经准备好的数据,上文已经介绍过;
-
clickFunc 为交互回调,传递的是用户交互事件信息;
-
config 为图表配置,传递的是图表的各类配置信息。
我们通过控制台输出一下 config 中的内容,发现 config 中一共传递了如下内容:colors、customOption、 theme 和 language(5.6.0 版本支持)。
-
colors 中包含的是“主题/颜色”中对应的颜色信息;
-
customOption 中包含有自定义的两个配置项“endValue”与“startValue”信息;
-
theme 中包含主题信息;
-
language 中包含当前系统使用语言(5.6.0 版本支持)。
2.3.2 配置项接入
从 config 入参中我们可以获取到 colors、customOption、 theme 与 language(5.6.0 版本支持)信息。我们仍以柱状截断图为例,针对colors 与 customOption 进行配置说明。
Step1. 从config中取出图表配置信息赋予变量
Step2. 将变量值应用于图表渲染
颜色应用
在 Amcharts 中,图表渲染时会从系统预设的 chart.colors.list 中取数,而 hart.colors.list 中存放的是通过am4core.color($hex_color) 生成的 object。因此我们通过循环遍历将 config 中的 colors 值依次传入其中。如下所示
自定义配置项应用
我们之前定义了两个配置项“startValue”和“endValue”,用于设置截断区间。尽管之前已经按照最大值的30%-90% 做了一个截断区间的自动化处理,但某些情况下还是需要手动调整截断区间。
因此这里做了一个优化,当“startValue”与“endValue”两个配置项中有值传入时,取手动配置的区间值。当“startValue”与“endValue”为空时,按照最大值的30%-90%自动截断。如下图所示:
Step3. 展示效果验证
至此我们完成了所有的工作。将结果保存为JSON格式文件并且更新插件后,可以对最终的可视化插件展示效果进行查看与验证。