Demo Address:
V6 visual large screen editor
A few months ago, I wrote an article about developing a visual large screen building platform from scratch. I briefly outlined some design ideas and effect demonstrations of the visual large screen building platform. In this article, I will introduce the implementation principle in detail on the topic of how to design a visual large screen building engine.
In keeping with my usual writing style, I will outline the article below so that you can read and study it selectively and efficiently:
- Learn about data visualization quickly
- How to design a universal large screen building engine
-
The core functions of the large screen building engine are realized
- Drag and drop implementation
- Material Center Design
- Dynamic renderer implementation
- Configuration panel design
- Control Center Overview
- Functional aid design
- Visualization of large screen post planning and future outlook
You can easily navigate according to the article on the right, and quickly locate the position you want to see. Next, we begin to enter the body.
Learn about data visualization quickly
When it comes to data visualization, I think you have had some contact with it. From the technical level, the most intuitive one is the front-end visualization framework, such as:
- echart
- antv
- Chart.js
- D3.js
- Vega
These libraries make it easy to create visual diagrams.
From the practical point of view, its main significance is to help users better analysis and expression of data. So when it comes to data visualization, more is to deal with all kinds of charts, through the data -> chart combination -> visual page this business process, constitutes the topic that we want to study today – design visual large screen to build the engine.
How to design a universal large screen building engine
The word “engine” may be a bit of a buzzword. In fact, in the Internet technology, we often hear all kinds of related terms, such as “browser rendering engine”, “rules engine”, “image recognition engine”, etc. I think the essence of “engine” is to provide a set of reliable mechanism. Provide a steady stream of productivity to the system. So what we’re talking about today is a “visual big screen building engine,” which is essentially a building mechanism that allows us to design complex visual pages.
In order to facilitate your understanding of visual construction, I present two visual large-screen pages to analyze the components of the visual large-screen with you:
Of course, the content and form displayed by the large screen in practical application are far more complex than this. We can extract two intuitive features of the large screen page from the above figure:
- Visual component set
- Spatial coordinate relation
Because we’re visualizing big screens as pages, HTML, there’s another feature: events/interactions. In conclusion, we have summarized the essential elements of a large visual screen:
As long as we fully understand the composition and characteristics of the visual large screen, we can better design the visual large screen construction engine. Based on the above analysis, I designed a basic engine architecture diagram:
I’m going to take you through the process of dismantling and implementing the building engine above.
The core functions of the large screen building engine are realized
As the saying goes: “good dismantling is half the success”, any complex task or system, as long as we can be dismantled into a lot of small sub-modules, can be well solved and implemented (learning is the same).
Let’s tackle the core submodules of the base engine one by one:
- Drag and drop implementation
- Material Center Design
- Dynamic renderer implementation
- Configuration panel design
- Control Center Overview
- Functional aid design
Drag and drop implementation
The dragger is the core module of the visual building engine, and it is also used to solve the problem of “spatial coordinates” in the features of the large screen mentioned above. Let’s look at the implementation effect first:
! [Uppass…] (a)
For technical implementation of drag and drop, we can use native JS implementation, can also use third-party mature drag and drop libraries, such as:
- DnD
- React-Dragable
- react-moveable
I also open-source a lightweight free drag library, rc-drag, which looks like this:
For the technical implementation of this, see my other article: Easily teach you how to drag and drop components, zoom, multi-control point scaling, and drag and drop data reporting. You can also do secondary extension and encapsulation based on this.
The basic prototype code for our dragger is as follows:
export default function DragBox(props) { const [x, y, config] = props; const [target, setTarget] = React.useState(); Const [elementGuidelines, setelementGuidelines] = Reaction. 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); // Custom drag start logic}} onDrag={e => {frame.translate = e.beforetranslate; e.target.style.transform = `translate(${e.beforeTranslate[0]}px, ${e.beforeTranslate[1]}px)`; // Customize drag-end logic}} /> </div>; }
The above is just the basic drag and drop function, we need to save the drag and drop location information in order to achieve the “what you build is what you get” effect in the preview. The location information is stored in the component’s DSL data along with other attributes, which will be covered in more detail in the next section.
To further deepen the dragger, we can set guides, alignment lines, snap, etc., and we can do different business logic at different times of the drag-and-drop (such as onDragStart and onDragEnd). These Moveable all provide corresponding API support, you can refer to use.
Material Center Design
The material center mainly provides “raw materials” for large screen pages. In order to design robust and versatile materials, we need to design a standard set of component structures and attribute protocols. In addition, for the convenience of material management and inquiry, we also need to classify materials. My classification is as follows:
- Visualization components (bar charts, pie charts, bar charts, map visualization, etc.)
- Decorative components (images, rotated images, embellished material, etc.)
- Text components (text, text running lights, text Kanban)
The specific material storehouse is demonstrated as follows:
Here I’ll take a visual component implementation as an example:
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: Const dataX = data.map(item => ({... Item, value: Number(item.value)})) Chart. Data (dataX) { position: legendPosition, layout: legendLayout, marker: { symbol: legendShape }, } : False,) chart. Tooltip ({showTitle: false, showMarkers: false,}) // Use the same tools as other chart sources (}). chart.render() }, []) return <div id={`chart-${id}`} /> } export default memo(ChartComponent)
The above is the realization mode of our basic materials, the visual components use G2, of course, you can also use familiar echart, d3.js, etc. Different materials have both general props and specific props, depending on how we define the material Schema.
Before designing the Schema, we need to make clear the attribute division of components. In order to meet the flexibility and versatility of component configuration, I made the following division:
- Appearance attributes (component width and height, color, label, presentation mode, etc.)
- Data configuration (static data, dynamic data)
- Events/interactions (e.g. clicks, jumps, etc.)
With this partition in place, we can easily design the generic Schema we want. Let’s take a look at the configuration panel after implementation:
These attributes are dynamically rendered using the parse engine based on the schema configuration items defined by us. The parse engine and configuration panel are described in the following sections. Let’s first look at the component’s schema structure:
Const Chart: ChartSchema = {editAttrs: [{key: 'layerName', type: 'Text', cate: 'base',}, {key: 'y', type: 'Number, cate:' base '},... DataConfig, / / data configuration items... eventConfig, configuration items] / / events, config: {width: 200, height: 200, zIndex: 1, layerName: 'bar ', labelColor: 'rgba(188,200,212,1)', //... Other configuration initial values multiColor: [' rgba (91, 143, 249, 1) ', 'rgba (91, 143, 249, 1)', 'rgba (91, 143, 249, and 1)', 'rgba (91, 143, 249, 1)'], data: [{name: 'A', value: 25,}, {name: 'B', value: 66,}],}},
Where editAttrs represents the list of editable attributes, config is the initial value of the attribute, of course, you can also design a similar general schema according to your own preferences.
Through the standard components and standard schemas designed above, we can produce all kinds of materials in bulk and efficiently, and can easily integrate any third party visual component library.
Dynamic renderer implementation
We all know that having too many elements on a page affects the overall loading speed of the page because the browser needs CPU/GPU to render the page. For visualization page, each visual components need $render large amount of information, it will no doubt caused no small impact the performance of the page, so we need to design a mechanism to asynchronous loading component to the canvas, rather than a one-time load dozens of hundreds of components (this page will have a lot of bad time, The user experience is extremely poor).
Dynamic loaders provide a mechanism to ensure that components are loaded asynchronously, reducing page size and allowing users to see page elements earlier. There are a lot of dynamic loading mechanisms we are familiar with, and both the Vue and React ecosystems provide solutions out of the box (although we can design our own dynamic model in Webpack, and here we simply encapsulate it from a ready-made solution for the purpose of writing more efficiently). Let’s first look at the process of dynamically rendering components:
The above demonstration shows that after dragging a component icon onto the canvas from the components menu on the left, the actual component starts to load the rendering.
Here we use the dynamic function provided by UMI 3.0 to minimize the implementation of a dynamic renderer. If you are not familiar with umi ecology, don’t worry, after reading my implementation process and principle, you can use any familiar dynamic loading mechanism to implement it. The implementation is as follows:
import React, { useMemo, memo, FC } from 'react' import { dynamic } from 'umi' import LoadingComponent from '@/components/LoadingComponent' const (cpName: String, Category: String) => {return dynamic({async Loader () {const {default: Graph } = await import(`@/components/materies/${cpName}`) return (props: DynamicType) = BB0 {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
Isn’t that easy? Of course, we can also design more complex and powerful dynamic renderer according to our own business needs.
Configuration panel design
The premise of the realization of configuration panel is to have a systematic design of component Schema structure. In the introduction of component library implementation, we introduced a design case of general component Schema. Based on such case structure, we realized dynamic configuration panel.
As you can see from the figure above, one of the core elements of the dynamic configuration panel is the form renderer. The purpose of the form renderer is to dynamically render the corresponding form items based on the Attrs property configuration list. I wrote a previous article detailing the technical implementation of a form designer. For those interested in this article, see Dooring Visualization Implementing a Dynamic Form Designer from Zero.
Let me simply implement a basic form renderer model:
const FormEditor = (props: FormEditorProps) => { const { attrs, defaultValue, onSave } = props; Const onFinish = (values: Store) = bb0 {const onFinish = (values: Store) = bb0; }; Const handleChange = (value) = bb0 {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 >)} / / other configuration type < / React fragments >); })} </Form> ); };
If you want to see more complete configuration panel, you can refer to the open source project H5 – Dooring | H5 visual editor
We can look at the final configuration panel implementation:
Overview of Control Center & Functional Aided Design
The implementation of the control center is mainly at the business level and does not involve too much complex technology, so I will briefly introduce it here. Because some of the information displayed on the visual large screen page may be private data, and only some people want to see it, we need to control the access to the page. Secondly, due to the internal business strategy needs, pages may be verified, state verification, data update frequency, etc., so we need to design a set of control center to manage. The most basic is access control, as follows:
Functional-aided design is mainly the optimization of some user operations, such as shortcut keys, canvas scaling, large screen fast navigation, undo redo and other operations, which can be improved according to specific product requirements. We can also refer to the realization of the design and construction of products in the later stage.
Visualization of large screen post planning and future outlook
In order to achieve a visualized large-screen engine that is more expressive and can meet more scenes, we need to improve the engine’s expansibility on the one hand, improve the material ecology on the other hand, and provide more intelligent scene functions as long as we keep up with The Times, such as building buried points and data early warning, etc. Specific planning is as follows:
- Enrich component materials, support 3D components, geospatial components, etc
- Buried points are set up to facilitate the analysis of components in the later stage
- Realize the data source, event mechanism closed loop
- Support for user-defined components
If you are interested in visual scaffolding or low code/zero code, please refer to my previous articles or share your thoughts in the comments section.