ThingJS 架构

image.png

场景搭建

  • 圈地
  • 添加自定义建筑
  • 设置参考图及其比例(可前置为第一步)
  • 设置建筑外立面
  • 自定义属性
  • 场景导出

    场景加载

    ```javascript // 线上 const app = new Thing.App({ url: “线上场景 URL”, }); // 本地 const app = new Thing.App({

});

  1. <a name="Flibf"></a>
  2. ## UI 元素创建及引入
  3. <a name="XcMXx"></a>
  4. ### ThingJS 界面体系
  5. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1016618/1661825028318-42246508-aa82-4391-bfb8-648c5e858a8a.png#clientId=u14ff5ce6-3738-4&from=paste&height=675&id=u4a4032c2&originHeight=675&originWidth=1408&originalType=binary&ratio=1&rotation=0&showTitle=false&size=213439&status=done&style=none&taskId=u39e46356-2a39-49c3-b193-17e67e61d1f&title=&width=1408)
  6. <a name="XeASG"></a>
  7. #### 3D 容器外的 UI 引入方式
  8. <a name="LmChE"></a>
  9. ##### `iframe` 方式引入
  10. 通过 `document.body.appendChild` 将 `iframe` 嵌入页面(需要对 `iframe` 进行 `position` 定位 )
  11. <a name="rwik9"></a>
  12. ##### `app.domElement.appendChild` 方式引入
  13. 通过 `app.domElement.appendChild` 方式引入创建的 `element` 元素(需要对其进行 `position` 定位)
  14. <a name="qWWyV"></a>
  15. #### 3D 容器内的 UI 引入方式
  16. <a name="xpeHV"></a>
  17. ##### 通过 `UIAnchor` 类型创建
  18. ```javascript
  19. class CreateUIAnchor {
  20. constructor() {
  21. this.ui = null;
  22. this.init();
  23. }
  24. // 初始化 UI
  25. init() {
  26. const template = `
  27. <div class="bubble" id="uianchor">
  28. <span>小车</span>
  29. </div>
  30. `;
  31. const uiElem = document.createElement("template");
  32. uiElem.innerHTML = template;
  33. document.querySelector("#div3d").append(uiElem.content.childNodes);
  34. this.createUI();
  35. }
  36. // 创建 UIAnchor
  37. createUI() {
  38. this.ui = app.create({
  39. type: "UIAnchor",
  40. parent: app.query("car02")[0],
  41. element: this.createElement(),
  42. localPosition: [0, 2, 0],
  43. pivot: [0.5, 1], // UI 元素定位中心, [0, 0] 左上角, [1, 1] 右下角
  44. });
  45. },
  46. // 创建元素
  47. createElement() {
  48. const originElem = document.querySelector("#uianchor");
  49. const newElem = originElem.cloneNode(true);
  50. newElem.style.display = "block";
  51. app.domElement.insertBefore(newElem, originElem);
  52. return newElem;
  53. },
  54. }

通过 Maker 类型创建
  1. class CreateMarker {
  2. constructor() {
  3. this.ui = null;
  4. this.init();
  5. }
  6. // 初始化 UI
  7. init() {
  8. const template = `
  9. <div class="bubble" id="marker">
  10. <span>小车</span>
  11. </div>
  12. `;
  13. const uiElem = document.createElement("template");
  14. uiElem.innerHTML = template;
  15. document.querySelector("#div3d").append(uiElem.content.childNodes);
  16. this.createUI();
  17. }
  18. // 创建 UIAnchor
  19. createUI() {
  20. this.ui = app.create({
  21. type: "Marker",
  22. parent: app.query("car02")[0],
  23. element: document,querySelector("#marker"),
  24. localPosition: [0, 2, 0],
  25. size: 2,
  26. });
  27. }
  28. }

注意:

  • 3D容器外的2D界面布局需使用 position 定位;
  • 3D容器内的顶牌,使用绑定 element 对象方式创建顶牌时,注意样式编写:
    • 样式不能写为模块/组件样式,需写为全局样式;
    • 顶牌 DOM 元素需要隐藏;
    • UIAnchorMarker 的区别是,前者不能被 app 实例管理操作;但销毁方式都是使用 destory 方法。

      ⭐创建物体

      参考 《创建/销毁物体》 章节。

      扩展知识

      坐标点位采集

      通过监听园区的点击事件,获取点击处的坐标点位。
      1. app.on("click", (e) => {
      2. console.log(e.pickedPosition);
      3. });

      创建物体并添加控件

      通过 app.create 创建的物体,可以通过 obj.addControl方法添加由 new THING.AxisTransformControl(obj, type, options, tag) 创建的操作控件,也能通过 obj.removeControl(tag) 移除已添加的控件。 ```javascript // 单击 app.on(“singleClick”, (ev) => { // 创建物体 app.create({ type: “Thing”, url: “”, localPosition: ev.pickedPosition, // 物体的初始位置为点击的点位(父级的坐标系) scale: [3, 3, 3], // 物体缩放 parent: app.level.current, // 父对象为当前层级对象 complete() { // 物体添加位置移动控件 obj.addControl(createTransCtrl(this, “transform”)); // 物体添加角度调整控件 obj.addControl(createTransCtrl(this, “rotate”)); } }); });

// 创建操作控件 function createTransCtrl(obj, type) { const tag = ${type}Control; return new THING.AxisTransformControl(obj, type, { changeStart() { console.log(start ${type}); }, changeEnd() { console.log(${type} end, type === “transform” ? obj.position : obj.angle); // 物体移除操作控件 obj.removeControl(tag); }, }, tag); }

  1. <a name="DZvJZ"></a>
  2. ## 物体效果设置
  3. - 颜色 `obj.style.color = <hex-color> | rgba() | <named-color> | null`
  4. - 透明度 `obj.style.opacity = <number>`
  5. - 勾边/轮廓 `obj.style.outlineColor = <hex-color> | rgba() | <named-color> | null`
  6. - 发光/阴影
  7. - 向外 `obj.style.glow = <boolean>`
  8. - 向内 `obj.style.innerGlow = <boolean>`
  9. - 缩放
  10. - 瞬时 `obj.style.scale = [x, y, z]`
  11. - 逐渐缩放 `obj.style.scaleTo = [x, y, z]`
  12. - 停止缩放 `obj.style.stopScaling()`
  13. - ⭐移动
  14. ```javascript
  15. // 移动到目标点
  16. obj.moveTo({
  17. position: [x, y, z],
  18. time: 1000,
  19. delayTime: 1000,
  20. orientToPath: true, // 移动时朝向移动路径
  21. lerpType: null, // 插值方式,默认线性(与 css 的 transition-time-funtion 类似)
  22. loopType: THING.loopType.Repeat, // 重复
  23. complete(e) {},
  24. });
  25. // 按路径移动
  26. const path = [[0, 0, 0], [1, 2, 3] ...];
  27. obj.movePath({
  28. path, // 移动路径,非 position 目标点
  29. // ....
  30. });

扩展知识

⭐自定义类管理设备状态

  1. // 定义类
  2. // 自定义安全帽类继承物体类
  3. class SafetyHelmet extends THING.Thing {
  4. contructor(app) {
  5. super(app);
  6. },
  7. // 添加自定义属性
  8. alarmed: flase,
  9. // 添加自定义方法
  10. alarm() {
  11. this.alarmed = true;
  12. }
  13. }
  14. // 注册类,参数是(类型名, 构造函数)
  15. THING.factory.registerClass("SafetyHelmet", SafetyHelmet);
  16. // 通过自定义类创建物体
  17. const sh = app.create({
  18. type: "SafetyHelmet",
  19. name: "安全帽001",
  20. url: "",
  21. position: [],
  22. // ...
  23. });
  24. // 查询自定义物体
  25. const shs = app.query(".SafetyHelmet");
  26. // 给自定义物体注册事件,并调用其方法
  27. app.on("click", ".SafetyHelmet", (e) => {
  28. e.object.alarm();
  29. console.log(e.object.alarmed);
  30. });

事件绑定与顶牌控制

事件绑定

ThingJS 中内置的事件有:鼠标事件,键盘事件,层级变化事件等。主要分为全局事件和局部事件;

  • 全局事件
    • app.on(eventName, callback, tagName?)
    • app.on(eventName, condition, callback, tagName?) 其中 condition 可为一种或多种类型,如 ".Thing"".Car || .SafetyHelmet"
  • 局部事件

    • obj.on(eventName, callback, tagName?)
    • obj.on(eventName, condition, callback, tagName?)
    • obj.query(".Marker").on(eventName, callback, tagName?)

      事件移除、暂停,恢复

      可以通过类似事件绑定 on 方法,为物体的事件进行移除 off, 暂停 pauseEvent , 恢复 resumeEvent

      顶牌控制

      标注的类型

  • Marker

    • 图片
    • canvas
    • Element
    • TextRegion 文本
  • WebView: 3D 内嵌页面
  • UIAnchor: 2D 界面(不能设置大小和近大远小 size, keepSize,不能通过 query 查询,无法被程序管理)
  • Widget: 2D 快捷界面库

    层级切换

    ThingJS 提供了 SenseLevel 模块,可以通过 app.level 控制层级。
    常用的 API 有:
app.level.chang(obj) 层级切换到指定对象
app.level.back() 回到上一层级
app.level.current 当前层级对象
app.level.previous 前一个层级对象
EnterLevel 进入层级事件,内含 4 个响应:
- THING.EventTag.LevelSenseOperation 进入层级时的场景控制
- THING.EventTag.LevelFly 进入层级时的飞行控制
- THING.EventTag.LevelSetBackground 进入层级时的天空背景控制
- THING.EventTag.LevelPickedResultFun 进入层级时的 Pick 对象设置
LeaveLevel 退出层级事件,内含 1 个响应:
- THING.EventTag.LevelSenseOperation 进入层级时的场景控制
LevelFlyEnd 层级切换后相机飞行结束事件

⭐相机

相机常用 API

app.camera.position 摄像机位置
app.camera.target 摄像机目标
app.camera.up 摄像机 UP 方向 默认值为 [0,1,0]
app.camera.lookAt(<position> &#124; <object> &#124; null) 摄像机“盯”住某一物体
app.camera.fit() 摄像机自动调整最佳位置(无回调)
app.camera.flyTo(<position> &#124; <object>) 设置摄像机飞行
app.camera.flying() 返回 <boolean> 类型的摄像机飞行状态
app.camera.stopFlying() 设置摄像机停止飞行
app.camera.rotateAround(<position> &#124; <object>) 设置摄像机环绕某点/物体飞行
app.camera.followObject(<object>) 设置摄像机跟随某一物体移动
app.camera.stopFollowObject() 设置摄像机停止跟随物体

相机交互控制

app.camera.inputEnable 是否允许相机交互
app.camera.enablePan 是否允许相机平移
app.camera.enableRotate 是否允许相机旋转
app.camera.enableZoom 是否允许相机缩放
app.camera.mousePanSpeed = 0.1 鼠标平移速度
app.camera.keyPanSpeed = 0.1 键盘平移速度
app.camera.yAngleLimitRange = [0, 360] 相机水平角度范围
app.camera.xAngleLimitRange = [0, 360] 相机垂直角度范围
app.camera.viewMode = "" 相机模式
- THING.CameraView.TopView 顶视图
- THING.CameraView.Normal 3D 视图(默认)

数据对接

  • Ajax
  • JSONP
  • Axios
  • Websocket