# 精灵图像实体

`SpriteImagesEntity` 是由 实体`Entity` 和 `SpriteImagesModule` 模块组合而成的新的实体。

 [SpriteImagesEntity](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html) =  [Entity](https://vjmap.com/map3d/api/classes/Entity.html) + [SpriteImagesModule](https://vjmap.com/map3d/api/classes/SpriteImagesModule.html) 

 `SpriteImagesModule` 中引用的是  [SpriteImages](https://vjmap.com/map3d/api/classes/SpriteImages.html)  对象
 ## 常用方法


 `SpriteImagesEntity` 是由 实体`Entity` 派生而来。具体 [Entity](https://vjmap.com/map3d/api/classes/Entity.html) 的所有属性和方法。同时也具有[SpriteImagesModule](https://vjmap.com/map3d/api/classes/SpriteImagesModule.html)  的相关方法。通过 [SpriteImages](https://vjmap.com/map3d/api/classes/SpriteImagesModule.html#SpriteImages) 对象 可访问  [SpriteImages](https://vjmap.com/map3d/api/classes/SpriteImages.html)  类的所有方法


 
|方法|描述|
|:---|:---|
|[SpriteImages](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#SpriteImages)|获取精灵图像对象|
|[getData](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#getData)|获取数据|
|[setData](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#setData)|设置数据。**异步**（返回 `Promise<void>`，内部先加载图标图片）——需要紧接着操作新精灵（如 `setItemHighlight`）时先 `await`|
|[setItemHighlight](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#setItemHighlight)|设置某项高亮：`setItemHighlight(index, isHighlight)`，`index` 为数据**下标**（不是 id）。⚠️ 该数据项须设置了 `highlightIcon` 或 `highlightOpacity > 0`，否则没有高亮材质、调用静默无效|
|[update](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#update)|更新数据|
|[getOptions](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#getOptions)|获取选项|
## 使用示例


### 创建SpriteImagesEntity

```js
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 i = 0; i < 20; i++) {
    for(let j = 0; j < 20; j++) {
        data.push({
            position: [i * 5 - 50, 0, j * 5 - 50],
            icon: env.assetsPath + `image/sensor${(i % 5) + 1}.png`,
            highlightOpacity: 0.6,
            id: j * 20 + i
        });
    }
}
let spriteImagesEnt = new vjmap3d.SpriteImagesEntity({
    data: data,
    sizeAttenuation: false,
    allowOverlap: false,
});
spriteImagesEnt.addTo(app);
```

### 增加事件
```js
spriteImagesEnt.pointerEvents = true
spriteImagesEnt.on("mouseover", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteImagesEnt.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)
    }
})
spriteImagesEnt.on("mouseout", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteImagesEnt.setItemHighlight(index, false)
        app.popup2d.hide()
    }
})
spriteImagesEnt.on("click", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        app.logInfo(`您点击了ID:${data[index].id}`)
    }
})
```

### 修改、新增、删除
调用 [setData](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#setData) 来对数据进行增删改
```js
// 如新增一条
data.push({
    position: vjmap3d.randPoint3D(),
    icon: env.assetsPath + `image/sensor${vjmap3d.randInt(1, 5)}.png`,
    highlightOpacity: 0.6,
    id: data.length + 1
})
spriteImagesEnt.setData(data)
```

### 设置是否允许重叠
调用 [getOptions](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#getOptions) 来获取属性，`update`更新数据
```js
spriteImagesEnt.getOptions().allowOverlap = true;
spriteImagesEnt.update(); // 更新
```

### 设置透明度随相机距离大小消退距离
调用 [getOptions](https://vjmap.com/map3d/api/classes/SpriteImagesEntity.html#getOptions) 来获取属性，`update`更新数据
```js
spriteImagesEnt.getOptions().fadeDistance = 100; // 距相机距离
spriteImagesEnt.update(); // 更新
```

> ⚠️ webgl 引擎下 `fadeDistance` 在图片材质**首次编译**时写进着色器，而材质按"图标+尺寸+旋转+透明度"缓存复用，`update()` 不会让已渲染过的材质重新编译——运行时改 `fadeDistance` 后 `update()` 对已显示的图片**基本不生效**。最好在构造时设定；运行时要改，先 `await spriteImagesEnt.setData([])`（释放缓存材质）再 `await spriteImagesEnt.setData(data)` 重建。

### 示例
<mapFrame>
<pre>
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 i = 0; i < 20; i++) {
    for(let j = 0; j < 20; j++) {
        data.push({
            position: [i * 5 - 50, 0, j * 5 - 50],
            icon: env.assetsPath + `image/sensor${(i % 5) + 1}.png`,
            highlightOpacity: 0.6,
            id: j * 20 + i
        });
    }
}
let spriteImagesEnt = new vjmap3d.SpriteImagesEntity({
    data: data,
    sizeAttenuation: false,
    allowOverlap: false,
});
spriteImagesEnt.addTo(app);
//
spriteImagesEnt.pointerEvents = true
spriteImagesEnt.on("mouseover", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteImagesEnt.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)
    }
})
spriteImagesEnt.on("mouseout", e => {
    let index = e?.intersection?.object?.userData.objectDataIndex
    if (index !== undefined) {
        spriteImagesEnt.setItemHighlight(index, false)
        app.popup2d.hide()
    }
})
spriteImagesEnt.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: [spriteImagesEnt.getOptions(), "allowOverlap"],
        onChange: () => {
           spriteImagesEnt.update()
        }
    },
    {
        type: 'slider',
        label: '消退距离',
        bounds: [0, 5000],
        stepSize: 1,
        getValue: () => spriteImagesEnt.getOptions().fadeDistance ?? 0,
        setValue: async (v) => {
           spriteImagesEnt.getOptions().fadeDistance = v;
           // webgl 下缓存材质不会重编译，update() 不够，需清空重建
           await spriteImagesEnt.setData([])
           await spriteImagesEnt.setData(data)
        }
    },
    {
        type: 'button',
        label: '再增加一个',
        value: ()=> {
            data.push({
                position: vjmap3d.randPoint3D(),
                icon: env.assetsPath + `image/sensor${vjmap3d.randInt(1, 5)}.png`,
                highlightOpacity: 0.6,
                id: data.length + 1
            })
            spriteImagesEnt.setData(data)
        }
    },
    {
        type: 'button',
        label: '修改第一个图标',
        value: ()=> {
            if (data.length > 0) {
                let icon = env.assetsPath + `image/sensor${vjmap3d.randInt(1, 5)}.png`
                data[0].icon = icon;
                data[0].highlightIcon = icon;
            }
            spriteImagesEnt.setData(data)
        }
    },
    {
        type: 'button',
        label: '删除第一个',
        value: ()=> {
            if (data.length == 0) {
                return
            }
            data.splice(0, 1)
            spriteImagesEnt.setData(data)
        }
    }
]
cfg.forEach(c => ui.appendChild(c));
</pre>
</mapFrame>
