唯杰地图 logo唯杰地图
VJMAP
EN

精灵文本实体

vjmap3d/references/entity/spriteTextsEntity.md 14.2 KB411 行
原文

SpriteTextsEntity 是由 实体Entity 和 SpriteTextsModule 模块组合而成的新的实体。

SpriteTextsEntity = Entity + SpriteTextsModule

SpriteTextsModule 中引用的是 SpriteTexts 对象

常用方法

SpriteTextsEntity 是由 实体Entity 派生而来。具体 Entity 的所有属性和方法。同时也具有SpriteTextsModule 的相关方法。通过 SpriteTexts 对象 可访问 SpriteTexts 类的所有方法

方法 描述
SpriteTexts 获取精灵图像对象
getData 获取数据
setData 设置数据。⚠️ 内容未变的文字精灵会被复用、不重设 userData.objectDataIndex,删除/中间插入后事件拿到的下标会错位,见下文"修改、新增、删除"
setItemHighlight 设置某项高亮:setItemHighlight(index, isHighlight),index 是数据下标(事件里的 userData.objectDataIndex),不是 id。⚠️ 仅当该数据项设置了 highlightStyle(高亮时的文字样式)才有效,否则静默无效
update 更新数据
getOptions 获取选项

使用示例

创建SpriteTextsEntity

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 image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let data = [];
for(let i = 0; i < 20; i++) {
    for(let j = 0; j < 20; j++) {
        let id = j * 20 + i;
        let strId = id + '';
        if (strId.length <= 2) strId = " " + strId;
        data.push({
            position:  [i * 5 - 50, 0, j * 5 - 50],
            text: `标签\n${strId}`,
            style: {
                x: 5,
                y: 5,
                fill: vjmap3d.render2d.color.random(),
                font: '16px Microsoft Yahei',//22px sans-serif
                padding: [10, 20, 20, 20],
                backgroundColor: {
                    image: image
                }
            },
            id: id
        });
    }
}
let spriteTextsEnt = new vjmap3d.SpriteTextsEntity({
    data: data,
    sizeAttenuation: false,
    allowOverlap: false,
});
spriteTextsEnt.addTo(app);

增加事件

spriteTextsEnt.pointerEvents = true
spriteTextsEnt.on("mouseover", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteTextsEnt.setItemHighlight(index, true);
        app.popup2d.show();
        app.popup2d.setOffset([0, -40]);
        app.popup2d.setHTML(`<span style="color:red">ID:${data[index].id}</span>`)
        app.popup2d.setPosition(data[index].position)
    }
})
spriteTextsEnt.on("mouseout", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteTextsEnt.setItemHighlight(index, false)
        app.popup2d.hide()
    }
})
spriteTextsEnt.on("click", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        app.logInfo(`您点击了ID:${data[index].id}`)
    }
})

修改、新增、删除

调用 setData 来对数据进行增删改

⚠️ setData 会按"id+位置+文字+样式"复用内容未变的精灵,被复用的精灵不会更新 userData.objectDataIndex(也不回填数据项的 object)。末尾追加、原位修改没问题;但删除或在中间插入后,后面那些精灵的下标全部错位,事件里 data[index] 会指向别的项(带 highlightStyle 时 setItemHighlight 还可能抛错)。删除/插入时先清空再重设:spriteTextsEnt.setData([]); spriteTextsEnt.setData(data);

// 如新增一条
 data.push({
    position: vjmap3d.randPoint3D(),
    text: `标签\n${data.length + 1}`,
    style: {
        x: 5,
        y: 5,
        fill: vjmap3d.render2d.color.random(),
        font: '16px Microsoft Yahei',//22px sans-serif
        padding: [10, 20, 20, 20],
        backgroundColor: {
            image: image
        }
    },
    id: data.length + 1
})
spriteTextsEnt.setData(data)

设置是否允许重叠

调用 getOptions 来获取属性,update更新数据

spriteTextsEnt.getOptions().allowOverlap = true;
spriteTextsEnt.update(); // 更新

设置透明度随相机距离大小消退距离

调用 getOptions 来获取属性,update更新数据

spriteTextsEnt.getOptions().fadeDistance = 100; // 距相机距离
spriteTextsEnt.update(); // 更新

⚠️ webgl 引擎下 fadeDistance 是在文字材质首次编译时写进着色器的,而文字材质按"文字+样式"缓存复用,update() 不会让已渲染过的材质重新编译——所以运行时改 fadeDistance 后 update() 对已显示的文字基本不生效。最好在构造时就设定;运行时要改,先 setData([])(会释放缓存材质)再 setData(data) 重建。

示例

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 image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let data = [];
for(let i = 0; i < 20; i++) {
    for(let j = 0; j < 20; j++) {
        let id = j * 20 + i;
        let strId = id + '';
        if (strId.length <= 2) strId = " " + strId;
        data.push({
            position:  [i * 5 - 50, 0, j * 5 - 50],
            text: `标签\n${strId}`,
            style: {
                x: 5,
                y: 5,
                fill: vjmap3d.render2d.color.random(),
                font: '16px Microsoft Yahei',//22px sans-serif
                padding: [10, 20, 20, 20],
                backgroundColor: {
                    image: image
                }
            },
            id: id
        });
    }
}
let spriteTextsEnt = new vjmap3d.SpriteTextsEntity({
    data: data,
    sizeAttenuation: false,
    allowOverlap: false,
});
spriteTextsEnt.addTo(app);
//
spriteTextsEnt.pointerEvents = true
spriteTextsEnt.on("mouseover", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteTextsEnt.setItemHighlight(index, true);
        app.popup2d.show();
        app.popup2d.setOffset([0, -40]);
        app.popup2d.setHTML(`ID:${data[index].id}`)
        app.popup2d.setPosition(data[index].position)
    }
})
spriteTextsEnt.on("mouseout", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteTextsEnt.setItemHighlight(index, false)
        app.popup2d.hide()
    }
})
spriteTextsEnt.on("click", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        app.logInfo(`您点击了ID:${data[index].id}`)
    }
})
const ui = await app.getConfigPane({ title: "设置", style: { width: "250px", overflow: "hidden"}});
let cfg = [
    {
        type: 'checkbox',
        label: '允许重叠',
        property: [spriteTextsEnt.getOptions(), "allowOverlap"],
        onChange: () => {
           spriteTextsEnt.update()
        }
    },
    {
        type: 'slider',
        label: '消退距离',
        bounds: [0, 5000],
        stepSize: 1,
        getValue: () => spriteTextsEnt.getOptions().fadeDistance ?? 0,
        setValue: (v) => {
           spriteTextsEnt.getOptions().fadeDistance = v;
           // webgl 下缓存材质不会重编译,update() 不够,需清空重建
           spriteTextsEnt.setData([])
           spriteTextsEnt.setData(data)
        }
    },
    {
        type: 'button',
        label: '再增加一个',
        value: ()=> {
            data.push({
                position: vjmap3d.randPoint3D(),
                text: `标签\n${data.length + 1}`,
                style: {
                    x: 5,
                    y: 5,
                    fill: vjmap3d.render2d.color.random(),
                    font: '16px Microsoft Yahei',//22px sans-serif
                    padding: [10, 20, 20, 20],
                    backgroundColor: {
                        image: image
                    }
                },
                id: data.length + 1
            })
            spriteTextsEnt.setData(data)
        }
    },
    {
        type: 'button',
        label: '修改第一个文字',
        value: ()=> {
            if (data.length > 0) {
                data[0].text = "修改";
            }
            spriteTextsEnt.setData(data)
        }
    },
    {
        type: 'button',
        label: '删除第一个',
        value: ()=> {
            if (data.length == 0) {
                return
            }
            data.splice(0, 1)
            // 删除会让复用精灵的下标错位,先清空再重设
            spriteTextsEnt.setData([])
            spriteTextsEnt.setData(data)
        }
    }
]
cfg.forEach(c => ui.appendChild(c));

SpriteImagesEntity 和 SpriteTextsEntity 组合使用

SpriteImagesEntity 和 SpriteTextsEntity 组合使用时,碰撞检测时,如需对应的两组同时隐藏或显示,需设置collideId属性为同一个,根据 collideZIndex 来进行排序

let textDatas = [];
let imageDatas = [];
for(let i = 0; i < 20; i++) {
    for(let j = 0; j < 20; j++) {
        let id = j * 20 + i;
        let position =  [i * 5 - 50, 0, j * 5 - 50]
        // 图片
        imageDatas.push({
            position: position,
            icon: env.assetsPath + `image/sensor${(i % 5) + 1}.png`,
            highlightOpacity: 0.6,
            id: id,
            collideZIndex: id,
            collideId: 'spriteImageText' + id, // 同组的id要一样
        });

        // 文本
        textDatas.push({
            position: position,
            text: `ID: ${id}`,
            style: {
                fill: '#FF1285',
                font: '20px sans-serif',
                padding: [5, 5, 5, 5],
                backgroundColor: '#FFFE91',
                borderColor: '#FFFE91',
                borderWidth: 2,
                borderRadius: 5
            },
            id: id,
            anchorX: "center",
            anchorY: "top",
            offsetY: 5,
            collideZIndex: id,
            collideId: 'spriteImageText' + id, // 同组的id要一样
        });
    }
}
//
let entImage = new vjmap3d.SpriteImagesEntity({
    data: imageDatas,
    sizeAttenuation: false,
    allowOverlap: false,
});
entImage.addTo(app);
//
let entText = new vjmap3d.SpriteTextsEntity({
    data: textDatas,
    sizeAttenuation: false,
    allowOverlap: false,
});
entText.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 image = await vjmap3d.loadImage(env.assetsPath + "image/imgbg.png")
let textDatas = [];
let imageDatas = [];
for(let i = 0; i < 20; i++) {
    for(let j = 0; j < 20; j++) {
        let id = j * 20 + i;
        let position =  [i * 5 - 50, 0, j * 5 - 50]
        // 图片
        imageDatas.push({
            position: position,
            icon: env.assetsPath + `image/sensor${(i % 5) + 1}.png`,
            highlightOpacity: 0.6,
            id: id,
            collideId: 'spriteImageText' + id,
            collideZIndex: id,
        });
        // 文本
        textDatas.push({
            position: position,
            text: `ID: ${id}`,
            style: {
                fill: '#FF1285',
                font: '20px sans-serif',
                padding: [5, 5, 5, 5],
                backgroundColor: '#FFFE91',
                borderColor: '#FFFE91',
                borderWidth: 2,
                borderRadius: 5
            },
            id: id,
            anchorX: "center",
            anchorY: "top",
            offsetY: 5,
            collideZIndex: id,
            collideId: 'spriteImageText' + id,
        });
    }
}
let entImage = new vjmap3d.SpriteImagesEntity({
    data: imageDatas,
    sizeAttenuation: false,
    allowOverlap: false,
});
entImage.addTo(app);
//
let entText = new vjmap3d.SpriteTextsEntity({
    data: textDatas,
    sizeAttenuation: false,
    allowOverlap: false,
});
entText.addTo(app);