extrusion-3d
本文回答:
fill-extrusion建筑物 3D 拉伸与动画。
一、基础
map.addSource("bldg", {
type: "geojson",
data: featureCollectionWithHeight
});
map.addLayer({
id: "bldg-3d",
type: "fill-extrusion",
source: "bldg",
paint: {
"fill-extrusion-color": ["get", "color"],
"fill-extrusion-height": ["get", "height"],
"fill-extrusion-base": ["get", "base"], // 起始高度
"fill-extrusion-opacity": 0.9
}
});
需要 map.getPitch() > 0(或构造时 pitch: 60)才能看到 3D 效果。
二、vjmap 封装
new vjmap.FillExtrusion({
data: { type: "FeatureCollection", features: [...] },
fillExtrusionColor: ["get", "color"],
fillExtrusionHeight: ["get", "height"],
fillExtrusionBase: 0,
fillExtrusionOpacity: 0.85
}).addTo(map);
三、Draw 内的 extrusionHeight
对 Draw 里的多边形实体,设置 extrusionHeight 自动变 3D:
draw.setFeatureProperty(featureId, "extrusionHeight", 100); // 引擎墨卡托"米";按 CAD 长度给高度用 map.geoLengthToHeight(cadLen)
// 取消
draw.setFeatureProperty(featureId, "extrusionHeight", undefined);
四、zoom / pitch 联动
当 zoom 小的时候隐藏 3D(性能):
map.setLayerZoomRange("bldg-3d", 14, 22);
或用表达式:
"fill-extrusion-opacity": [
"interpolate", ["linear"], ["zoom"],
13, 0,
14, 1
]
五、动画 —— 高度 grow
let t = 0;
function animate() {
t += 0.02;
map.setPaintProperty("bldg-3d", "fill-extrusion-height", [
"*", ["get", "height"], Math.min(t, 1)
]);
if (t < 1) requestAnimationFrame(animate);
}
animate();
六、光照 light
map.setLight({
anchor: "viewport",
color: "#fff8e0",
intensity: 0.6,
position: [1.15, 210, 30] // radial, azimuth, polar
});
anchor: "map":光源随地图旋转。anchor: "viewport":光源固定到屏幕。
七、阴影效果(高度差分层)
vjmap 的 vjmapext.fx 有 shadow / glow 等 GPU 效果(属于 mapext,不在 vjmap core)。详见 mapext 文档。
八、点击拾取
map.on("click", "bldg-3d", (e) => {
const h = e.features[0].properties.height;
alert(`高度:${h} 米`);
});
map.on("mouseenter", "bldg-3d", () => map.getCanvas().style.cursor = "pointer");
map.on("mouseleave", "bldg-3d", () => map.getCanvas().style.cursor = "");
九、与 CAD 图结合
CAD 面状图层(如 POLYGON 实体)可以通过 svrStyleVar 或 svc.conditionQueryFeature(矢量瓦片模式,见 backend/query-sql)取得高度属性,再做 fill-extrusion。
十、常见坑
- pitch=0 看不到 3D:必须设
pitch: 60之类。 - 顶层 fill 遮住底层:多个 fill-extrusion 叠加会有深度冲突,统一放同一图层。
- height 单位是引擎的墨卡托"米",不是 CAD 单位:GeoProjection 把整张 CAD 图映射到整个墨卡托世界,高度值与 CAD 长度的比例取决于图的范围(
高度 = CAD 长度 × 赤道周长 / 图宽),简单/1000换算不对。要"高度 = CAD 里的 L"用map.geoLengthToHeight(L);要"高度 = 当前 zoom 下 N 像素"用map.pixelToHeight(N, map.getZoom())。 - 移动时卡:
fill-extrusion-pattern用贴图很慢,尽量用纯色。 - 透明度叠加怪异:
fill-extrusion-opacity< 1 时 depth sorting 不完美。 - 按
type找层会撞上 Draw 内置层:Draw 控件自带gl-draw-fill-extrusion-inactive/-static层(fill-extrusion-opacity恒 0.9)。用getStyle().layers.find(l => l.type === "fill-extrusion")找到的多半是它而不是你的层——vjmap.FillExtrusion生成的层名是custom_fillExtrusion_layer_<rand>、源名custom_fillExtrusion_<rand>,按layerId/sourceId匹配。 FillExtrusion的 options 键是 camelCase(fillExtrusionOpacity),内部 kebab 化进paint;getter/setter 委托map.getPaintProperty/map.setPaintProperty(回读值建议Number()归一后再严格比较)。