欢迎光临
我们一直在努力

Three.js 3D 中国地图本地预览教程:设计器跑起来后,用 cpolar 发给同事远程验收

Three.js 3D 中国地图本地预览教程:设计器跑起来后,用 cpolar 发给同事远程验收

Three.js 3D 中国地图本地预览与 cpolar 远程验收封面图

本地 3D 地图做出来以后,最尴尬的不是代码跑不起来,而是验收的人不在旁边。截图看不到旋转、缩放、悬停效果;录屏又没法让对方自己点一点。

这篇把链路跑完整:用 Vite 搭一个 Three.js 3D 中国地图预览页,读取 GeoJSON 渲染地图块,再用 cpolar 临时生成 HTTPS 地址,发给同事远程验收。重点是预览和验收,不做后台,不暴露源码目录。

1 什么是 Three.js 3D 地图预览?

这里的 3D 中国地图预览,不是完整 GIS 系统,更像一个设计器预览页:前端读取中国地图 GeoJSON,用 Three.js 把省份区域拉出厚度,形成可旋转、可缩放的 3D 效果。

它适合三个场景:大屏项目前期确认地图风格、产品远程看交互手感、前端单独调地图材质和相机角度。我建议先把地图组件独立跑起来,别一上来接菜单、权限、接口。地图先稳,后面塞进业务系统会轻松很多。

还有一个现实原因:3D 地图的沟通成本比普通页面高。颜色深一点、相机俯视角低一点、区域厚度高一点,截图里看着差别不大,真放到浏览器里旋转时感受完全不同。把预览页单独做出来,大家围绕同一个可访问地址提意见,反馈会具体很多。

2 环境准备:创建 Vite 项目

先确认本机有 Node.js 18 或更高版本:

node -v
npm -v

创建项目并安装依赖:

npm create vite@latest three-china-map-demo — –template vanilla
cd three-china-map-demo
npm install
npm install three d3-geo
npm run dev — –host 0.0.0.0

这里加 –host 0.0.0.0 是为了后面远程访问。只用默认启动方式时,本机能打开,同事从外部访问会卡在监听地址上。

终端看到 http://localhost:5173/ 后,用浏览器打开它。能看到 Vite 默认页,就说明本地预览服务已经起来。

这里有个小提醒:如果你的电脑同时开了多个前端项目,Vite 会自动换到 5174、5175 这类端口。后面配置 cpolar 时要用终端里真实显示的端口,不要死记 5173。本文统一用 5173 演示,是为了让命令前后一致。

Vite 本地服务启动成功并监听 localhost 5173 端口

这张图建议放 Vite 启动成功的终端或默认页截图。若页面打不开,先检查终端里的端口是不是 5173,再确认命令没有被中断。

3 准备中国地图 GeoJSON 文件

Three.js 只负责渲染,地图边界要靠 GeoJSON。把完整中国地图文件命名为 china.json,放到 Vite 的 public 目录:

three-china-map-demo
├── public
│ └── china.json
├── src
│ └── main.js
├── index.html
└── package.json

代码里直接使用 fetch('/china.json') 读取。这里别把文件塞进 src/assets 再绕相对路径,新手很容易在构建路径上踩坑。

GeoJSON 的关键结构是 FeatureCollection 和 features。每个 feature 里有区域名称和坐标:

正式项目里还要注意数据来源。演示阶段可以先用公开示例文件跑通渲染,但团队验收时要统一行政区边界版本,别一边用旧数据,一边拿新设计稿对齐边界。地图类组件最怕“看起来差一点”,这种差异经常不是 Three.js 的问题,而是底图数据版本不同。

{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "name": "示例区域" },
"geometry": {
"type": "Polygon",
"coordinates": [[[116, 39], [117, 39], [117, 40], [116, 40], [116, 39]]]
}
}
]
}

如果控制台报 404,优先检查文件名、目录和访问路径。路径不对时,调材质、相机、灯光都解决不了问题。

4 编写 Three.js 3D 地图页面

先把 index.html 改成最简单的挂载入口:

<div id="app"></div>
<script type="module" src="/src/main.js"></script>

再写 src/main.js。这份代码负责初始化场景、读取 GeoJSON、把经纬度投影成平面坐标,并给每个区域生成带厚度的几何体。

import * as THREE from 'three';
import { geoMercator } from 'd3-geo';
import './style.css';

const app = document.querySelector('#app');
const scene = new THREE.Scene();
scene.background = new THREE.Color('#07111f');

const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, -115, 95);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
app.appendChild(renderer.domElement);

scene.add(new THREE.AmbientLight('#7aa7ff', 1.2));
const light = new THREE.DirectionalLight('#ffffff', 2);
light.position.set(30, -40, 80);
scene.add(light);

const mapGroup = new THREE.Group();
scene.add(mapGroup);
const projection = geoMercator().center([104, 37]).scale(75).translate([0, 0]);

function drawPolygon(coordinates, name) {
const shape = new THREE.Shape();
coordinates.forEach((point, index) => {
const [x, y] = projection(point);
if (index === 0) shape.moveTo(x, -y);
else shape.lineTo(x, -y);
});

const geometry = new THREE.ExtrudeGeometry(shape, { depth: 3, bevelEnabled: false });
const material = new THREE.MeshStandardMaterial({ color: '#1f8cff', metalness: 0.2, roughness: 0.45 });
const mesh = new THREE.Mesh(geometry, material);
mesh.name = name;
mapGroup.add(mesh);
}

function drawFeature(feature) {
const name = feature.properties?.name || '区域';
const { type, coordinates } = feature.geometry;
if (type === 'Polygon') coordinates.forEach(ring => drawPolygon(ring, name));
if (type === 'MultiPolygon') coordinates.forEach(polygon => polygon.forEach(ring => drawPolygon(ring, name)));
}

async function loadMap() {
const response = await fetch('/china.json');
const geojson = await response.json();
geojson.features.forEach(drawFeature);
}

function animate() {
requestAnimationFrame(animate);
mapGroup.rotation.z += 0.002;
renderer.render(scene, camera);
}

window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});

loadMap();
animate();

再创建 src/style.css,让画布铺满屏幕:

html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
background: #07111f;
}

canvas {
display: block;
}

刷新页面后,浏览器里会出现基础 3D 地图。若黑屏,先看控制台:Failed to fetch 查 china.json 路径;坐标报错查 GeoJSON 结构;地图偏移就调 projection.center()、projection.scale() 和相机位置。

如果你想让画面更像大屏,可以先改三处:scene.background 控制背景色,MeshStandardMaterial 控制省份颜色,DirectionalLight 控制高光方向。别一次改十几个参数,改多了很难判断是哪一项影响了最终效果。

Three.js 使用 GeoJSON 渲染 3D 中国地图预览页面

这张图建议放地图已经渲染出来的浏览器截图。重点截出地址栏和地图主体,方便审核人确认本地预览已经跑通。

5 调整预览页:别把半成品发出去

远程验收前,至少确认三件事:画布没有白边,浏览器控制台没有红色报错,地图在常见屏幕尺寸下不会跑出视野。

如果要让同事手动旋转视角,可以启用 OrbitControls:

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

在 animate() 里补一行:

controls.update();

我通常只保留旋转和缩放,不放太多调试按钮。验收链接的目标是看视觉和交互,不是把开发调试台交给所有人。

发出去之前,还可以把浏览器缩放调回 100%,并用无痕窗口打开一次。无痕窗口能避开缓存影响,页面能正常打开,说明你发给同事的体验更接近真实访问。这个动作很小,但能提前发现路径、缓存、端口写错这类低级问题。

6 使用 cpolar 暴露本地 5173 端口

本地页面稳定后,就可以让外部同事访问了。cpolar 在这里负责一件事:把本机 5173 端口映射成公网 HTTPS 地址。

macOS 安装命令如下:

brew tap probezy/core && brew install cpolar
sudo cpolar service install
sudo cpolar service start
cpolar version

Linux 安装命令如下:

curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash
cpolar version

安装后打开本地管理页:

open http://127.0.0.1:9200

Windows 或其他不能执行 open 的环境,直接在浏览器访问:

http://127.0.0.1:9200

Web UI 登录后,桌面环境通常会完成账号绑定。纯命令行环境或绑定失败时,再执行:

cpolar authtoken your_token_here

这里别把 your_token_here 原样复制,要换成账号后台里的真实 token。绑定完成后,开启 HTTP 隧道:

cpolar http 5173

终端会输出公网地址,复制 HTTPS 地址发给同事。验收前先用手机流量打开一次,能打开就说明外网链路已经通了。

这里要把边界说清楚:cpolar 解决的是“外部怎么访问我的本地预览页”,不是替你把开发服务变成生产服务。临时验收结束后,直接关闭隧道或停止 Vite 服务。这样做最稳,也能避免同事过几天还拿旧链接看旧版本。

cpolar 将本机 Vite 5173 端口映射为 HTTPS 链接供同事远程验收

这张图建议放 cpolar 在线隧道列表或终端输出。图里保留协议、本地端口 5173 和 HTTPS 地址,token 不要截进去。

7 远程验收:只开放预览页

发链接时,把验收点写清楚,别只丢一个地址:

3D 中国地图预览地址:https://xxxx.cpolar.top
请重点看:
1. 地图整体视角是否符合大屏风格
2. 旋转、缩放是否顺滑
3. 省份边界和配色是否需要调整

如果公网地址打不开,按顺序查:本机 http://localhost:5173/ 是否正常、Vite 是否使用 –host 0.0.0.0、cpolar 在线隧道是否指向 5173、复制出去的是不是 HTTPS 地址。

免费随机公网地址会在 24 小时内变化,适合短时验收。固定二级子域名需要基础套餐或以上。正式演示要使用发布环境和访问控制,不要把开发机预览服务当成生产站点。

如果同事反馈“能打开但很卡”,先让对方关闭其他占用显卡的页面,再让你这边把地图自动旋转关掉试一次。3D 页面卡顿不一定是隧道问题,模型面数、材质、灯光、设备性能都会影响体验。验收阶段先确认视觉方向,性能优化放到组件接入前集中处理。

8 总结

现在这条链路已经打通:Vite 启动前端预览,Three.js 渲染 3D 地图,GeoJSON 提供边界数据,cpolar 把本地 5173 端口临时变成外网 HTTPS 地址。它适合设计评审、客户演示和同事远程验收。

关键步骤记住这三段:

  • 本地先跑稳:创建 Vite 项目,安装 three 和 d3-geo,确认 localhost:5173 能打开
  • 地图单独验证:把 china.json 放到 public,用 fetch('/china.json') 读取并渲染
  • 远程再开放:确认页面无报错后,用 cpolar http 5173 生成 HTTPS 地址

我更推荐把 3D 地图组件先做成独立预览页。独立页好排错,也更适合远程验收;等视觉、交互和数据口径都确认后,再接入正式项目,返工会少很多。

赞(0)
未经允许不得转载:171主机测评 » Three.js 3D 中国地图本地预览教程:设计器跑起来后,用 cpolar 发给同事远程验收
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址