精灵文本实体
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);