Engineering a Front-End 3D Industrial Dashboard: From Layered Architecture to Performance Optimization

Smart AI Code2026-09-274 min read

This article is based on a breakdown of the open-source project MF-TurbineMonitor (author fengtianxi001). Full code and live demo: GitHub | Live Demo

MF-TurbineMonitor is a wind turbine data dashboard case study. It uses a tech stack of Vue3 + TS + Vite + Three.js + ECharts + TWEEN + mockjs + autofit.js, with no backend and all data mocked. It integrates 3D equipment, real-time data panels, and disassembly animations into a single dashboard, while keeping the code maintainable, reusable, and performance-controllable. This article is suitable for front-end developers who are building or about to build 3D visualization dashboards, as well as readers who want to understand how Three.js is implemented in industrial scenarios.

1. Overall Architecture: Three Hooks with Layered Decoupling

The project separates the Three.js scene, device business logic, and chart wrapper into three hooks, and connects events through provide/inject:

  • useThree.ts — Three.js scene, renderer, model loading, and render loop
  • useTurbine.ts — wind turbine device business logic (interaction, animation, state management)
  • useEcharts.ts — on-demand ECharts import and data-driven charts

When adapting to other devices, only useTurbine needs to be changed; useThree does not need to be touched. This layered approach is the most worthwhile part of the project to learn from, rather than any single flashy effect.

2. Renderer Configuration for the 3D Scene

The renderer needs to enable alpha and localClippingEnabled; this is a prerequisite for the later clipping animation. Many failed attempts at creating a "cut-open shell" effect miss exactly this configuration line.

For models, the project uses 3 Draco-compressed GLB files:

  • equipment (9 parts)
  • skeleton (two shells split out for color/wireframe)
  • plane (ground)

It retrieves objects by modeling names via getObjectByName, avoiding hard-coded object references. The render loop uses a renderMixins Map to attach custom functions, enabling pluggable rendering extensions.

3. Interaction Implementation: Two-Layer Highlighting and State Machine Convergence

Interactions are divided into click and hover scenarios. The technical approaches differ, but they expose a unified interface externally:

Click interaction: Use Raycaster to pick the target object, then use OutlinePass for outline highlighting.

Hover interaction: Use a custom hexPass to modify material.emissive and achieve a self-illumination effect.

Both technical approaches are unified externally as the selectedObjects interface. The author believes that converging state switching into a setter for selectedObjects (i.e., a state machine) is the most elegant way to write component-based interactions—callers do not need to care about the internal implementation, and only need to set the selected object.

4. Animation Choreography: TWEEN Chain Sequences

Plane clipping animation: Attach clippingPlanes to THREE.Plane, and animate the constant through TWEEN to achieve the "cut-open shell" effect.

Disassembly animation: Part coordinates are centrally configured, interpolating from the COMPOSE state to the DECOMPOSE state, and using TWEEN's chain() method to sequence the animation instead of setTimeout. This makes it controllable and cancelable, and easy to clean up when the component unmounts.

5. 3D Labels: Rendering Vue Components as 3D Objects

3D labels use loadCSS2DByVue to render Vue components into DOM, then wrap them as CSS2DObject and mount them in the scene. This allows reuse of Vue templates, styles, and event handling, avoiding manual DOM operations in Three.js.

6. Data and Charts

  • Dynamic data is generated with mockjs; static statistics are hard-coded.
  • ECharts is imported on demand and wrapped in useEcharts; the 7 panels do not reinvent the wheel.
  • The details panel uses computed(() => CONFIG[props.name]) to achieve data-driven switching.

7. Performance and Deployment

  • Use autofit.js for 1920×1080 proportional scaling.
  • Font splitting, imagemin compression, and manualChunks dependency splitting all target first-screen speed.
  • Build output goes to the docs/ directory, and with base:'./', GitHub Pages hosting is available at zero cost.

8. Three Key Engineering Details

  1. Use chain for animations, not setTimeout: Controllable and cancelable, avoiding animation buildup and memory leaks.
  2. Call geometry/material.dispose() when components unmount: Not releasing GPU memory will make performance progressively worse.
  3. The selectedObjects setter is the state machine: Callers do not need to manage the internal implementation, reducing coupling between components.

9. Verification and Secondary Development

If you want to do secondary development based on this project, it is recommended to verify in the following order:

  1. First run the GitHub Pages live demo to confirm the overall visual effect and interaction smoothness.
  2. Clone the project locally and confirm that the development environment (Vue3 + TS + Vite + Three.js) can build normally.
  3. Read the source code in the order useThree → useTurbine → useEcharts to understand the layering boundaries.
  4. Try replacing useTurbine with the business logic of another device to verify the reusability of the architecture.
  5. Check the manualChunks configuration before the production build to ensure the dependency splitting strategy is reasonable.

Notes:

  • All project data is mocked. When connecting real data, you need to replace the mockjs parts yourself.
  • Model files use Draco compression, so ensure that Three.js's DRACOLoader is configured correctly.
  • GitHub Pages hosting is limited by repository visibility; production deployment requires additional configuration.