精灵标记实体
SpriteMarkerEntity 是由 实体Entity 和 SpriteMarkerModule 模块组合而成的新的实体。
SpriteMarkerEntity = Entity + SpriteMarkerModule
SpriteMarkerModule 中引用的是 SpriteMarker 对象
常用方法
SpriteMarkerEntity 是由 实体Entity 派生而来。具体 Entity 的所有属性和方法。同时也具有SpriteMarkerModule 的相关方法。通过 SpriteMarker 对象 可访问 SpriteMarker 类的所有方法
| 方法 | 描述 |
|---|---|
| SpriteMarker | 获取精灵标记对象 |
| setMaterial | 设置材质 |
| updateRenderTexture | 更新渲染材质 |
| refreshRenderTexture | 刷新渲染材质 |
| dispatchRenderTextureEvent | 给渲染材质触发相应事件 |
| setAllowOverlap | 设置是否允许重叠 |
| updateScale | 更新缩放尺寸 |
| getBoundingRect | 获取矩形范围 |
使用示例
创建SpriteMarkerEntity
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" } // 是否显示视角指示器
}
})
let texture = await vjmap3d.ResManager.loadTextureSync(env.assetsPath + "image/sensor1.png")
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
texture: texture // 材质
});
spriteMarker.addTo(app)
如果不做为实体创建,只创建SpriteMarker,可用如下代码:
let spriteMarker = new vjmap3d.SpriteMarker(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
texture: texture // 材质
}, app);
app.scene.add(spriteMarker)
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" } // 是否显示视角指示器
}
})
let texture = await vjmap3d.ResManager.loadTextureSync(env.assetsPath + "image/sensor1.png")
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
texture: texture // 材质
});
spriteMarker.addTo(app)
通过渲染材质绘制
原理: 通过2D渲染引擎 render2d 渲染出材质,做为材质参数输入
let image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
renderTexture: { // 渲染材质
autoCanvasSize: false,
canvasWidth: 80, // 像素宽,这里是根据底图的像素宽高定的
canvasHeight: 80, // 像素高,这里是根据底图的像素宽高定的
sharedCanvas: false,
elements: [
new vjmap3d.render2d.Image({
style: {
x: 0,
y: 0,
image: image,
width: 80, // 像素宽,这里是图片像素宽高定的
height: 80 // 像素高,这里是图片像素宽高定的
}
}),
new vjmap3d.render2d.Text({
style: {
x: 40,
y: 20,
text: '唯杰',
fill: "#0ff",
font: '30px Arial',
verticalAlign: "top",
align: "center"
}
})
]
}
});
spriteMarker.addTo(app)
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" } // 是否显示视角指示器
}
})
let image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
renderTexture: {
autoCanvasSize: false,
canvasWidth: 80, // 像素宽,这里是根据底图的像素宽高定的
canvasHeight: 80, // 像素高,这里是根据底图的像素宽高定的
sharedCanvas: false,
elements: [
new vjmap3d.render2d.Image({
style: {
x: 0,
y: 0,
image: image,
width: 80, // 像素宽,这里是图片像素宽高定的
height: 80 // 像素高,这里是图片像素宽高定的
}
}),
new vjmap3d.render2d.Text({
style: {
x: 40,
y: 20,
text: '唯杰',
fill: "#0ff",
font: '30px Arial',
verticalAlign: "top",
align: "center",
//backgroundColor: vjmap3d.render2d.color.random(),
//borderColor: vjmap3d.render2d.color.random(),
//borderWidth: 2,
//borderRadius: 5,
// textShadowBlur: 2,
// textShadowColor: '#893e95',
// textShadowOffsetX: 2,
// textShadowOffsetY: 4,
}
})
]
}
});
spriteMarker.addTo(app)
随相机尺寸大小变化
需要进行以下设置
/** 是否使用大小衰减(根据相机远近自动调整点的大小) false的话,表示用像素大小,true表示世界坐标 */
sizeAttenuation: true,
/** 宽(sizeAttenuation为false是像素大小,否则表示世界坐标宽度) */
width: 1,
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" } // 是否显示视角指示器
}
})
let image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: true, // 世界坐标大小,随相机远近缩放(近大远小)
width: 1, // 世界坐标宽度
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
renderTexture: {
autoCanvasSize: false,
canvasWidth: 80, // 像素宽,这里是根据底图的像素宽高定的
canvasHeight: 80, // 像素高,这里是根据底图的像素宽高定的
sharedCanvas: false,
elements: [
new vjmap3d.render2d.Image({
style: {
x: 0,
y: 0,
image: image,
width: 80, // 像素宽,这里是图片像素宽高定的
height: 80 // 像素高,这里是图片像素宽高定的
}
}),
new vjmap3d.render2d.Text({
style: {
x: 40,
y: 20,
text: '唯杰',
fill: "#0ff",
font: '30px Arial',
verticalAlign: "top",
align: "center"
}
})
]
}
});
spriteMarker.addTo(app)
给渲染材质中的元素增加事件效果
渲染材质中的每个元素都可以响应事件进行事件处理,渲染材质的事件由SpriteMarkerEntity派发事件。
可点击下面示例中的文字进行查看效果
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" } // 是否显示视角指示器
}
})
let image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png");
// 创建一个文字按钮1
let button1 = new vjmap3d.render2d.Text({
style: {
x: 40,
y: 12,
text: '查看详情',
fill: "#0ff",
font: '14px Arial',
verticalAlign: "top",
align: "center"
}
})
// 创建一个文字按钮2
let button2 = new vjmap3d.render2d.Text({
style: {
x: 40,
y: 38,
text: '点击跳转',
fill: "#BDFFAB",
font: '14px Arial',
verticalAlign: "top",
align: "center"
}
})
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: [-2, 0, 1], // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
renderTexture: {
autoCanvasSize: false,
canvasWidth: 80, // 像素宽,这里是根据底图的像素宽高定的
canvasHeight: 80, // 像素高,这里是根据底图的像素宽高定的
sharedCanvas: false,
elements: [
new vjmap3d.render2d.Image({
style: {
x: 0,
y: 0,
image: image,
width: 80, // 像素宽,这里是图片像素宽高定的
height: 80 // 像素高,这里是图片像素宽高定的
}
}),
button1,
button2
]
}
});
// 响应文字按钮1的相关事件
button1.on("mouseover", function() {
button1.attr("style", {
fill: "#ff0"
});
spriteMarker.refreshRenderTexture();
app.setCursor("pointer")
});
button1.on("mouseout", function() {
button1.attr("style", {
fill: "#0ff"
});
spriteMarker.refreshRenderTexture();
app.setCursor("")
});
button1.on("mouseup", function() {
app.logInfo("您点击了 查看详情 ", 2000)
});
//
// 响应文字按钮2的相关事件
button2.on("mouseover", function() {
button2.attr("style", {
fill: "#ff0"
});
spriteMarker.refreshRenderTexture();
app.setCursor("pointer")
});
button2.on("mouseout", function() {
button2.attr("style", {
fill: "#BDFFAB"
});
spriteMarker.refreshRenderTexture();
app.setCursor("")
});
button2.on("mouseup", function() {
app.logInfo("您点击了 点击跳转 ", 2000)
});
//
// 增加spriteMarker至应用
spriteMarker.addTo(app);
spriteMarker.pointerEvents = true; // 允许事件交互
// 响应spriteMarker的相关事件,并且向RenderTexture的元素进行派发事件
spriteMarker.on("mousemove", e => {
spriteMarker.dispatchRenderTextureEvent("mousemove", e?.intersection?.uv);
});
spriteMarker.on("mouseup", e => {
spriteMarker.dispatchRenderTextureEvent("mouseup", e?.intersection?.uv);
});
spriteMarker.on("mouseout", e => {
spriteMarker.dispatchRenderTextureEvent("mouseout");
});
批量绘制
批量绘制时,需要注意的有:
- 材质或相关资源尽量共享
- 使用
allowOverlap进行碰撞检测
let spriteMarker = new vjmap3d.SpriteMarkerEntity({
allowOverlap: false,
sizeAttenuation: false, // 必须显式写:未传时按 true 处理,又没给 width/height 时 addTo 会抛 "width or height is null"
renderTexture: {
sharedCanvas: true, // 提高效率共享一个canvas(缺省即共享;仅在 autoCanvasSize:true 且未设 canvasWidth 时生效)
autoCanvasSize: true,
elements: [ /* render2d 元素 */ ]
}
})
sizeAttenuation未传时按true(世界坐标大小)处理,此时必须给width或height(世界单位),否则addTo时抛width or height is null;要固定像素大小就显式写sizeAttenuation: false(宽高缺省取纹理像素尺寸)。sharedCanvas只在autoCanvasSize: true且没有设置canvasWidth(也没有render2dObject/pointerEvents)时才生效;一旦写了canvasWidth/canvasHeight,每个标记都会新建自己的 canvas,sharedCanvas: true被忽略。
3D示例
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" } // 是否显示视角指示器
}
})
let image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let imgEle = new vjmap3d.render2d.Image({
style: {
x: 0,
y: 0,
image: image,
width: 80, // 像素宽,这里是图片像素宽高定的
height: 80 // 像素高,这里是图片像素宽高定的
}
})
for(let i = 0; i < 20; i++) {
for(let j = 0; j < 20; j++) {
let positon = [i * 5 - 50, 0, j * 5 - 50];
let idx = `${j * 20 + i}`
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: positon, // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
allowOverlap: false,
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
renderTexture: {
autoCanvasSize: true,
sharedCanvas: true, // ⚠️ 下面设了 canvasWidth,共享 canvas 实际不生效(每个标记各建一个 canvas)
canvasWidth: 80, // 像素宽,这里是根据底图的像素宽高定的
canvasHeight: 80, // 像素高,这里是根据底图的像素宽高定的
elements: [
imgEle,
new vjmap3d.render2d.Text({
style: {
x: 40,
y: 20,
text: idx,
fill: vjmap3d.render2d.color.random(),
font: '30px Arial',
verticalAlign: "top",
align: "center"
}
})
]
}
});
// 增加spriteMarker至应用
spriteMarker.addTo(app);
spriteMarker.add(vjmap3d.EventModule, {
clickHighlight: true,
highlightOpacity: 0.2,
hoverSelect: true,
hoverHtmlText: `ID: ${idx}`,
popupOptions: {
anchor: "bottom",
offset: [0, -80]
},
clickCallback: (ent, isClick) => {
if (isClick) app.logInfo("您点击的ID是: " + idx, 2000)
}
});
}
}
2D地图示例
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
mapid: env.exampleMapId, // 地图ID
// @ts-ignore
mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
style: vjmap.openMapLightStyle() // div为深色背景颜色时,这里也传深色背景样式
})
if (res.error) {
// 如果打开出错
showError(res.error)
return;
}
// 获取地图范围
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(mapBounds);
//
// 地图对象
let map = new vjmap.Map({
container: 'map', // DIV容器ID
style: svc.rasterStyle(), // 样式,这里是栅格样式
center: prj.toLngLat(mapBounds.center()), // 设置地图中心点
zoom: 2, // 设置地图缩放级别
pitch: 45,
antialias: true, // 反锯齿
renderWorldCopies: false // 不显示多屏地图
});
//
// 关联服务对象和投影对象
map.attach(svc, prj);
await map.onLoad();
//
// 创建3d图层
let mapLayer = new vjmap3d.MapThreeLayer(map, {
stat: {
show: true,
left: "0"
}
});
map.addLayer(new vjmap.ThreeLayer({ context: mapLayer }))
let app = mapLayer.app;
let bounds = map.getGeoBounds(0.4);
let image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let imgEle = new vjmap3d.render2d.Image({
style: {
x: 0,
y: 0,
image: image,
width: 80, // 像素宽,这里是图片像素宽高定的
height: 80 // 像素高,这里是图片像素宽高定的
}
})
for(let i = 0; i < 20; i++) {
for(let j = 0; j < 20; j++) {
let positon = vjmap3d.toWorld([bounds.min.x + i * bounds.width() / 20, bounds.min.y + j * bounds.height() / 20]);
let idx = `${j * 20 + i}`
let spriteMarker = new vjmap3d.SpriteMarkerEntity(
{
position: positon, // 位置
sizeAttenuation: false, // 固定像素大小,不随相机变化
allowOverlap: false,
anchorX: "center", // x对齐方式
anchorY: "bottom", // y对齐方式
renderTexture: {
autoCanvasSize: true,
sharedCanvas: true, // ⚠️ 下面设了 canvasWidth,共享 canvas 实际不生效(每个标记各建一个 canvas)
canvasWidth: 80, // 像素宽,这里是根据底图的像素宽高定的
canvasHeight: 80, // 像素高,这里是根据底图的像素宽高定的
elements: [
imgEle,
new vjmap3d.render2d.Text({
style: {
x: 40,
y: 20,
text: idx,
fill: vjmap3d.render2d.color.random(),
font: '30px Arial',
verticalAlign: "top",
align: "center"
}
})
]
}
});
spriteMarker.idx = idx;
// 增加spriteMarker至应用
spriteMarker.addTo(app);
spriteMarker.add(vjmap3d.EventModule, {
hoverHighlight: false,
hoverSelect: false,
hoverHtmlText: `ID: ${idx}`,
popupOptions: {
anchor: "bottom",
offset: [0, -80]
},
popupAsEntityChild: false, // popup如果直接加到实体上,因为实体group位置在地图中心点,所以不能把popup做为他的子实体加入。或者用下面的语句加入实体中的SpriteMarker中去
//popupCallback: (entity, popup, isHover) => entity.node.children[0],
hoverCallback: (ent, isHover) => {
let opacity = isHover ? 0.6 : 1.0;
if (spriteMarker.SpriteMarker.material.opacity != opacity) {
spriteMarker.SpriteMarker.material.opacity = opacity
}
},
clickCallback: (ent, isClick) => {
if (!isClick) return
app.logInfo("您点击的ID是: " + idx, 2000)
}
});
}
}