内置对象
WebGPU 引擎
本页内置对象(Label / MeshLine / ScreenQuad / Heatmap / TextMesh / TrailLine 等)的内部材质在 render: { engine: "webgpu" } 时会自动切换为 TSL 实现,用法不变。例外:ScreenQuad 的自定义 GLSL fragmentShader/vertexShader/uniforms 选项在 webgpu 下被丢弃(只告警,仅显示 map 纹理),需要自定义着色请改传 material(如 ShadertoyMaterial)。webgpu 引擎下请在 await app.whenReady() 之后再创建,详见 WebGPU 渲染引擎。
Label 文本
通过 createLabel 创建文本。
用法
let label = vjmap3d.createLabel({
lineHeight: 1,
color: "#0ff",
text: "Hello,VJMAP3D!",
anchorPoint: [0.5, 0],
transparent: true,
position: [0, 0, 0]
});
//label.setText("Hello");
//label.position.set(0, 0, 0);
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let label = vjmap3d.createLabel({
lineHeight: 1,
color: "#0ff",
text: "Hello,VJMAP3D!",
anchorPoint: [0.5, 0],
transparent: true,
position: [0, 0, 0]
});
//label.setText("Hello");
//label.position.set(0, 0, 0);
app.scene.add(label);
MeshLine Mesh线
通过 MeshLine 创建MeshLine。
用法
let mesh_line = new vjmap3d.MeshLineGeometry();
mesh_line.setPoints(positions[n], null, false);
let texture = vjmap3d.ResManager.loadTexture(env.assetsPath + "textures/roadline.png");
texture.wrapS = THREE.RepeatWrapping;
let meshline_material = new vjmap3d.MeshLineMaterial({
map: texture,
useMap: true,
color: new THREE.Color(colors[n]),
transparent: true,
resolution: app.commonUniforms.resolution,
time: app.commonUniforms.time,
sizeAttenuation: false,
lineWidth: 20,
depthTest: true,
side: THREE.DoubleSide,
repeat: new THREE.Vector2(1, 1),
speed: new THREE.Vector2(1, 0),
fog: false
});
let trail_mesh = new vjmap3d.MeshLine(mesh_line, meshline_material);
app.scene.add(trail_mesh);
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
},
control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtVecUrl =
"https://t3.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
let provider = new vjmap3d.MapProvider(
[
{
url: tdtVecUrl
},
{
url: tdtAnnoUrl
}
],
{
rootTile: [10, 836, 446],
colorFilter: {
inVerseColor: true,
bright: 1.2,
contrast: 1,
saturation: 1,
monochromeColor: "#4586b6"
}
}
);
let mapviewEnt = new vjmap3d.MapViewEntity({
provider,
baseScale: 100,
});
mapviewEnt.addTo(app);
let res = await vjmap3d.httpHelper.get(env.assetsPath + "json/szmapdata.json");
let linePrimary = res.data.features;
let positions = [];
let colors = [];
for (var i = 0; i < linePrimary.length; i++) {
const item = linePrimary[i];
const points = [];
item.geometry.coordinates.forEach(ln => {
points.push(mapviewEnt.lngLatToWorld(ln));
});
let lineColor = "#ffff00";
if (item.properties.highway === "primary") {
lineColor = "#7eff10";
} else if (item.properties.highway === "tertiary") {
lineColor = "#0eeeee";
} else if (item.properties.highway === "secondary") {
lineColor = "#ffffff";
} else {
lineColor = "#ff0ffe";
}
positions.push(points);
colors.push(lineColor);
}
for (let n = 0; n < positions.length; n++) {
let mesh_line = new vjmap3d.MeshLineGeometry();
mesh_line.setPoints(positions[n], null, false);
//
let texture = vjmap3d.ResManager.loadTexture(env.assetsPath + "textures/roadline.png");
texture.wrapS = THREE.RepeatWrapping;
let meshline_material = new vjmap3d.MeshLineMaterial({
map: texture,
useMap: true,
color: new THREE.Color(colors[n]),
transparent: true,
resolution: app.commonUniforms.resolution,
time: app.commonUniforms.time,
sizeAttenuation: false,
lineWidth: 20,
depthTest: true,
side: THREE.DoubleSide,
repeat: new THREE.Vector2(1, 1),
speed: new THREE.Vector2(1, 0),
fog: false
});
let trail_mesh = new vjmap3d.MeshLine(mesh_line, meshline_material);
app.scene.add(trail_mesh);
}
app.cameraControl.loadState({
cameraTarget: new THREE.Vector3(-40, 0, -30),
cameraPosition: new THREE.Vector3(-40, 8, -30)
})
PathTube 管道
通过 PathTubeEntities 创建管道实体。
用法
let texture = new THREE.TextureLoader().load(env.assetsPath + "textures/diffuse.jpg", () => {
texture.wrapS = THREE.RepeatWrapping;
});
let outMaterial = new THREE.MeshPhongMaterial({
color: 0x7dffeb,
map: texture,
side: THREE.FrontSide
});
let texture2 = new THREE.TextureLoader().load(env.assetsPath + "textures/uv_grid_opengl.jpg", () => {
texture2.wrapS = THREE.RepeatWrapping;
});
let innerMaterial = new THREE.MeshPhongMaterial({
map: texture2,
side: THREE.FrontSide
});
let opts = {
data: [
{
paths: [
[3, 0, 2],
[-10, 0.5, -5],
[-12, 0, 3],
[-5, 2, 4]
],
name: "path1",
},
{
paths: [
[3, 0, 2],
[12, 0, -3],
[15, 3, 4]
],
name: "path2",
},
{
paths: [
[2, 1, 10],
[-1, 0, 11],
[3, 0, 2]
],
name: "path3",
}
],
nodeRadius: 0.5,
innerSideMaterial: innerMaterial,
outerMaterial: outMaterial,
sect: {
shape: "arch"
}
}
let pathTube = new vjmap3d.PathTubeEntities(opts);
pathTube.addTo(app);
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let texture = new THREE.TextureLoader().load(env.assetsPath + "textures/diffuse.jpg", () => {
texture.wrapS = THREE.RepeatWrapping;
});
let outMaterial = new THREE.MeshPhongMaterial({
color: 0x7dffeb,
map: texture,
side: THREE.FrontSide
});
let texture2 = new THREE.TextureLoader().load(env.assetsPath + "textures/uv_grid_opengl.jpg", () => {
texture2.wrapS = THREE.RepeatWrapping;
});
let innerMaterial = new THREE.MeshPhongMaterial({
map: texture2,
side: THREE.FrontSide
});
let opts = {
data: [
{
paths: [
[3, 0, 2],
[-10, 0.5, -5],
[-12, 0, 3],
[-5, 2, 4]
],
name: "path1",
},
{
paths: [
[3, 0, 2],
[12, 0, -3],
[15, 3, 4]
],
name: "path2",
},
{
paths: [
[2, 1, 10],
[-1, 0, 11],
[3, 0, 2]
],
name: "path3",
}
],
nodeRadius: 0.5,
innerSideMaterial: innerMaterial,
outerMaterial: outMaterial,
sect: {
shape: "arch"
}
}
let pathTube = new vjmap3d.PathTubeEntities(opts);
pathTube.addTo(app);
//
for(let n = 0; n < pathTube.entity.childrens.length; n++) {
let ent = pathTube.entity.childrens[n];
ent.add(vjmap3d.EventModule, {
clickHighlight: true,
highlightOpacity: 0.2,
hoverSelect: true,
// @ts-ignore
hoverHtmlText: ent.node.isNode ?
`节点:关联${ent.node.userData.dataIndexs} 条路径`:
opts.data[ent.node.userData.dataIndex].name,
popupOptions: {
anchor: "bottom"
}
});
}
ExtrudeMesh 挤出模型
通过 ExtrudeMesh 创建挤出模型。
用法
let texture = vjmap3d.ResManager.loadTexture(env.assetsPath + "textures/uv_grid_opengl.jpg");
texture.wrapS = THREE.RepeatWrapping;
const material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
map: texture,
transparent: true,
opacity: 1.0
});
// 挤出路径的点位置 转弯半径 转弯分段数 当前点的缩放 取个名字
let paths = [
{
vector3: [-3, 0, 0],
radius: 0.5,
segments: 2,
scale: { x: 1, y: 1 }
},
{
vector3: [3, 0, 0],
radius: 0.5,
segments: 2,
scale: { x: 1, y: 1 }
},{
vector3: [0, 0, 5],
radius: 0.5,
segments: 2,
scale: { x: 1, y: 1 }
}
];
const extrudeMesh = new vjmap3d.ExtrudeMesh(
{
paths: paths, // 自动生成路径的
shapePath:[[-0.5, 0], [0.5, 0], [0.25, 0.5], [-0.25, 0.5], [-0.5, 0]],// 被拉伸成模型的平面路径
closed: false, // 是否闭合路径
},
material
);
app.scene.add(extrudeMesh);
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let texture = vjmap3d.ResManager.loadTexture(env.assetsPath + "textures/uv_grid_opengl.jpg");
texture.wrapS = THREE.RepeatWrapping;
const material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
map: texture,
transparent: true,
opacity: 1.0
});
// 挤出路径的点位置 转弯半径 转弯分段数 当前点的缩放 取个名字
let paths = [
{
vector3: [-3, 0, 0],
radius: 0.5,
segments: 2,
scale: { x: 1, y: 1 }
},
{
vector3: [3, 0, 0],
radius: 0.5,
segments: 2,
scale: { x: 1, y: 1 }
},{
vector3: [0, 0, 5],
radius: 0.5,
segments: 2,
scale: { x: 1, y: 1 }
}
];
const extrudeMesh = new vjmap3d.ExtrudeMesh(
{
paths: paths, // 自动生成路径的
shapePath:[[-0.5, 0], [0.5, 0], [0.25, 0.5], [-0.25, 0.5], [-0.5, 0]],// 被拉伸成模型的平面路径
closed: false, // 是否闭合路径
},
material
);
app.scene.add(extrudeMesh);
// 创建一个平面的道路
let texture2 = vjmap3d.ResManager.loadTexture(env.assetsPath + "textures/road1.png");
texture2.wrapS = THREE.RepeatWrapping;
const material2 = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
map: texture2,
transparent: true,
opacity: 1.0
});
let paths2 = [
{
vector3: [-10, 0, 0],
radius: 0.5,
segments: 6,
scale: { x: 1, y: 1 }
},
{
vector3: [-5, 0, 0],
radius: 0.5,
segments: 6,
scale: { x: 1, y: 1 }
},{
vector3: [-8, 0, 5],
radius: 0.5,
segments: 6,
scale: { x: 1, y: 1 }
}
];
const extrudeMesh2 = new vjmap3d.ExtrudeMesh(
{
paths: paths2, // 自动生成路径的
shapePath:[[-0.5, 0], [0.5, 0]],// 被拉伸成模型的平面路径
closed: false, // 是否闭合路径
},
material2
);
app.scene.add(extrudeMesh2);
ScreenQuad 铺满屏幕的四边形
通过 ScreenQuad 创建。
用法
let shader = await new THREE.FileLoader().loadAsync(env.assetsPath + "shader/fire.frag")
let mat = new vjmap3d.ShadertoyMaterial(app, {
shader: shader,
brightOpacity: 1,
mouseInputPos: [400, 200],
transparent: true,
side: THREE.DoubleSide
});
let screenQuad = new vjmap3d.ScreenQuad(app, {
material: mat
});
app.scene.add(screenQuad.mesh);
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let shader = await new THREE.FileLoader().loadAsync(env.assetsPath + "shader/fire.frag")
let mat = new vjmap3d.ShadertoyMaterial(app, {
shader: shader,
brightOpacity: 1,
mouseInputPos: [400, 200],
transparent: true,
side: THREE.DoubleSide
});
let screenQuad = new vjmap3d.ScreenQuad(app, {
material: mat
});
app.scene.add(screenQuad.mesh);
Heatmap 热力图
通过 Heatmap 创建热力图。
用法
let data = [];
for(let k = 0; k < 100; k++) {
data.push({
point: vjmap3d.randPoint2D({minX: -10, maxX: 10, minY: -10, maxY: 10}),
value: Math.random() * 50
})
}
//
let heatmap = new vjmap3d.Heatmap({
mode3D: false, // 3d模式
heightRatio: 1.0,
bounds: [[-10, -10], [10, 10]],
data: data,
min: 0,
max: 50,
// 可以传一个多边形进行裁剪
clipPolygon: [
[-10,0],
[0, -10],
[10, 0],
[0, 10]
]
})
let mesh = await heatmap.create();
let heatmapEnt = new vjmap3d.MeshEntity(mesh);
heatmapEnt.addTo(app);
示例
二维热力图
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let data = [];
for(let k = 0; k < 100; k++) {
data.push({
point: vjmap3d.randPoint2D({minX: -10, maxX: 10, minY: -10, maxY: 10}),
value: Math.random() * 50
})
}
//
let heatmap = new vjmap3d.Heatmap({
mode3D: false, // 3d模式
heightRatio: 1.0,
bounds: [[-10, -10], [10, 10]],
data: data,
min: 0,
max: 50,
// 可以传一个多边形进行裁剪
clipPolygon: [
[-10,0],
[0, -10],
[10, 0],
[0, 10]
]
})
let mesh = await heatmap.create();
let heatmapEnt = new vjmap3d.MeshEntity(mesh);
heatmapEnt.addTo(app);
heatmapEnt.pointerEvents = true
//
const popup = new vjmap3d.Popup2D({
backgroundColor: "#A0FFA0",
buttonColor: "#f00",
closeOnClick: false,
closeButton: false,
showArrow: true,
isHide: true
}).setText("").addTo(app);
//
heatmapEnt.on("mousemove", e => {
console.log(e)
if (e?.intersection?.uv) {
let value = heatmap.getValueByUV(e?.intersection?.uv.toArray());
popup.setText(`值: ${value}`)
if (value) {
popup.position.copy(e?.intersection?.point)
popup.show();
} else {
popup.hide();
}
}
})
heatmapEnt.on("mouseout", e => {
popup.hide();
})
// 模拟值变化
setInterval(() => {
let data = [];
for(let k = 0; k < 100; k++) {
data.push({
point: vjmap3d.randPoint2D({minX: -10, maxX: 10, minY: -10, maxY: 10}),
value: Math.random() * 50
})
}
heatmap.setData({
// bounds: [[0, 0], [100, 100]],
data,
min: 0,
max: 50
})
}, 3000)
三维热力图
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let data = [];
for(let k = 0; k < 100; k++) {
data.push({
point: vjmap3d.randPoint2D({minX: -10, maxX: 10, minY: -10, maxY: 10}),
value: Math.random() * 50
})
}
//
let heatmap = new vjmap3d.Heatmap({
mode3D: true, // 3d模式
heightRatio: 1.0,
bounds: [[-10, -10], [10, 10]],
data: data,
min: 0,
max: 50
})
let mesh = await heatmap.create();
let heatmapEnt = new vjmap3d.MeshEntity(mesh);
heatmapEnt.addTo(app);
heatmapEnt.pointerEvents = true
//
const popup = new vjmap3d.Popup2D({
backgroundColor: "#A0FFA0",
buttonColor: "#f00",
closeOnClick: false,
closeButton: false,
showArrow: true,
isHide: true
}).setText("").addTo(app);
//
heatmapEnt.on("mousemove", e => {
console.log(e)
if (e?.intersection?.uv) {
let value = heatmap.getValueByUV(e?.intersection?.uv.toArray());
popup.setText(`值: ${value}`)
if (value) {
popup.position.copy(e?.intersection?.point)
popup.show();
} else {
popup.hide();
}
}
})
heatmapEnt.on("mouseout", e => {
popup.hide();
})
// 模拟值变化
setInterval(() => {
let data = [];
for(let k = 0; k < 100; k++) {
data.push({
point: vjmap3d.randPoint2D({minX: -10, maxX: 10, minY: -10, maxY: 10}),
value: Math.random() * 50
})
}
heatmap.setData({
// bounds: [[0, 0], [100, 100]],
data,
min: 0,
max: 50
})
}, 3000)
生成热力图数据分析材质
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
//
const box = new THREE.Mesh(
new THREE.BoxGeometry(3, 3, 3),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
let meshEnt = new vjmap3d.MeshEntity(box);
meshEnt.addTo(app);
//
const createHeatmap = async () => {
// 获取实体的外包盒
let box3d = meshEnt.getBoundingBox(false);
let pointNum = 10; // 模拟十个点的业务数据
let data = []
let size = new THREE.Vector3();
box3d.getSize(size)
for(let k = 0 ; k < pointNum; k++) {
data.push({
x: box3d.min.x + Math.random() * size.x,
y: box3d.min.y + Math.random() * size.y,
z: box3d.min.z + Math.random() * size.z,
value: Math.random() * 100
})
}
// 设置热力图数据分析材质
await meshEnt.setHeatmapDataMaterial({
data,
})
}
createHeatmap();
let control = new vjmap3d.ButtonGroupControl({
buttons: [
{
id: "id1",
html: "数据热力图",
style: {
width: "100px"
},
onclick: async () => {
await createHeatmap();
}
},{
id: "id2",
html: "取消热力图",
style: {
width: "100px"
},
onclick: async () => {
// 还原成原来的材质
meshEnt.resetMaterial();
}
}
]
});
app.addControl(control, "top-right");
MeasureRuler 测量标尺
用法
{
let lineStart = new THREE.Vector3(-3, 0, -6);
let lineEnd = new THREE.Vector3(2, 0, 3);
vjmap3d.creataMeasureRuler(app, {
startPoint: lineStart,
endPoint: lineEnd
})
}
{
let lineStart = new THREE.Vector3(5, 0, 5);
let lineEnd = new THREE.Vector3(5, 5, 5);
vjmap3d.creataMeasureRuler(app, {
startPoint: lineStart,
endPoint: lineEnd,
planePoint: [4, 0, 5],
showTextCb(ptStart, ptEnd, dist, fractionDigits) {
return `${dist.toFixed(fractionDigits ?? 2)} 米`
},
})
}
{
let lineStart = new THREE.Vector3(-17, 0, -6);
let lineEnd = new THREE.Vector3(-3, 6, -19);
let ent = vjmap3d.creataMeasureRuler(app, {
startPoint: lineStart,
endPoint: lineEnd,
textAngleCb(ptStart, ptEnd, quat) {
return [{
axis: "x",
angle: Math.PI
}]
}
})
}
const ui = await app.getConfigPane({ title: "绘制", style: { width: "250px"}});
ui.appendChild({
type: "button",
label: '绘制标尺',
value: async ()=>{
let line = await app.actionDrawLineSting({
pointMaxCount: 2
});
if (line.isCancel) return;
let lineClr = vjmap3d.randColor();
let arrowClr = vjmap3d.randColor();
let txtClr = vjmap3d.randColor();
let sideLineClr = vjmap3d.randColor();
let ruler = vjmap3d.creataMeasureRuler(app, {
startPoint: line.data.coordinates[0],
endPoint: line.data.coordinates[1],
rulerLineStyle: {
color: lineClr,
},
arrowStyle: {
color: arrowClr,
},
sideLineStyle: {
color: sideLineClr
},
textStyle: {
color: txtClr,
billboard: false
}
})
}
})
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
{
let lineStart = new THREE.Vector3(-3, 0, -6);
let lineEnd = new THREE.Vector3(2, 0, 3);
vjmap3d.creataMeasureRuler(app, {
startPoint: lineStart,
endPoint: lineEnd
})
}
{
let lineStart = new THREE.Vector3(5, 0, 5);
let lineEnd = new THREE.Vector3(5, 5, 5);
vjmap3d.creataMeasureRuler(app, {
startPoint: lineStart,
endPoint: lineEnd,
planePoint: [4, 0, 5],
showTextCb(ptStart, ptEnd, dist, fractionDigits) {
return `${dist.toFixed(fractionDigits ?? 2)} 米`
},
})
}
{
let lineStart = new THREE.Vector3(-17, 0, -6);
let lineEnd = new THREE.Vector3(-3, 6, -19);
let ent = vjmap3d.creataMeasureRuler(app, {
startPoint: lineStart,
endPoint: lineEnd,
textAngleCb(ptStart, ptEnd, quat) {
return [{
axis: "x",
angle: Math.PI
}]
}
})
}
const ui = await app.getConfigPane({ title: "绘制", style: { width: "250px"}});
ui.appendChild({
type: "button",
label: '绘制标尺',
value: async ()=>{
let line = await app.actionDrawLineSting({
pointMaxCount: 2
});
if (line.isCancel) return;
let lineClr = vjmap3d.randColor();
let arrowClr = vjmap3d.randColor();
let txtClr = vjmap3d.randColor();
let sideLineClr = vjmap3d.randColor();
let ruler = vjmap3d.creataMeasureRuler(app, {
startPoint: line.data.coordinates[0],
endPoint: line.data.coordinates[1],
rulerLineStyle: {
color: lineClr,
},
arrowStyle: {
color: arrowClr,
},
sideLineStyle: {
color: sideLineClr
},
textStyle: {
color: txtClr,
billboard: false
}
})
}
})
TextMesh 文本Mesh
通过 createTextMesh 创建文本Mesh。
提示
因为创建文本mesh,需要通过await Plugins.loadPlugin("text3d")加载text3d来加载插件,所以createTextMesh是一个异步函数。
用法
const textParams = {
color: '#00ffff',
text: `春天是一年四季中最美丽的季节之一,也是最富有生机的季节。
在这个季节里,大自然给我们带来了很多惊喜和愉悦。
在这个季节里,我喜欢去公园里散步,欣赏那里的美景。
The end 。`,
fontSize: 2,
maxWidth: 40,
lineHeight: 1,
font: env.assetsPath + 'fonts/gb2312.ttf',
// onPreloadEnd 只有同时传了 font 和 characters(要预加载的字符)时才会被调用
onPreloadEnd: () => {
// console.log('loaded')
},
}
let text = await vjmap3d.createTextMesh(textParams);
let mesh = text.mesh;
app.scene.add(mesh)
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
const textParams = {
color: '#00ffff',
text: `春天是一年四季中最美丽的季节之一,也是最富有生机的季节。
在这个季节里,大自然给我们带来了很多惊喜和愉悦。
在这个季节里,我喜欢去公园里散步,欣赏那里的美景。
The end 。`,
fontSize: 2,
maxWidth: 40,
lineHeight: 1,
font: env.assetsPath + 'fonts/gb2312.ttf',
// onPreloadEnd 只有同时传了 font 和 characters(要预加载的字符)时才会被调用
onPreloadEnd: () => {
// console.log('loaded')
},
}
let text = await vjmap3d.createTextMesh(textParams);
let mesh = text.mesh;
app.scene.add(mesh)
TrailLine
通过 TrailLine 创建。
用法
let guideLine = new vjmap3d.TrailLine({
positions:[[1,1,1],[-5,1,5],[-5,1,1]], //以xz轴为平面
width:10, //线宽
placeLength:15, //图标放置的长度
placeSpace:10, //图标放置间隔
iconUrl: env.assetsPath + "textures/arrow.png", //图标
speed:2, //速度
smooth: true,
isAnimate:true //是否需要动画
})
guideLine.update(app)
app.scene.add(guideLine.trail)
app.signal.onAppUpdate.add(e => {
guideLine.update(app)
})
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
let guideLine = new vjmap3d.TrailLine({
positions:[[1,1,1],[-5,1,5],[-5,1,1]], //以xz轴为平面
width:10, //线宽
placeLength:15, //图标放置的长度
placeSpace:10, //图标放置间隔
iconUrl: env.assetsPath + "textures/arrow.png", //图标
speed:2, //速度
smooth: true,
isAnimate:true //是否需要动画
})
guideLine.update(app)
app.scene.add(guideLine.trail)
app.signal.onAppUpdate.add(e => {
guideLine.update(app)
})
Billboard 公告板
公告板是某一个网格它会通过旋转轴始终对着相机,这样就可以让它的正面始终是相机方向。通过 Billboard 创建公告板。对于实体,也可以通过增加公告板模块BillboardModule来设置。
用法
let torusBillboardEnt = new vjmap3d.MeshEntity(createTorusMesh())
torusBillboardEnt.position.set(1, 2, 0);
torusBillboardEnt.addTo(app);
let opts = {
follow: true,
lockX: false,
lockZ: false,
lockY: false,
baseOnPreRotation: true,
}
torusBillboardEnt.add(vjmap3d.BillboardModule, opts);
示例
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
stat: { show: true, left: "0" },
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
}
})
const createTorusMesh = () => {
const geometry = new THREE.TorusKnotGeometry(1, 0.35, 100, 32)
const mat = new THREE.MeshStandardMaterial({
roughness: 0,
color: 0xffffff * Math.random(),
})
const torusMesh = new THREE.Mesh(geometry, mat)
torusMesh.castShadow = true
torusMesh.receiveShadow = true
return torusMesh
}
// 正常的,用于对比
let torusNormalEnt = new vjmap3d.MeshEntity(createTorusMesh())
torusNormalEnt.position.set(0, 6, 0);
torusNormalEnt.addTo(app);
// 设置了公告板模式的
let torusBillboardEnt = new vjmap3d.MeshEntity(createTorusMesh())
torusBillboardEnt.position.set(1, 2, 0);
torusBillboardEnt.addTo(app);
let opts = {
follow: true,
lockX: false,
lockZ: false,
lockY: false,
baseOnPreRotation: true,
}
torusBillboardEnt.add(vjmap3d.BillboardModule, opts);
let mod = torusBillboardEnt.getModule(vjmap3d.BillboardModule);
const ui = await app.getConfigPane()
let gui = new vjmap3d.UiPanelJsonConfig();
gui.add(opts, "follow").onChange(v => {
mod.billboard.updateProps(opts)
})
gui.add(opts, "lockX").onChange(v => {
mod.billboard.updateProps(opts)
})
gui.add(opts, "lockZ").onChange(v => {
mod.billboard.updateProps(opts)
})
gui.add(opts, "lockY").onChange(v => {
mod.billboard.updateProps(opts)
})
gui.add(opts, "baseOnPreRotation").onChange(v => {
mod.billboard.updateProps(opts)
})
gui.toJson().forEach(c => ui.appendChild(c));