一、先拆口播:视频说它是「3D 大屏模板」,代码里其实是「一套 R3F 工程」

视频口播 5 条要点(定位 / 核心功能 / 技术栈 / 开发体验 / 获取方式)我全实查了,但读源码之后口播其实把最值钱的部分漏了——它不是「一个 demo 模板」,而是4 个完全独立的 3D 大屏 demo(Demo0~3)+ 一套可复用的组件/hooks 底盘(源码 7,937 行 · 19 个 map/*.tsx · 14 个 panel/chart*.tsx · 5 个通用 hooks/),每个 demo 都换过 3D 表达(Demo0 地形 + 侧边扫光 · Demo1 城市 3D 柱 + 热力图 + 云 · Demo2 光束 + 镜面 + 地形 + 自定义 shader · Demo3 完整 3D 建筑模型 + postprocessing Bloom + ToneMapping + HDR 环境光)。你要做的选型判断不是「用不用它」,而是「从 4 个 demo 里挑一个当起点,改数据 + 换地理 + 调 Leva 面板」——这条视频完全没说。

二、核心机制(源码实证,不是抄 README)

下面这几条都是只有 clone 下来读 src/ 才能写出来的,README 一个字没提。

① 视频说的「侧边扫光」= 一整段 CatmullRom 曲线滑窗 + onBeforeCompile 注入 GLSL。 打开 Demo0/map/flyLine.tsx(126 行):先拿 sc_outline.json(四川省轮廓 GeoJSON)走 d3-geo 投影,把每个轮廓顶点变 3D 向量 (x, -y, 0.49),再喂给 new CatmullRomCurve3(v3Arr).getSpacedPoints(800) 生成 800 个点。每帧 useFrame 里:index = (index + 60 * delta) % total(沿轮廓匀速前进)→ 取 50 点窗口 → 如果 end > total(越过尾端)就 pts.slice(start).concat(pts.slice(0, overflow)) 把首尾两段拼起来保持曲线连续(这是"扫光绕一圈不闪断"的关键)→ 每 200 点重新 setFromPoints。
更狠的是最后这段——它不是画线,是 <points>(点云)+ onBeforeCompile 直接改 Three.js 内建 pointsMaterial 的 GLSL:

// 每个点带一个 percent(中点=1、两端=0,形成三角衰减)
attribute float percent;
gl_PointSize = percent * size;               // 顶点着色器:中点最大、两端最小
// 片元着色器:把方形点渲成圆形柔光
float r = distance(gl_PointCoord, vec2(0.5));
float alpha = pow(1.0 - r / 0.5, 6.0);       // 6 次幂 = 硬边缘 + 高斯衰减
gl_FragColor = vec4(gl_FragColor.rgb, gl_FragColor.a * alpha);

所以「扫光」的真相是:800 点轮廓上每帧滑一个 50 点窗口 → 每点按位置生成大小(三角函数包络)→ 片元再按到点心的 6 次幂衰减渲成光斑。这套东西你套个普通 <line> 是完全做不出来的。

②「3D 地形起伏」= 三张自定义贴图 + 按 bbox 重算 UV。 Demo0/map/baseMap.tsx 把四川 GeoJSON 的每个 feature 走 d3-geo 投影成 THREE.Shape,喂给 ShapeGeometry。表面是 meshStandardMaterial 叠了 diffuse(sc_map.png)+ normal(sc_normal_map.png)+ displacement(sc_displacement_map.png)三张贴图,metalness 0.2 / roughness 0.5。而 shape.tsx(45 行)里有个很关键的 useLayoutEffect:按这个 geometry 的 bbox 重算每个顶点的 UV(u=(x-min.x)/width),让三张贴图能跟地理轮廓精确对齐——不是拿 Three.js 默认的 UV 直接糊上去。

③「3D 热力图」根本不是 ECharts——是自研 GLSL。 Demo1/map/heatmap.tsx 用了 keli-heatmap.js(一个 2025 年出的轻量热力图库)同时渲两个 canvas:heatmap(彩色:蓝→绿→黄→红渐变)+ greymap(灰度:0=黑 1=白,当高度图用);两个 canvas 各变 CanvasTexture,喂给 shaderMaterial 自写的 vertex/fragment:
height = z_scale * texture2D(greyMap, uv).a → 灰度值直接决定这个顶点高度(热力越强的地方 3D 越高),gl_FragColor = vec4(u_color, u_opacity) * texture2D(heatMap, uv) → 彩色层再乘上去。这就是「热力图在 3D 地图上鼓起」的完整机制。

④ 响应式不是 media query——是 autofit.js 整页 scale。 src/components/autoFit.tsx(29 行)就一行核心:autofit.init({ el: "#autofit_xxx" })。autofit.js 的思路是按设计稿定死 1920×1080 尺寸,浏览器按视口宽度比例整体 scale 整个 Wrapper(不是逐个改字号、不写一行 @media)。这跟「大屏开发圈」惯用 rem + postcss-pxtorem 或 vw/vh 方案完全不同——代价是字体跟着整体缩放不清晰,收益是任何分辨率下版面 1:1 不变形。

⑤ 4 个 demo 各带一个「招牌特性」,选型先看这里:

Demo招牌 3D 效果读哪个文件
Demo0地形起伏 + 侧边扫光(上面①②④)+ 4 张 ECharts(chart1~4)map/flyLine.tsx · map/shape.tsx
Demo1城市 3D 柱(map/bar.tsx)+ 热力图(map/heatmap.tsx)+ 云层(map/cloud.tsx 434 行,最复杂)+ 6 张 EChartscityData.ts(数据)· map/scene.tsx
Demo2光束(map/beamLight.tsx)+ 镜面反射(map/mirror.tsx)+ 自定义 shader 扫光(map/shaderMaterial.tsx)+ 6 张 EChartsmap/boundary.tsx · map/cone.tsx
Demo3完整 3D 建筑模型(model.tsx)+ postprocessing(EffectComposer + Bloom + ToneMapping)+ HDR 环境光demo.tsx

三、拿到服务器上真跑了一遍:pnpm 全绿(有结果),npm 那侧挂

「实测」不能止于「它挂了」,得有「做对了它真的能跑 + 有结果」。我把源码拉到自己一台 ECS(OpenCloudOS 9.6 · 2C · 3.6G · Node 24.20.0)上跑全链路,两边对照:

A 侧 · npm(本机 Windows,无 pnpm):装得上、build 挂。 npm install 把 272 个包都装上了,但 npm run build 直接死在类型检查:

$ npm run build
> tsc -b && vite build

src/pages/Demo3/demo.tsx(75,22): error TS2322:
  Property 'disableNormalPass' does not exist on type
  'IntrinsicAttributes & BloomEffectOptions & { ... }'.

根因:仓库只有 pnpm-lock.yaml(lockfileVersion 9.0),pin 的 postprocessing 旧版类型里 <Bloom disableNormalPass /> 能过;npm 无锁,拉了 postprocessing@6.39.5(新类型已把 disableNormalPass 从 BloomEffectOptions 上拆走),tsc 即报 TS2322。「pnpm install 三步跑起来」不是套话——pnpm 是硬依赖。

B 侧 · pnpm(服务器):全链路真绿,输出都在这。 装 pnpm 9(对 lockfileVersion 9.0)走 frozen lockfile:

$ pnpm -v
9.15.9
$ pnpm install --frozen-lockfile
Done in 14.3s using pnpm v9.15.9
$ pnpm run build
✓ built in 1.68s          # tsc -b + vite,退出码 0
$ pnpm preview --port 8417
  ➜  Local:  http://127.0.0.1:8417/sc-datav/

# curl 验证(服务器上执行)
/sc-datav/                  → HTTP 200 · <title>大屏可视化</title>
/sc-datav/assets/index-*.js → HTTP 200
/demo0 ~ /demo3            → 全部 HTTP 200

产物也真的落在盘上:dist/ 共 46M · 42 个文件,含 4 张 demo 预览图(demo_0~3.jpg)、font/、hdr/(Demo3 的环境光贴图)。从头到尾「装 + 构 + 起 + 验证」30 秒内——这就是「不熟悉数据大屏开发、照着改也能上手」的具体含义。

下面是真跑出来、截在 1920×1080 的画面——不是转述、不是网图,是上面 pnpm preview :8417 起来后用无头 Chromium(SwiftShader 软渲染 WebGL)一页页抓下来的:左侧是 Index 首页,后四张正是 /demo0~3 对应页(与 curl 全 200 一一对应)。

sc-datav Index 首页(真跑渲染 1920x1080)
Index 首页 · 实跑 SwiftShader 渲染 · 1920×1080
Demo0 地形+侧边扫光(真跑渲染)
Demo0 · 地形 + 侧边扫光 · 实跑
Demo1 城市3D柱+热力图+云(真跑渲染)
Demo1 · 城市 3D 柱 + 热力图 + 云 · 实跑
Demo2(真跑渲染)
Demo2 · 实跑
Demo3 环境光HDR(真跑渲染)
Demo3 · HDR 环境光 · 实跑
跑完还白捡两个性能注意点(读代码 + 实测交叉确认的):

  • chunk 警告:build 尾报「Some chunks are larger than 500 kB」——sc_outline(四川省轮廓 GeoJSON 数据)单块 811 kB、postprocessing 的 extends 块 869 kB。看 App.tsx 只有 Index 路由是 lazy,Demo0~3 全是急加载,首屏得等这两个大块下完;生产用要么自己拆块、要么把 demo 路由改 lazy。
  • 部署路径写死:vite.config.ts 里 base: "/sc-datav/",挂到子域/子路径时 nginx 要 location /sc-datav/ → dist/,放不了域根,改 base 要重 build。

四、口播 5 条 vs 我实查的对照表(含 1 条修正)

口播实查结论
「换数据直接用」src/assets/sc.json + sc_outline.json 是四川省 GeoJSON 示例数据;Demo1/cityData.ts 是城市数据示例;改这两处 + 改 panel/chart1~4 里的 echarts.setOption 文案就是你自己数据✅ 属实,但「换数据」= 换 3 类文件(GeoJSON / 城市数据 / ECharts option)
「Three.js 3D 地图 + 飞线 + 侧边扫光 + 多图表联动 + 响应式 + Leva 实时调试」全对,只是「响应式」的实现方式是 autofit.js scale(不写 @media),Leva 是 useControls + <Leva collapsed /> 挂到右上✅ 属实(细节已在上文拆)
「支持四川等地理轮廓精确呈现」四川只是模板自带的示例数据(sc.json 就一份),不是「内置一堆省份可切换」。想换别的地区要自备 GeoJSON(作者另仓 sat-hunter 是「地图轮廓贴图下载工具」,README 明链了)⚠️ 口播偏宽,实际是「自带四川示例数据、其他地区要自备」
「React 19 + TS · Three.js/R3F/Drei · ECharts · GSAP · autofit.js」全对;实际版本号(package.json):React 19.1.1 · TS ~5.9.3 · three ^0.183.2 · R3F ^9.4.2 · drei ^10.7.6 · echarts ^6.0.0 · GSAP ^3.13.0 · autofit.js ^3.2.8 · leva ^0.10.1 · Vite 8.0.0 · Zustand ^5.0.8 · postprocessing ^3.0.4;另外口播没提的:d3-geo(投影)、keli-heatmap.js(3D 热力图)、topojson-client、styled-components、react-router 7✅ 属实且更细
「内置 Leva 实时调试面板 · 边调边看」<Leva collapsed /> 一个组件就够;useControls({ ... }) 在任意组件里声明字段就自动出 UI。Demo0 里连「网格显隐 / 单元格颜色 / 分区颜色 / 背景色」四个都在 Leva 里✅ 属实,且比口播说的「面板」更细(Leva 是自动出 UI,不手写表单)

五、数据红线 · 边界 · 什么时候别用它

边界 1 · 它是「模板」不是「引擎」:没有 <SCMap region="北京" /> 这种 API。想换成别的省 / 园区 = 自己准备 GeoJSON + 自己改 map/*.tsx 里对应那段的 projection(feature.geometry.coordinates) 循环。做「通用 3D 地图 SDK」它还差一层抽象。

边界 2 · 4 个 demo 是并列的、不是渐进的:Demo1/2 里的热力图、云、光束、镜面不是「Demo0 之上加功能」,是完全重写的另一套 3D 场景。别指望「从 Demo0 逐步升级到 Demo3」,是「挑一个当起点、把其它 demo 里的组件 import 过来」。

边界 3 · 硬依赖 pnpm,npm 会挂 build(见第三节实测)。仓库只有 pnpm-lock.yaml 没有 package-lock.json;用 npm 装一次必拉进不一致的传递依赖,Demo3 会因 disableNormalPass 挂 TS 类型检查。

边界 4 · 视觉风格偏「大屏」:暗色 + 高对比 + 强光斑。做浅色 / 明亮背景的 dashboard 直接不搭,改起来比换数据还累(meshStandardMaterial 的 metalness/roughness、Leva 里的 cellColor/sectionColor、autofit.js 的 Wrapper 背景色全是围绕暗色设计的)。

什么时候别用它:① 需要「输入任意 GeoJSON 就出 3D 图」的通用引擎——找 antv-l7 / mapbox-gl;② 需要浅色 SaaS 后台——找 shadcn/ui / ant-design;③ 需要「数据实时推、秒级刷新」的作战大屏——它的 ECharts 都是拉一次静态 option,没写 WebSocket 通道,要自己加;④ 团队不熟 React + R3F——学曲线比它本身还长,还不如用 Vue + 2D ECharts 起。

数据红线(2026-09-29 实查):仓库 knight-L/sc-datav(GitHub API:2,342★ / 473 fork / Apache-2.0 / TS / 0 open issues / 创建 2025-11-11 / 最后推送 2026-03-13 / homepage knight-l.github.io/sc-datav);版本号 package.json 原文(React 19.1.1 · TS ~5.9.3 · three ^0.183.2 · R3F ^9.4.2 · drei ^10.7.6 · ECharts ^6.0.0 · GSAP ^3.13.0 · autofit.js ^3.2.8 · leva ^0.10.1 · Vite 8.0.0);6 处源码实证(扫光=flyLine.tsx · 地形 UV=shape.tsx · 3D 热力图=heatmap.tsx · autofit scale=autoFit.tsx · 4 demo 招牌特性对照表 · 数据流 zustand=stores/index.ts)均为 clone 源码 + 逐文件读 实查;「npm install 挂 build / pnpm 才能过」为本机(Windows + Node 22.22.2)实测(272 包 install OK,tsc -b 报 Demo3/demo.tsx:75 disableNormalPass TS2322,根因为 npm 拉了 postprocessing@6.39.5 而 pnpm-lock.yaml pin 的旧版能过);「四川是自带示例数据、非可切换功能」为对口播的修正。

数据红线:本文所有版本号 / 星标 / 文件路径 / 源码引用均 2026-09-29 实查 git clone 得到的 _src_sc-datav/;「npm install 挂 build」为作者本机(Windows + Node 22)实测复现;「服务器 pnpm 全链路实测」为 ECS(OpenCloudOS 9.6 · 2C · 3.6G · Node 24.20.0 · pnpm 9.15.9)实跑:pnpm install --frozen-lockfile 14.3s / pnpm run build 退出码 0(vite 1.68s)/ pnpm preview :8417 / /sc-datav/ 与 demo0~3 全部 HTTP 200 / dist 46M·42 文件;chunk 警告(sc_outline 811kB、extends 869kB)与 base='/sc-datav/' 为 build 输出 + vite.config.ts 实查;「4 demo 招牌特性对照表」由逐 demo 目录读代码归纳;「什么时候别用它」是 4 条工程判断、非作者声明。