作者:vivo 互联网大数据团队- Wang Lei
一、前言
始终以来,许多产品平台都在尝试通过可视化搭建的伎俩来升高 GUI 利用的研发门槛,进步生产效率。随着咱们业务的倒退,数据建设的欠缺,用户对于数据可视化的诉求也日益增多,而数据大屏是数据可视化的其中一种展现形式,它作为大数据展现媒介的一种,被宽泛使用于各种会展、公司展厅、发布会等。
相比于传统手工定制的图表与数据仪表盘,通用大屏搭建平台的呈现,能够解决定制开发, 数据扩散带来的利用开发、数据保护老本低等问题,通过数据采集、荡涤、剖析到直观实时的数据可视化展示,可能多方位、多角度、全景展示各项指标,实时监控,动静高深莫测。
本文将通过麻利BI平台的通用大屏搭建能力的实现计划,来解说一下通用可视化搭建平台整体的设计思路。
二、疾速理解可视化大屏
2.1 什么是数据可视化
从技术层面上来讲,最直观的就是前端可视化框架:Echart、Antv、Chart.js、D3.js、Vega 等,这些库都能帮咱们疾速把数据转换成各种模式的可视化图表。
从业务层面来讲, 其最次要的意义就在于通过数据 -> 图表组合 -\> 可视化页面这一业务流程,来帮忙用户更加直观整体的剖析不同行业和场景的趋势和法则。
所以在数据畛域里,对于简单难懂且体量宏大的数据而言,图表的信息量要大得多,这也是数据可视化最基本的目标。
2.2 可视化大屏都有哪些局部
次要由 可视化组件 + 事件交互 + 坐标关系 组成,成果如下图所示:
2.3 可视化大屏和常见的BI报表看板的区别
常常会有同学会问到,可视化大屏和BI报表看板的区别是什么?
这里简略的做一下介绍:
- 大屏和报表看板都只是BI的其中一种展示形式,大屏更多是通过不同尺寸的显示器硬件上进行投屏,而报表看板更多是在电脑端进行展现应用。
- 大屏更加重视数据动态变化 ,会有极强的视觉体验和冲击力,提供丰盛的轮播动画、表格滚动等动画特效。而报表看板更重视交互式数据摸索剖析,例如上卷下钻、排序、过滤、图表切换、条件预警等。
三、设计思路
3.1 技术选型
- 前端框架:React 全家桶(集体习惯)
- 可视化框架:Echarts[DataV-React](https://github.com/DataV-Team...) (封装度高,json构造的配置项易拓展) D3.js(可视化元素粒度小、定制能力强)
- 拖拽插件:dnd-kit (满足树状构造视图的跨组件拖拽)
- 布局插件:React-Grid-Layout(网格自在布局,批改源码,反对多个方向的拖拽,自在布局、锁定缩放比等)
3.2 架构设计
下图是咱们搭建平台的整体架构设计:
整个大屏搭建平台蕴含四个十分重要的子系统和模块:
- 可视化物料核心:是整个平台最根底的模块,咱们在开源的图表库和自主开发的可视化组件下面定义了一层规范的 DSL 协定,这个协定和接入 画布编辑器 的协定是对应的,目前曾经有 40+ 相干组件,组件数量还在一直增长。
- 画布编辑器:是搭建平台的外围与难点,反对页面布局配置、页面交互配置和组件数据配置等性能,另外还反对代码片段的配置,也能够称得上是一个低代码平台。
- 数据中心:是提供专门用于连贯不同数据源的服务,例如直连 MySQL、ClickHouse、Elasticsearch、Presto 等,提供了大屏搭建所须要的原始数据。
- 管理中心:是大屏的后盾经营治理模块,蕴含了大屏模版治理、大屏公布下线、拜访权限等治理性能。
3.3 搭建流程
通过下面提到的大屏组成元素,咱们能够剖析总结出大屏搭建主流程如下图所示:
四、外围性能实现
接下来咱们会逐个对平台几个外围性能实现进行解析:
1、大屏自适应布局
背景:解决页面错乱问题,实现多种分辨率的大屏适配:
思考:首先咱们想到的是挪动端适配支流的 vh、vw、rem组合的形式以及 font.js+rem 等两种计划。第一种计划次要是通过媒体查问来定义父级大小,而后对组件的height、margin、padding等多种css属性采纳rem作为单位,继承父级设置等单位(1vw),实现自适应适配,第二种计划是援用第三方脚本,通过在main.js中写代码计算,应用rem进行继承,实现适配。
① vh、vw、rem组合
//vw vh单位 w3c的官网解释 vw:1% of viewport’s width vh:1% of viewport’s height//例如,设计稿的宽度为1920px,则1vw=19.2px,为了不便计算,咱们将html元素的font-size大小设置为100px,也就是5.208vw=100px。body,html { font-size:5.208vw}
② font.js + rem
//监听窗口的oversize事件,来动静计算根节点字体大小,再配合rem做适配(function(doc, win) { const docEl = doc.documentElement const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize' const recalc = function() { let clientWidth = docEl.clientWidth if (!clientWidth) return docEl.style.fontSize = 100 * (clientWidth / 1920) + 'px' } if (!doc.addEventListener) return win.addEventListener(resizeEvt, recalc, false) doc.addEventListener('DOMContentLoaded', recalc, false)})(document, window)
缺点:当咱们大屏外面应用到的第三方插件,它的款式应用的是px为单位时,例如 line-height 的设置为20px,此时就不能适应行高,就会呈现重叠等错乱问题。或者咱们利用 postcss-px2rem 插件进行全局替换,然而在应用过程中,须要留神对曾经解决过适配的插件,例如 Ant Design,否则引入的antd 控件应用会呈现款式错乱的问题
解决思路:采纳了css3 的缩放 transform: scale(X,Y) 属性,次要是通过监听浏览器窗口的 onresize 事件,当窗口大小发生变化时,咱们只须要依据大屏容器的理论宽高,去计算对应的放大放大的比例,就能够实现布局的自适应了,咱们也提供了不同的布局适应成果,例如等高缩放、等宽缩放、全屏铺满等,不同的缩放形式,决定了咱们在计算宽高比例的优先级。因而咱们前面在做画布的放大性能,也能够间接应用这种计划来实现。
// 基于设置的设计稿尺寸 换算对应的宽高比useEffect(() => { const wR = boxSize.width / viewWidth; const hR = boxSize.height / viewHeight; setBgScaleRatio(wR); setBgHeightScaleRatio(hR);}, [boxSize, viewWidth, viewHeight]); //依据等宽、等高、全屏等不同的缩放比例 计算scale值const getScale = (proportion, x, y) => { if (proportion === 'radioWidth') { return `scaleX(${x})` } if (proportion === 'radioHeight') { return `scaleY(${y})` } return `scale(${x}, ${y})`}
2、大屏组件通用开发流程设计
背景:随着可视化组件的增多、新增组件流程繁琐简短,为了防止反复的造轮子以及后续引入第三方组件,须要制订一套通用的组件开发流程:
设计思路:组件 = component 组件主体 + schema 组件配置协定层 + 组件定义层(类型、从属关系、初始化宽低等)
① component 组件主体:
- 可视化框架选型:行业支流可视化库有 Echart、Antv、Chart.js、D3.js、Vega、DataV-React 基于可视化的通用性和定制性的需要,咱们抉择了 Echart、DataV-React 作为根底组件的开发框架,面对定制性要求更高的自定义组件,咱们抉择了可视化粒度更小的 D3.js。
- 封装通用 Echarts 组件(初始化、事件注册、实例登记等):
// initialization echartsconst renderNewEcharts = () => { // 1. new echarts instance const echartObj = updateEChartsOption(); // 2. bind events bindEvents(echartObj, onEvents || {}); // 3. on chart ready if (typeof onChartReady === 'function') onChartReady(echartObj); // 4. on resize echartObj.resize();}; // bind the eventsconst bindEvents = (instance, events) => { const _bindEvent = (eventName, func) => { instance.on(eventName, (param) => { func(param, instance); }); }; // loop and bind for (const eventName in events) { if (Object.prototype.hasOwnProperty.call(events, eventName)) { _bindEvent(eventName, events[eventName]); } }}; // dispose echarts and clear size-sensorconst dispose = () => { if ($chartEl.current) { clear($chartEl.current); // dispose echarts instance (echartsLib || echarts).dispose($chartEl.current); }};
- 封装通用 DataV 组件(DataV-React、自定义等组件入口,对立负责配置、数据收集、监听resize)
const DataV: React.FC<DataVProps> = (props) => { const { config } = props; const [renderCounter, setRenderCounter] = useState(0); const $dataVWarpEl = useRef(null); const $componentEl = useRef(null); useEffect(() => { // 绑定容器size监听 const resizefunc = debounce(() => { $componentEl.resize(); }, 500) // fixme addResizeListener($dataVWarpEl.current, resizefunc); return () => { // 革除订阅 removeResizeListener($dataVWarpEl.current, resizefunc); }; }, []); return ( <DataVWarp ref={$dataVWarpEl}> <CompRender config={config} ref={$componentEl} /> </DataVWarp> );};
② schema 组件配置协定层 + 组件定义层(类型、从属关系、初始化宽低等)
咱们自定义了一套 schema 组件的DSL,构造协定层。通过DSL约定了组件的配置协定,包含组件的可编辑属性、编辑类型、初始值等,之所以定义统一的协定层,次要是不便前期的组件扩大,配置后移。
- JSON Schema设计:
{ headerGroupName: '公共配置', //配置所属类型 headerGroupKey: 'widget', //配置所属类型key值 雷同的key值都归属一类 name: '题目名称', //属性名称 valueType: ['string'], //属性值类型 optionLabels: [], //服务下拉列表、多选框等控件的标签名 optionValues: [], //服务下拉列表、多选框等控件的标签值 tip: false, //配置项的 Tooltip 注解 ui: ['input'], //应用的控件类型 class: false, //控件类名,定制控件款式 css: { width: '50%'}, //批改控件款式 dependencies: ['widget,title.show,true'], //属性之间的联动,规定['配置所属类型, 属性key, 属性值'] depContext: DepCommonShowState, //属性之间的校验回调办法 compShow: ['line'], //哪些组件可配置 dataV: { key: 'title.text', value: '' }, //配置的key值和默认value值},
- 表单DSL设计:
收益:以上是咱们定制的DSL构造协定层,用户只须要填写Excel表格,就能够实现动静表单的创立,实现组件配置项分类、配置复用、配置项之间联动、属性正文等性能。目前属性配置器曾经反对了罕用的15种的配置UI控件,通过定制的DSL构造协定层,能够疾速实现组件的配置界面初始化,为后续布局的组件物料核心做筹备。
3、拖拽器实现
背景:React-Grid-Layout 拖拽插件不反对自在布局和组件不同纬度拖拽:
解决方案:通过剖析源码,对不同纬度的拖拽事件以及拖拽指标碰撞事件进行了重写,并且也拓展了锁定宽高比、旋转透明度等性能。
源码剖析:
resize伸缩个性加强(优化),拖拽的同时,除了批改容器宽高外,也动静调整了组件的坐标地位
// CSS Transforms support (default)if (useCSSTransforms) { if (activeResize) { const { width, height, handle } = activeResize; const clonePos = { ...pos }; if (["w", "nw", "sw"].includes(handle)) { clonePos.left -= clonePos.width - width; } if (["n", "nw", "ne"].includes(handle)) { clonePos.top -= clonePos.height - height; } style = setTransform(clonePos, this.props.angle); } else { style = setTransform(pos, this.props.angle); }}
重叠显示,自在布局(优化),通过管制布局是否压缩,动静调整拖拽指标的层级zIndex来实现多图层组件操作交互和自在定位。
// 每次拖拽时zIndex要在以后最大zIndex根底上 + 1,并返回给组件应用const getAfterMaxZIndex = useCallback(i => { if (i === curDragItemI) { return; } setCurDragItemI(i); setMaxZIndex(maxZIndex => maxZIndex + 1); return maxZIndex;}, []);
革新后成果展现
4、大屏状态推送
背景:大屏的前期保护须要有版本公布自更新以及大屏下线等需要,这个时候就须要有一套音讯告诉机制,通过命令来管制大屏的运行状态。
解决方案:基于websocket通信机制,建设长链接,实现了心跳及重连机制,实时对上线公布后的大屏进行更新或下线治理。
五、成果预览
六、总结
本文通过可视化页面搭建、no/low code 平台、Schema 动静表单等技术思维来剖析解说了如何去设计开发一个通用的数据大屏搭建平台。
以后的设计方案根本满足了数据大屏的外围能力搭建需要。如果想实现更富裕展示力, 满足更多场景的大屏搭建平台, 咱们还须要进一步提高平台的扩展性和欠缺整体的物料生态, 具体布局如下:
- 丰盛和拓展大屏组件&配置能力,笼罩不同行业的可视化场景。
- 可视化物料平台的搭建,积淀优良的可视化组件、大屏模版素材。
- 3D以及动效渲染引擎开发实现。