xinrui-3d
v1.1.8
Published
3D scene renderer for floor resources
Readme
xinrui-3d
基于 Three.js 的楼层 3D 场景渲染模块,用于展示编辑器保存的楼层、墙体、区域、普通图标、通行设施和设备图标。
安装
npm install xinrui-3d three vueimport { createScene3D } from 'xinrui-3d'快速开始
const scene3d = createScene3D(container, floorData, {
resourceBaseUrl: '/resource/',
showAreaLabels: true,
showIconSprites: true,
showPassageSprites: true,
showDeviceSprites: true
})
scene3d.on('click', (object, feature) => {
console.log(feature)
})组件销毁时释放资源:
scene3d.dispose()创建场景
createScene3D(
container: HTMLElement,
data?: EditorData | FeatureCollection,
options?: SceneOptions
): TDScene常用配置:
| 配置 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| targetSize | number | 0 | 场景目标尺寸,0 表示按原始比例计算 |
| areaHeight | number | 0.08 | 地面区域厚度 |
| wallHeight | number | 4 | 墙体高度 |
| showAreaLabels | boolean | true | 显示房间文字 |
| showIconSprites | boolean | true | 显示普通图标 |
| showPassageSprites | boolean | true | 显示通行设施图标 |
| showDeviceSprites | boolean | true | 设备图标总开关;关闭后筛选方法也不能显示设备 |
| showPaths | boolean | false | 显示路径 |
| showFog | boolean | true | 启用远景雾 |
| background | number | 0xf7fafc | 背景颜色 |
| resourceBaseUrl | string | | 图标资源根路径 |
| resourceExtension | string | | 默认资源扩展名 |
| resourceFolders | object | | 不同业务图层的资源目录映射 |
| resolveResourcePath | function | | 自定义资源路径解析函数 |
数据结构
推荐使用编辑器数据结构:
{
schemaVersion: 'rt-gis-editor.v1',
coordinateSystem: {
metersPerUnit: 1
},
baseMap: {
type: 'FeatureCollection',
features: []
},
business: {
type: 'FeatureCollection',
features: []
}
}业务点位通过 properties.layerType 区分:
| layerType | 内容 |
| --- | --- |
| icon | 普通图标 |
| device | 设备图标 |
| passage / facility | 通行设施 |
设备示例:
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [113.1, 23.1]
},
properties: {
layerType: 'device',
categoryCode: 'air-conditioner',
deviceCode: 'AC-001',
categoryName: '空调',
markerUrl: '/resource/device/air-conditioner.png'
}
}设备显隐
模块不再创建设备分类栏或 Vue 分类组件。设备数据会在初始化时缓存,但设备 Sprite 默认不创建,业务侧通过下面的方法控制显示时才按需创建。
设备类型字段按以下顺序读取:
properties.deviceTypeCodeproperties.categoryCodeproperties.typeCodeproperties.subType
指定类型显示或隐藏
scene3d.showDeviceType(deviceTypeCode: string, visible?: boolean): number
scene3d.hideDeviceType(deviceTypeCode: string): numbershowDeviceType 第二个参数控制显示或隐藏,默认 true。返回值是当前实际显示的设备图标数量。
scene3d.showDeviceType('air-conditioner') // 显示空调类型
scene3d.showDeviceType('camera', true) // 显示监控类型
scene3d.showDeviceType('air-conditioner', false) // 隐藏空调类型
scene3d.hideDeviceType('camera') // 隐藏监控类型类型显隐支持叠加。例如先显示空调,再显示监控,会同时显示两个类型;隐藏其中一个类型不会影响另一个类型。
全部设备显示或隐藏
scene3d.setAllDeviceVisible(visible?: boolean): number
scene3d.showAllDevices(): number
scene3d.hideAllDevices(): numberscene3d.showAllDevices()
scene3d.hideAllDevices()
scene3d.setAllDeviceVisible(true)
scene3d.setAllDeviceVisible(false)调用全部显隐方法会清空类型级覆盖状态。传入空字符串也等同于控制全部设备:
scene3d.showDeviceType('', true) // 等同 showAllDevices()
scene3d.showDeviceType('', false) // 等同 hideAllDevices()当 showDeviceSprites: false 时,以上方法都会返回 0,设备图标继续保持隐藏。
性能说明
设备 Sprite 采用按需创建策略:setData() 时只缓存设备 feature,不创建 Three Sprite;调用 showDeviceType() 或 showAllDevices() 时,只为需要显示的设备创建 Sprite。已经创建过的 Sprite 后续会复用,隐藏时只切换 object.visible。
这样可以降低首次打开场景的 CPU/GPU 压力,并避免一开始加载所有隐藏设备贴图。首次显示某个设备类型时会有一次创建和贴图加载成本;之后再次显示同一批设备会很快。若调用 showAllDevices() 且设备数量达到上千、图标贴图尺寸较大、或每个设备使用独立图片,仍然会在这次调用时产生集中开销。此类场景建议复用同一类设备贴图、压缩图片尺寸,或按楼层/区域分批传入数据。
运行时图标
新增图标
const sprite = scene3d.createIcon(
'/resource/device/camera.png',
[113.1, 23.1],
{
layerType: 'device',
categoryCode: 'camera',
deviceCode: 'CAM-001'
}
)addIcon() 是 createIcon() 的别名。
替换设备图标
scene3d.replaceDeviceIcon(
'/resource/device/camera-offline.png',
'CAM-001',
{ status: 'offline' }
)移动设备图标
scene3d.moveDeviceIcon('CAM-001', [113.2, 23.2], {
duration: 1000,
follow: true
})设备状态
scene3d.setDeviceAlarm('CAM-001')
scene3d.setDeviceOffline('CAM-001')setDeviceAlarm(deviceCode) 会把指定设备切换为告警状态,内部使用红色状态色重新生成该设备 Sprite,并返回是否处理成功:
setDeviceAlarm(deviceCode: string): booleanconst ok = scene3d.setDeviceAlarm('CAM-001')
if (!ok) {
console.warn('未找到设备,或设备图标无法更新')
}setDeviceOffline(deviceCode) 会把指定设备切换为离线状态,内部使用灰色状态色重新生成该设备 Sprite,并返回是否处理成功:
setDeviceOffline(deviceCode: string): booleanscene3d.setDeviceOffline('CAM-001')设备查找字段按以下顺序匹配:
properties.deviceCodeproperties.deviceIdproperties.deviceNoproperties.codeproperties.idfeature.id
如果设备 Sprite 还没有被按需创建,状态会先写入缓存的设备 feature;后续调用 showDeviceType() 或 showAllDevices() 显示该设备时,会直接使用更新后的状态。
相机跟随
scene3d.followDevice('CAM-001', {
distance: 10,
duration: 800
})
scene3d.stopFollowDevice()地图与坐标
scene3d.setData(nextFloorData)
scene3d.switchMap(nextFloorData)
const world = scene3d.mapCoordinateToWorld([113.1, 23.1])
const coordinate = scene3d.worldToMapCoordinate(world.x, world.z)切换地图
switchMap(data, options) 是 setData(data, options) 的语义化别名,用于楼层、底图或业务点位整体切换。
switchMap(data: EditorData | FeatureCollection, options?: SceneOptions): void调用后会执行完整重建流程:
- 清理当前地图、标签、业务图标、路径和动画状态
- 重新归一化传入数据
- 根据新底图和业务数据重建坐标投影
- 渲染新底图、区域标签、非设备业务图标和路径
- 缓存设备 feature,设备 Sprite 仍按需创建
- 重新适配相机视角
scene3d.switchMap(floor2Data, {
targetSize: 120,
showAreaLabels: true,
showPaths: false
})切换后之前通过 showDeviceType()、showAllDevices() 设置的设备显隐状态会重置;需要按新楼层重新调用:
scene3d.switchMap(floor2Data)
scene3d.showDeviceType('camera')如果只是新增、移动或替换少量运行时图标,优先使用 createIcon()、moveDeviceIcon()、replaceDeviceIcon(),不需要调用 switchMap() 整体重建。
TDScene 方法速查
数据与生命周期
setData(data: EditorData | FeatureCollection, options?: SceneOptions): void
switchMap(data: EditorData | FeatureCollection, options?: SceneOptions): void
resize(): void
clear(): void
dispose(): void图层与设备显隐
showDeviceType(deviceTypeCode: string, visible?: boolean): number
hideDeviceType(deviceTypeCode: string): number
setAllDeviceVisible(visible?: boolean): number
showAllDevices(): number
hideAllDevices(): number运行时图标
createIcon(image: string | HTMLImageElement | HTMLCanvasElement | THREE.Texture, coordinate: CoordinateLike, info?: object, options?: object): THREE.Sprite | null
addIcon(...args): THREE.Sprite | null
replaceDeviceIcon(image: ImageLike, deviceCode: string, info?: object): boolean
replaceIcon(image: ImageLike, deviceCode: string, info?: object): boolean
moveDeviceIcon(deviceCode: string, endPosition: PositionLike, options?: MoveOptions): boolean
moveIcon(deviceCode: string, endPosition: PositionLike, options?: MoveOptions): boolean设备状态
setDeviceAlarm(deviceCode: string): boolean
setDeviceOffline(deviceCode: string): boolean
replaceDeviceSpriteColor(deviceCode: string, color: string, status?: string): boolean查询与坐标
findObjectIndex(deviceCode: string): number
findObject(deviceCode: string): THREE.Object3D | null
mapCoordinateToWorld(coordinate: [number, number]): { x: number, y: number, z: number } | null
worldToMapCoordinate(x: number, z: number): [number, number] | null
resolveRuntimePosition(position: PositionLike, fallbackY?: number): object | null相机与事件
followDevice(deviceCode: string, options?: FollowOptions): boolean
cameraFollowDevice(deviceCode: string, options?: FollowOptions): boolean
trackDevice(deviceCode: string, options?: FollowOptions): boolean
stopFollowDevice(): void
animateCamera(endPos: THREE.Vector3, endTarget: THREE.Vector3, options?: CameraOptions): boolean
on(type: 'click', callback: (object, feature) => void): (() => void) | null
off(type: 'click'): voidCSS3D 标签
createLabel(component: VueComponent, props?: object, scale?: number): CSS3DSpriteVue CSS3D 标签
const label = scene3d.createLabel(DeviceLabel, {
name: '摄像头 1',
status: '在线'
}, 0.02)
label.position.set(x, y, z)
scene3d.scene.add(label)该方法只创建 CSS3DSprite,不会自动加入场景。
事件
const cleanup = scene3d.on('click', (object, feature) => {
console.log(object, feature)
})
cleanup()
// 或
scene3d.off('click')导出
import {
TDScene,
TDMethods,
createScene3D
} from 'xinrui-3d'TDMethods 提供数据归一化、坐标投影、Mesh/Sprite 创建、资源路径解析和 Three.js 资源释放等底层能力。
