关于前端:从零设计可视化大屏搭建引擎

演示地址: V6可视化大屏编辑器

几个月前我写了一篇对于 从零开发一款可视化大屏制作平台 的文章, 简略概述了一下可视化大屏搭建平台的一些设计思路和成果演示, 这篇文章我会就 如何设计可视化大屏搭建引擎 这一主题, 具体介绍一下实现原理。

依照我一贯的写作格调, 我会在上面列出文章的纲要,以便大家有抉择且高效率的浏览和学习:

  • 疾速理解数据可视化
  • 如何设计通用的大屏搭建引擎
  • 大屏搭建引擎外围性能实现

    • 拖拽器实现
    • 物料核心设计
    • 动静渲染器实现
    • 配置面板设计
    • 控制中心概述
    • 性能辅助设计
  • 可视化大屏前期布局和将来瞻望

大家能够轻松依据右侧的文章导航, 疾速定位到本人想看的地位, 接下来咱们开始进入注释。

疾速理解数据可视化

说到数据可视化, 想必大家多多少少稍接触过, 从技术层面谈, 最直观的就是前端可视化框架, 比方:

  • echart
  • antv
  • Chart.js
  • D3.js
  • Vega

这些库都能帮咱们轻松制作可视化图表。

从实用性的角度来谈, 其最次要的意义就在于帮忙用户更好的剖析和表白数据。所以说谈到数据可视化, 更多的是和各种图表打交道, 通过 数据 -> 图表组合 -> 可视化页面 这一业务流程, 就形成了咱们明天要钻研的话题——设计可视化大屏搭建引擎

如何设计通用的大屏搭建引擎

说到 “引擎” 这个词兴许有种莫名的高大上, 其实在互联网技术中, 咱们常常会听到各种相干的名词,比方 “浏览器渲染引擎” , “规定引擎” , “图像识别引擎” 等, 我感觉 “引擎” 的实质就是提供一套牢靠的机制, 为零碎提供源源不断的生产力。 所以咱们明天谈的“可视化大屏搭建引擎”, 实质上也是提供一套搭建机制, 撑持咱们设计各种简单的可视化页面。

为了不便大家了解可视化搭建, 我这里展现2张可视化大屏的页面, 来和大家一起剖析一下可视化大屏的组成因素:

当然理论利用中大屏展示的内容和模式远比这简单, 咱们从上图能够提炼出大屏页面的2个直观特色:

  • 可视化组件集
  • 空间坐标关系

因为咱们可视化大屏载体是页面, 是html, 所以还有另外一个特色: 事件/交互。综上咱们总结出了可视化大屏的必备因素:

咱们只有充沛的了解了可视化大屏的组成和特色, 咱们能力更好的设计可视化大屏搭建引擎, 基于以上剖析, 我设计了一张根底引擎的架构图:

接下来我就带大家一起来拆解并实现下面的搭建引擎。

大屏搭建引擎外围性能实现

俗话说: “好的拆解是胜利的一半”, 任何一个简单工作或者零碎, 咱们只有能将其拆解成很多细小的子模块, 就能很好的解决并实现它. (学习也是一样)

接下来咱们就逐个解决上述根底引擎的几个外围子模块:

  • 拖拽器实现
  • 物料核心设计
  • 动静渲染器实现
  • 配置面板设计
  • 控制中心概述
  • 性能辅助设计

拖拽器实现

拖拽器是可视化搭建引擎的外围模块, 也是用来解决上述提到的大屏页面特色中的“空间坐标关系”这一问题。 咱们先来看一下实现成果:

![上传中…]()

无关拖拽的技术实现, 咱们能够利用原生 js 实现, 也能够应用第三方成熟的拖拽库, 比方:

  • DnD
  • React-Dragable
  • react-moveable

我之前也开源了一个轻量级自在拖拽库 rc-drag , 成果如下:

无关它的技术实现能够参考我的另一篇文章: 轻松教你搞定组件的拖拽, 缩放, 多控制点伸缩和拖拽数据上报。 大家也能够基于此做二次扩大和封装。

咱们拖拽器的根本原型代码如下:

export default function DragBox(props) {
    const [x, y, config] = props;
    const [target, setTarget] = React.useState();
    const [elementGuidelines, setElementGuidelines] = React.useState([]);
    const [frame, setFrame] = React.useState({
        translate: [x, y],
    });
    React.useEffect(() => {
        setTarget(document.querySelector(".target")!);
    }, []);
    return <div className="container">
        <div className="target">拖拽外部组件, 比方图表/根底组件等</div>
        <Moveable
            target={target}
            elementGuidelines={elementGuidelines}
            snappable={true}
            snapThreshold={5}
            isDisplaySnapDigit={true}
            snapGap={true}
            snapElement={true}
            snapVertical={true}
            snapHorizontal={true}
            snapCenter={false}
            snapDigit={0}
            draggable={true}
            throttleDrag={0}
            startDragRotate={0}
            throttleDragRotate={0}
            zoom={1}
            origin={true}
            padding={{"left":0,"top":0,"right":0,"bottom":0}}
            onDragStart={e => {
                e.set(frame.translate);
                // 自定义的拖拽开始逻辑
            }}
            onDrag={e => {
                frame.translate = e.beforeTranslate;
                e.target.style.transform = `translate(${e.beforeTranslate[0]}px, ${e.beforeTranslate[1]}px)`;
                // 自定义的拖拽完结逻辑
            }}
        />
    </div>;
}

以上只是实现了根本的拖拽性能, 咱们须要对拖拽地位信息做保留以便在预览是实现“所搭即所得”的成果。地位信息会和其余属性对立保留在组件的DSL数据中, 这块在接下来内容中会具体介绍。

对于拖拽器的进一步深刻, 咱们还能够设置参考线, 对齐线, 吸附等, 并且能够在拖拽的不同期间(比方onDragStartonDragEnd)做不同的业务逻辑。这些 Moveable 都提供了对应的api反对, 大家能够参考应用。

物料核心设计

物料核心次要为大屏页面提供“原材料”。为了设计强壮且通用的物料, 咱们须要设计一套规范组件构造和属性协定。并且为了不便物料治理和查问, 咱们还须要对物料进行分类, 我的分类如下:

  • 可视化组件 (柱状图, 饼图, 条形图, 地图可视化等)
  • 润饰型组件 (图片, 轮播图, 润饰素材等)
  • 文字类组件 (文本, 文本跑马灯, 文字看板)

具体的物料库演示如下:

这里我拿一个可视化组件的实现来举例说明:

import React, { memo, useEffect } from 'react'
import { Chart } from '@antv/g2'

import { colors } from '@/components/BasicShop/common'

import { ChartConfigType } from './schema'

interface ChartComponentProps extends ChartConfigType {
  id: string
}

const ChartComponent: React.FC<ChartComponentProps> = ({
  id, data, width, height,
  toggle, legendPosition, legendLayout, legendShape,
  labelColor, axisColor, multiColor, tipEvent, titleEvent,
  dataType, apiAddress, apiMethod, apiData, refreshTime,
}) => {
  useEffect(() => {
    let timer:any = null;
    const chart = new Chart({
      container: `chart-${id}`,
      autoFit: true,
      width,
      height
    })

    // 数据过滤, 接入
    const dataX = data.map(item => ({ ...item, value: Number(item.value) }))
    chart.data(dataX)
    
    // 图表属性组装
    chart.legend(
      toggle
        ? {
          position: legendPosition,
          layout: legendLayout,
          marker: {
            symbol: legendShape
          },
        }
        : false,
    )

    chart.tooltip({
      showTitle: false,
      showMarkers: false,
    })

    // 其余图表信息源配置, 办法雷同, 此处省略
    // ...

    chart.render()

  }, [])

  return <div id={`chart-${id}`} />
}

export default memo(ChartComponent)

以上就是咱们的根底物料的实现模式, 可视化组件采纳了g2, 当然大家也能够应用相熟的echartD3.js等. 不同物料既有通用的 props , 也有专有的 props, 取决于咱们如何定义物料的Schema

在设计 Schema 前咱们须要明确组件的属性划分, 为了满足组件配置的灵活性和通用性, 我做了如下划分:

  • 外观属性 (组件宽高, 色彩, 标签, 展示模式等)
  • 数据配置 (静态数据, 动态数据)
  • 事件/交互 (如单击, 跳转等)

有了以上划分, 咱们就能够轻松设计想要的通用Schema了。 咱们先来看看实现后的配置面板:

这些属性项都是基于咱们定义的schema配置项, 通过 解析引擎 动静渲染进去的, 无关 解析引擎 和配置面板, 我会在上面的章节和大家介绍。
咱们先看看组件的 schema 构造:

const Chart: ChartSchema = {
  editAttrs: [
    {
      key: 'layerName',
      type: 'Text',
      cate: 'base',
    },
    {
      key: 'y',
      type: 'Number',
      cate: 'base',
    },
    ...DataConfig, // 数据配置项
    ...eventConfig, // 事件配置项
    
  ],
  config: {
    width: 200,
    height: 200,
    zIndex: 1,
    layerName: '柱状图',
    labelColor: 'rgba(188,200,212,1)',
    // ... 其余配置初始值
    multiColor: ['rgba(91, 143, 249, 1)', 'rgba(91, 143, 249, 1)', 'rgba(91, 143, 249,,1)', 'rgba(91, 143, 249, 1)'],
    data: [
      {
        name: 'A',
        value: 25,
      },
      {
        name: 'B',
        value: 66,
      }
    ],
  },
}

其中 editAttrs 示意可编辑的属性列表, config 为属性的初始值, 当然大家也能够依据本人的爱好, 设计相似的通用schema

咱们通过以上设计的规范组件和规范schema, 就能够批量且高效的生产各种物料, 还能够轻松集成任何第三方可视化组件库。

动静渲染器实现

咱们都晓得, 一个页面中元素很多时会影响页面整体的加载速度, 因为浏览器渲染页面须要耗费CPU / GPU。对于可视化页面来说, 每一个可视化组件都须要渲染大量的信息元, 这无疑会对页面性能造成不小的影响, 所以咱们须要设计一种机制, 让组件异步加载到画布上, 而不是一次性加载几十个几百个组件(这样的话页面会有大量的白屏工夫, 用户体验极度降落)。

动静加载器就是提供了这样一种机制, 保障组件的加载都是异步的, 一方面能够缩小页面体积, 另一方面用户能够更早的看到页面元素。目前咱们熟的动静加载机制也有很多, VueReact 生态都提供了开箱即用的解决方案(尽管咱们能够用 webpack 自行设计这样的动静模型, 此处为了进步行文效率, 咱们间接基于现成计划封装)。咱们先看一下动静渲染组件的过程:

下面的演示能够轻微的看出从左侧组件菜单拖动某个组件图标到画布上后, 真正的组件才开始加载渲染。

这里咱们以 umi3.0 提供的 dynamic 函数来最小化实现一个动静渲染器. 如果不相熟 umi 生态的敌人, 也不必焦急, 看完我的实现过程和原理之后, 就能够利用任何相熟的动静加载机制实现它了。 实现如下:

import React, { useMemo, memo, FC } from 'react'
import { dynamic } from 'umi'

import LoadingComponent from '@/components/LoadingComponent'


const DynamicFunc = (cpName: string, category: string) => {
  return dynamic({
    async loader() {
      //  动静加载组件
      const { default: Graph } = await import(`@/components/materies/${cpName}`)

      return (props: DynamicType) => {
        const { config, id } = props
        return <Graph {...config} id={id} />
      }
    },
    loading: () => <LoadingComponent />
  })
}

const DynamicRenderEngine: FC<DynamicType> = memo((props) => {
  const { 
  type, 
  config, 
  // 其余配置... 
  } = props
  const Dynamic = useMemo(() => {
    return DynamicFunc(config)
  }, [config])

  return <Dynamic {...props} />
})

export default DynamicRenderEngine

是不是很简略? 当然咱们也能够依据本身业务须要, 设计更简单弱小的动静渲染器。

配置面板设计

实现配置面板的前提是对组件 Schema 构造有一个零碎的设计, 在介绍组件库实现中咱们介绍了通用组件 schema 的一个设计案例, 咱们基于这样的案例构造, 来实现 动静配置面板

由上图能够晓得, 动静配置面板的一个外围因素就是 表单渲染器。 表单渲染器的目标就是基于属性配置列表 attrs 来动静渲染出对应的表单项。我之前写了一篇文章具体的介绍了表单设计器的技术实现的文章, 大家感兴趣也能够参考一下: Dooring可视化之从零实现动静表单设计器

我这里来简略实现一个根底的表单渲染器模型:

const FormEditor = (props: FormEditorProps) => {
  const { attrs, defaultValue, onSave } = props;

  const onFinish = (values: Store) => {
    // 保留配置项数据
    onSave && onSave(values);
  };
  
  const handlechange = (value) => {
    // 更新逻辑
  }

  const [form] = Form.useForm();

  return (
    <Form
      form={form}
      {...formItemLayout}
      onFinish={onFinish}
      initialValues={defaultValue}
      onValuesChange={handlechange}
    >
      {
        attrs.map((item, i) => {
        return (
          <React.Fragment key={i}>
            {item.type === 'Number' && (
              <Form.Item label={item.name} name={item.key}>
                <InputNumber />
              </Form.Item>
            )}
            {item.type === 'Text' && (
              <Form.Item label={item.name} name={item.key}>
                <Input placeholder={item.placeholder} />
              </Form.Item>
            )}
            {item.type === 'TextArea' && (
              <Form.Item label={item.name} name={item.key}>
                <TextArea rows={4} />
              </Form.Item>
            )}
            // 其余配置类型
          </React.Fragment>
        );
      })}
    </Form>
  );
};

如果大家想看更残缺的配置面板实现, 能够参考开源我的项目 H5-Dooring | H5可视化编辑器

咱们能够看看最终的配置面板实现成果:

控制中心概述 & 性能辅助设计

控制中心的实现次要是业务层的, 没有波及太多简单的技术, 所以这里我简略介绍一下。 因为可视化大屏页面展现的信息有些可能是私密数据, 只心愿一部分人看到, 所以咱们须要对页面的拜访进行管制。 其次因为企业外部业务策略需要, 可能会对页面进行各种验证, 状态校验, 数据更新频率等, 所以咱们须要设计一套控制中心来治理。 最根本的就是访问控制, 如下:

性能辅助设计 次要是一些用户操作上的优化, 比方快捷键, 画布缩放, 大屏快捷导航, 撤销重做等操作, 这块能够依据具体的产品需要来欠缺。 大家前期设计搭建产品时也能够参考实现。

可视化大屏前期布局和将来瞻望

为了实现更富裕展示力, 满足更多场景的可视化大屏引擎, 咱们一方面须要进步引擎扩展性, 一方面须要欠缺物料生态, 其次只有与时俱进, 提供更多智能化的场景性能, 比方搭建埋点, 数据预警等, 具体布局如下:

  • 丰盛组件物料, 反对3D组件, 天文空间组件等
  • 搭建埋点, 不便前期对组件进行剖析
  • 实现数据源, 事件机制闭环
  • 反对用户自定义组件

如果大家对可视化搭建或者低代码/零代码感兴趣, 也能够参考我往期的文章或者在评论区交换你的想法和心得。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理