Frontend Development 15 min read

Design and Implementation of a General‑Purpose Data Visualization Big‑Screen Platform

This article presents the overall design, technology selection, architecture, and core implementation details—including adaptive layout, component development workflow, drag‑and‑drop handling, and state push mechanisms—of a low‑code, reusable big‑screen data visualization platform built with React and modern front‑end visualization libraries.

Architecture Digest
Architecture Digest
Architecture Digest
Design and Implementation of a General‑Purpose Data Visualization Big‑Screen Platform

The article introduces the motivation behind building a generic big‑screen data visualization platform, aiming to lower the development threshold for GUI applications, improve production efficiency, and meet growing user demands for real‑time, multi‑dimensional data displays in various scenarios such as exhibitions and conferences.

Quick Overview of Visualization Big Screens

Data visualization transforms large, complex datasets into intuitive charts and dashboards, providing richer information density than raw data. Big screens differ from traditional BI dashboards by focusing on large‑format projection, dynamic visual effects, and multi‑angle monitoring.

Design Ideas

Technical Selection : The front‑end stack includes React (full family), visualization frameworks ECharts, DataV‑React, and D3.js for fine‑grained customizations, a drag‑and‑drop library (dnd‑kit), and a grid layout plugin (React‑Grid‑Layout) for flexible positioning.

Architecture : The platform consists of four subsystems – Visual Material Center (standard DSL for components), Canvas Editor (core low‑code builder supporting layout, interaction, and data configuration), Data Center (connectors for MySQL, ClickHouse, Elasticsearch, Presto, etc.), and Management Center (template management, publishing, access control). The overall architecture is illustrated in the original diagram.

Core Feature Implementations

1. Adaptive Layout : Two approaches were evaluated – using viewport units (vh, vw, rem) with media queries, and a //font.js + rem script that dynamically calculates the root font size based on window size. Sample code:

//vw vh units w3c official explanation
//vw: 1% of viewport’s width, vh: 1% of viewport’s height
// Example: design width 1920px => 1vw = 19.2px, set html font-size to 100px (5.208vw = 100px)
body,html { font-size: 5.208vw; }
// Listen to resize/orientationchange and recalculate root font size
(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);

When third‑party plugins use fixed pixel units, CSS transform: scale() is applied based on container size changes, offering scaling modes such as equal‑height, equal‑width, and full‑screen. The scaling logic is:

useEffect(() => {
  const wR = boxSize.width / viewWidth;
  const hR = boxSize.height / viewHeight;
  setBgScaleRatio(wR);
  setBgHeightScaleRatio(hR);
}, [boxSize, viewWidth, viewHeight]);

const getScale = (proportion, x, y) => {
  if (proportion === 'radioWidth') return `scaleX(${x})`;
  if (proportion === 'radioHeight') return `scaleY(${y})`;
  return `scale(${x}, ${y})`;
};

2. Generic Component Development Process : A component consists of the component body, a schema‑based configuration DSL, and definition metadata (type, hierarchy, initial size). The DSL defines editable properties, UI controls, dependencies, and data bindings, enabling dynamic form generation from simple Excel inputs.

{
  headerGroupName: '公共配置',
  headerGroupKey: 'widget',
  name: '标题名称',
  valueType: ['string'],
  ui: ['input'],
  dependencies: ['widget,title.show,true'],
  dataV: { key: 'title.text', value: '' }
}

Component wrappers for ECharts and DataV‑React are provided. Example for ECharts initialization and event binding:

const renderNewEcharts = () => {
  const echartObj = updateEChartsOption();
  bindEvents(echartObj, onEvents || {});
  if (typeof onChartReady === 'function') onChartReady(echartObj);
  echartObj.resize();
};

const bindEvents = (instance, events) => {
  const _bindEvent = (eventName, func) => {
    instance.on(eventName, param => func(param, instance));
  };
  for (const eventName in events) {
    if (Object.prototype.hasOwnProperty.call(events, eventName)) {
      _bindEvent(eventName, events[eventName]);
    }
  }
};

const dispose = () => {
  if ($chartEl.current) {
    clear($chartEl.current);
    (echartsLib || echarts).dispose($chartEl.current);
  }
};

DataV component skeleton:

const DataV: React.FC
= (props) => {
  const { config } = props;
  const [renderCounter, setRenderCounter] = useState(0);
  const $dataVWarpEl = useRef(null);
  const $componentEl = useRef(null);

  useEffect(() => {
    const resizefunc = debounce(() => { $componentEl.resize(); }, 500);
    addResizeListener($dataVWarpEl.current, resizefunc);
    return () => { removeResizeListener($dataVWarpEl.current, resizefunc); };
  }, []);

  return (
);
};

3. Drag‑and‑Drop Enhancements : The default React‑Grid‑Layout lacks free‑form layout and multi‑axis dragging. The implementation rewrites drag events, adds lock‑aspect‑ratio, rotation, and opacity controls, and updates Z‑index dynamically:

// 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);
  }
}
// Increment Z‑index on each drag
const getAfterMaxZIndex = useCallback(i => {
  if (i === curDragItemI) return;
  setCurDragItemI(i);
  setMaxZIndex(maxZIndex => maxZIndex + 1);
  return maxZIndex;
}, []);

4. State Push via WebSocket : A long‑lived WebSocket connection with heartbeat and reconnection logic enables real‑time updates and offline management of deployed big screens.

The article concludes with a preview of the platform’s visual effects and a summary that the current design satisfies core big‑screen requirements, while future work includes expanding component libraries, building a material ecosystem, and adding 3D/effect rendering capabilities.

FrontendReactLow-codeData VisualizationEChartsbig screen
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.