唯杰地图 logo唯杰地图
VJMAP
EN

精灵标记实体

vjmap3d/references/entity/spriteMarkerEntity.md 21.4 KB613 行
原文

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)
            }
        });
}

}