跪拜 Guibai
← All articles
Frontend · Three.js · Data Visualization

A Reusable 3D Pipeline Dashboard: Map, Topology, and Spatial Flow in Three.js

By Strayer ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Dashboard projects often collapse under their own complexity when transparency sorting, camera controls, and multi-module animation states collide. This demo packages five concrete, battle-tested patterns—the net-zero camera sway, global-direction pipe offset, three-stage render order, combinatorial preset switching, and the orchestrator module split—that can be lifted individually into any Three.js visualization without adopting the whole framework.

Summary

Built on real Handan city GeoJSON and 136 pipeline segments, this Three.js and ECharts demo layers a glowing topology network onto a geographic base map. Supply and return pipes run as parallel Catmull-Rom curves, offset by a global direction vector to avoid self-intersection at bends, while particles stream in opposite directions along each path. A three-layer pipe construction—solid inner, semi-transparent outer glow, and flowing particles—uses explicit renderOrder and depthWrite:false to fix transparency sorting across the scene.

A camera micro-sway system coexists with OrbitControls by undoing the previous frame's offset before the controls update, then reapplying a new sway, creating a net-zero loop that never corrupts user drag state. The high-altitude compute engine switches between four visual presets by destroying and rebuilding its entire group, with one preset cycling through polyhedral morphs on each scan pulse.

The architecture follows a strict orchestrator pattern: SceneManager owns the stage and dispatches per-frame updates to self-contained modules like NetworkBuilder, MapBuilder, and ComputeEngine, each holding references to scene, camera, and controls but managing its own meshes and animations independently.

Takeaways
Supply pipes are chained from discrete segments by tracing connId relationships, then smoothed with CatmullRomCurve3 at tension 0.3.
Return pipes reuse the supply curve but offset globally using the start-to-end direction vector, avoiding the self-intersection that point-by-point normal offsets cause at bends.
Each pipe is three layers: an opaque inner tube at renderOrder 5, a semi-transparent outer glow shell at renderOrder 10, and particles at renderOrder 5, all with depthWrite:false to prevent semi-transparent objects from occluding each other.
Camera micro-sway undoes the previous frame's offset before OrbitControls.update runs, then applies a new sway; this keeps the controls' internal state clean and pauses sway during user interaction.
The compute engine switches visual presets by disposing the entire engine group and rebuilding it, preventing mesh and particle leaks from old schemes.
Breathing animations use smootherstep (t³(6t² - 15t + 10)) instead of raw sin, producing zero-derivative endpoints that eliminate abrupt transitions.
Picking uses invisible TubeGeometry proxies (opacity 0) on each pipe segment, hit-tested with a Raycaster that traverses up to the ancestor carrying userData.
Conclusions

The global-direction offset for parallel pipes is a deliberate trade-off: it sacrifices physical accuracy at sharp bends for guaranteed visual stability, which is the right call for a dashboard where no one measures pipe spacing.

Undoing camera sway before the controls update, rather than trying to integrate sway into the controls themselves, is a cleaner separation of concerns—OrbitControls never knows sway exists, so its damping and state machine remain untouched.

The three-stage render order with universal depthWrite:false is effectively a manual painter's algorithm for a scene where WebGL's default transparency sorting would fail; it works because the scene's depth complexity is known and fixed.

Disposing and rebuilding the entire engine group on preset switch is brute-force but eliminates an entire class of bugs where lingering meshes, particles, or labels from the old preset contaminate the new one.

Concepts & terms
CatmullRomCurve3
A Three.js curve type that passes through all control points and accepts a tension parameter; lower tension (0.3 here) produces tighter bends, higher tension looser curves.
smootherstep
An easing function t³(6t² - 15t + 10) whose first and second derivatives are zero at t=0 and t=1, producing motion that starts and stops with no velocity or acceleration discontinuity—visually smoother than a sine wave for pulsing animations.
UnrealBloomPass
A post-processing pass from the Three.js EffectComposer that extracts bright areas of the scene (above a threshold), blurs them, and adds the result back, creating a glow around emissive materials.
renderOrder
A Three.js Object3D property that overrides the default depth-sorted draw order; objects with higher renderOrder are drawn later, appearing on top regardless of their actual depth, critical for controlling transparency layering.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗