likes
comments
collection
share

ThingJSAPI使用总结(超详细)

作者站长头像
站长
· 阅读数 20

在此声明,此文章为本人方便查找使用ThingJsAPI的个人总结,原创API请查看ThingJS官网www.thingjs.com

一、APP对象

  • 创建App

    html文件 <div id="div3d"></div> js文件

    var app = new THING.App({
        container: 'div3d',
        url:'场景的url地址',
        complete: () => {
    
        }
    });
    

    App作为ThingJS的入口,提供了很多功能,详细说明请往下阅读

二、环境和效果控制

  • 改变场景背景色

    //设置背景颜色
    app.background = 0Xffffff;
    //设置背景图片
    app.background = 'imageUrl';
    //设置天空盒
    app.skyBox = 'Night';
    //ThingJs提供的内置天空盒属性
    SunCloud、BlueSky、MilkyWay、Night、CloudySky、Universal、White、Dark
    //取消天空盒效果
    app.skyBox = null;
    
  • 改变园区、建筑、楼层各层级的背景色

    //设置园区的天空盒
    var app = new THING.App({
        skyBox: 'blueSky' //也可以加载本地图片../../images/blueSky
    });
    //设置建筑层级的背景颜色
    app.on(THING.EventType.EnterLevel, '.Building', function () {
        app.skyBox = null;
        app.background = '#BDE2FF';
    })
    //设置楼层层级的背景颜色
    app.on(THING.EventType.EnterLevel, 'Floor', function () {
        app.skyBox = null;
        app.background = 0xffaaee;
    })
    
  • 设置带有随时间变化的天空盒

    app.skyEffect = {
        showHelper: false, //显示光源位置
        turbidity: 10, //光源扩散大小
        rayleigh: 2, //大气散射
        time: 14, //时间 [0~24]
        beta: 30, //水平角度
        mieCoefficient: 0.028, //该数越大,天空越暗
    	mieDirectionalG: 0.9999 //该数越大,太阳越小
    }
    //取消效果
    app.skyEffect = null;
    

    *注意:app.skyBox与app.skyEffect不可同时使用,否则会出现问题

  • 灯光效果

    灯光效果对于场景效果有决定性作用,ThingJS提供了一套通用方案来设置灯光效果
    

    详情请见ThingJs官网--在线开发--灯光

  • 特效模拟(下雨、下雪、着火、爆炸等等)

    1、着火 ThingJSAPI使用总结(超详细)

    //创建火焰粒子
    var car = app.query('car01')[0];
    var fire = app.query('#fire01')[0];
    if (!fire) {
        app.create({
            id: 'fire01', //设定火焰唯一标识
            type: 'ParticleSystem', //设置此物体类实现粒子效果
            url: 'https://thingjs.com/static/particles/fire1',//设置火焰图片
            parent: car, //设置粒子的父物体
            localPosition: [0, 0, 0] //设置粒子相对于父物体的位置
        })
    }
    //销毁火焰
    if (fire) {
        fire.destroy();
    }
    

    2、下雪 ThingJSAPI使用总结(超详细)

    var particle = app.create({
        type: 'ParticleSystem',
        url: 'http://model.3dmomoda.com/models/18112014q3t8aunaabahzxbxcochavap/0/particles',
        position: [0, 40, 0] //设置雪花下落的位置
    });
    

    3、下雨 ThingJSAPI使用总结(超详细)

   var particle = app.create({
       type: 'ParticleSystem',
       url: 'http://model.3dmomoda.com/models/18112113d4jcj4xcoyxecxehf3zodmvp/0/particles',
       position: [0,30, 0]
   });

4、喷水 ThingJSAPI使用总结(超详细)

    var particle = app.query('#water01')[0];
    if (!particle) {
        // 创建喷水效果
        particle = app.create({
            id: 'water01',
            type: 'ParticleSystem',
            url: 'https://thingjs.com/static/particles/water1',
            position: [0, 0, 5]
        });

        // 每一帧旋转 1 度
        particle.on('update', function (ev) {
            ev.object.rotateY(1.0);
        }, '喷水旋转')
    }
    //销毁喷水效果
    if (particle) {
        // 先卸载掉事件
        particle.off('update', null, '喷水旋转');
        // 再删除粒子
        particle.destroy();
    }

三、获取对象——query查询

  • 定义说明

    ThingJs的query方法包括 全局局部

    如何快速找到对象呢?

    1.在父子树上,通过parent,children属性找到要控制的对象 2.在分类对象属性树上,通过类身上分类属性找到要控制的对象(例如:floor.style.color = 0xffffff) 3.使用query方法

  • 全局查询

    let buildings = app.query('.Building');
    let things = app.query('.Thing');
    let floors = app.query('.Floor');
    let rooms = app.query('.Room);
    let markers = app.query('Marker);
    

ThingJSAPI使用总结(超详细)

  • 局部查询

    //根据对象名称name查询
    app.query('car01');
    //根据物体类查询
    app.query('.Thing);
    //根据对象ID查询
    app.query('#0001');
    //根据物体类型属性查询
    app.query('[Deploy]');
    //根据具体物体类型属性查询
    app.query('[视频=video]');
    app.query('["userData/物体类型" = "摄像头"]');
    //查询levelNums属性大于等于2的对象
    app.query('[levelNum >= 2]');
    
  • 对查询结果的操作

    查询结果返回的是selector对象,查询结果可以相加、排除、绑定事件批量操作

    //在查询结果中进行再查询,可实现多个条件的'与操作'
    let result = app.query('.Thing').query('["userData" = "Door"]');
    
    //实现多个条件的'或操作'
    let result = app.query('["userData" = "Door"]');
    app.query('["userData" = "Desk"]').add(result);
    
    //实现'非操作',not操作支持标准的条件
    let building = app.query('.Building');
    building.query('.Floor').not('1层');
    
    //获取第一个对象
    let obj = app.query('.Thing')[0];
    
    //循环选择器对象,数组方式
    var objs = app.query('.Thing');
    for (let i =0; i < objs.length; i++) {
        console.log(objs[i]);
    }
    
    //循环选择器对象,对象方式
    app.query('.Thing').forEach(thing => {
        console.log(thing);
    })
    
    //批量操作对象
    app.query('.Thing').visible = false;
    //对查询到的物体绑定事件
    app.query('.Thing').on('click', function () {
        // do something
    })
    

四、对象创建

  • 创建物体

    var truck = app.create({
        type: "Thing",
        name: 'truck',
        position: [-5, 0, 0],
        url: '模型的地址',
        complete: () => {
            console.log('物体创建成功’);
        }
    })
    //删除物体
    truck.destroy();
    
    

    *注意:如果该物体被销毁后,则会连同该物体的后代一同销毁

  • 创建物体的通用相关参数

    参数说明
    type该物体用什么物体类来创建
    id该物体的编号
    name物体的名字
    position设置世界位置
    localPosition设置在父物体下的相对位置,和position只能选择一个
    angles设置世界下的旋转角度
    localAngles设置在父物体下的相对角度,和angles只能选择一个
    scale设置相对自身坐标下的缩放比例
    parent设置父物体是谁
  • 常见的物体种类

    类型参数
    模型物体Thing
    基本形体Box;Sphere;Plane;Cylinder;Tetrahedron;
    建筑相关Campus;Building;Floor;Room;
    界面相关UIAnchor;Marker;WebView;
    粒子相关ParticleSystem
    线相关Line;RouteLine;
    其他Heatmap
    *注意:创建物体,没有显示填写该物体的父物体时

    1、如果没有开启系统层级,则该物体的默认父级是root(*不会是园区campus) 2、如果开启了层级系统,但是创建物体时没有写parent,则默认当前层级为父级

    例如:j进入楼层层级后再创建Thing,如果没有指定parent,则默认当前层为parent

    var app = new THING.App({
        url: 'models/storehouse'     // 场景地址
    });
    app.on('load', function (ev) {
        var campus = ev.campus;
        var floor = campus.buildings[0].floors[0];
        // 将层级切换到楼层级别
        app.level.change(floor);
        // 第一次进入该楼层后 创建物体
        floor.one(THING.EventType.EnterLevel, function () {
            // 进入楼层层级后再创建 Thing
            // 如果没有指定 parent ,则 parent 默认为该楼层
            var obj = app.create({
                type: 'Thing',
                name: 'truck',
                url: 'http://model.3dmomoda.com/models/66b7f5979ff043afa4e79f7299853a4b/0/gltf/', // 模型地址 
                complete: function (ev) { 
                    // 打印结果:创建的物体父亲为该楼层
                    console.log('thing created: ' + ev.object.parent.id);
                }
            });
        })
    });
    

    *注意:为了方便对物体对象进行管理,建议遵守如下规则 1、显示指明该物体对象的parent 2、针对type参数,创建指定类型的物体 3、url:物体模型资源路径,是Thing物体需要的参数 4、complete:初始化完成函数回调

  • 区分对象物体类

    每个物体类都有它自己的类识别属性 if (b instanceof THING.Building) { console.log('This is a building'); }

  • 对象属性

    一个对象的属性分为4个类别:通用类型、类专属属性、CampusBuilder 中用户添加的属性、用户在程序运行中自行添加的属性

    1、通用属性

    参数说明
    id作为物体的唯一编号
    name用户设置的物体名字,用户可自行定义
    position世界坐标系下的位置
    localPosition父物体坐标下的位置
    angles世界坐标系下三轴旋转角度
    localAngles世界父物体下三轴旋转角度
    scale自身坐标系下三轴缩放量
    visible是否可见(true,false)
    style效果控制(color,outlineColor...)
    children获取所有子物体
    brothers获取所有兄弟排除自己
    parent获取父物体
    parents获取所有父物体(第0位为直属父物体,最后一位为世界根物体)
    2、CampusBuilder 中用户添加的属性
    //从 CampusBuilder 导入的用户自定义的属性可通过 userData 属性访问到
    obj.userData["物体类型"];
    

    3、用户在程序运行中自行添加的属性

    //比如,我们从后台接收到的监控数据,可以直接给对象添加自定义属性 monitorData 来进行存储:
    obj.monitorData = {
        温度:10,
        单位:“摄氏度”
    }
    

    4、类专属属性

    详细参数请查看上面的常见物体种类

五、界面

  • 2Dhtml界面

    UIAnchor 还有一个神奇的功能,虽然是 2D html 界面,我们可以把它连接到 3D 物体上,跟随 3D 物体移动,我们使用 UIAnchor 物体来实现这个功能

    html文件

    <div id="UI">界面内容</div>
    

    js文件

    // 生成一个新面板
    function create_element() {
        var srcElem = document.getElementById('UI');
        var newElem = srcElem.cloneNode(true);
        newElem.style.display = "block";
        app.domElement.insertBefore(newElem, srcElem);
        return newElem;
    }
    //物体顶界面
    var uiAnchor = app.create({
        type: "UIAnchor",
        parent: app.query("car02")[0],
        element: create_element(),
        localPosition: [0, 2, 0],
        pivot: [0.5, 1]
    });
    //根绝位置创建界面
    var uiAnchor0 = app.create({
        type: 'UIAnchor',
        element: create_element(),
        position: [0, 1, 0]
    });
    //删除UIAnchor
    uiAnchor.destroy();
    
    参数说明
    element要绑定的html页面元素
    pivot指定页面的哪个点放到 localPosition 位置上,0.5 相当于 50%
  • 3D界面

    ThingJS 主要提供 Marker 物体和 WebView 物体以支持 3D 空间界面

    1.Marker

    • 创建图片url类型的marker
    app.create({
    type: "Marker",
    offset: [0, 2, 0],
    size: [4, 4],
    url: "https://thingjs.com/static/images/warning1.png",
    parent: app.query("car01")[0]
    });
    
    • 创建canvas类型的marker
    function createTextCanvas(text, canvas) {
        if (!canvas) {
            canvas = document.createElement("canvas");
            canvas.width = 64;
            canvas.height = 64;
        }
    
        const ctx = canvas.getContext("2d");
        ctx.fillStyle = "rgb(32, 32, 256)";
        ctx.beginPath();
        ctx.arc(32, 32, 30, 0, Math.PI * 2);
        ctx.fill();
    
        ctx.strokeStyle = "rgb(255, 255, 255)";
        ctx.lineWidth = 4;
        ctx.beginPath();
        ctx.arc(32, 32, 30, 0, Math.PI * 2);
        ctx.stroke();
    
        ctx.fillStyle = "rgb(255, 255, 255)";
        ctx.font = "32px sans-serif";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText(text, 32, 32);
        return canvas;
    }
    var marker = app.create({
        type: "Marker",
        offset: [0, 2, 0],
        size: 3,
        canvas: createTextCanvas('100'),
        parent: app.query('car02')[0]
    }).on('click', function (ev) {
        var txt = Math.floor(Math.random() * 100);
        ev.object.canvas = createTextCanvas(txt, ev.object.canvas)
    })
    
    • 创建html界面类型的marker html文件
    <div id="buildMarker" class="loadview">
        <img src="img/normalLocation.png" alt="定位图标">
        <span class="buildName"></span>
    </div>
    

    js文件

    var buildings = app.buildings;
    buildings.forEach(function (building) {
        $('#buildMarker .buildName').text('建筑ID:' + building.id);
        // 创建Marker
        var buildingMarker = app.create({
            type: "Marker",
            offset: [0, 5, 0],
            size: 4,
            parent: building,
            keepSize: true,
            element: document.getElementById('buildMarker'),
        });
    })
    
    参数说明
    type设定创建类型marker
    offset设置自身坐标系下偏移量
    size设置marker物体大小
    url图片的url
    canvas接收 canvas 作为贴图显示,创建的canvas函数
    element要绑定的html页面元素
    parent指定marker的父物体
    keepSize控制是否受距离远近影响,true:表示保持大小,false:表示呈现近大远小的效果
    • WebView物体

    我们可以使用 WebView 物体,将其他网站或者页面的内容嵌到 3D 中

    var webView = app.create({
        type: "WebView",
        url: "http://www.thingjs.com",
        position: [0, 10, 0],
        size:16,
        width: 10,
        height: 8,
        isClose: true,
    });
    
    • 其他界面

    快捷界面库、iframey引用界面等详细内容这里不做展示,详情请看ThingJs官网--在线开发--界面

六、控制对象

  • 控制显示\隐藏

    通过设置visible属性直接控制单个物体或对象集合的显隐

    //单个物体
    var car = app.query('car01')[0];
    car.visible = false;
    //对象集合
    var buildings = app.query('.Building');
    buildings.visible = false;
    

    *注意:如果对象有相应的父子关系,那么,当隐藏父级时,相对应的其后代也会隐藏 系统层级默认显隐规则

    • 进入园区级别,显示该园区下的地面(Ground)、建筑的外立面(Facade)以及其他直属物体(Thing)

    • 进入建筑级别,隐藏该建筑的外立面,显示该建筑的楼层(包括子孙) *注意:此时当前建筑并没隐藏,只是隐藏了该建筑的外立面

    • 进入楼层级别,隐藏其他楼层,显示当前楼层的子孙

    案例详情请见ThingJs官网--在线开发--场景--场景显隐规则

  • 控制位置

    描述或控制一个物体的位置,我们在不同情况下会分别使用 3 套坐标系统:

    1.世界坐标系:position 2.父物体坐标系:ocalPosition 3.自身坐标系:translate([0,0,2]) *注意:正常情况下,子物体会随着父物体移动而一起移动,如果想控制子物体不随父物体移动,可通过设置子物体的inheritPosition属性为 false 而实现

  • 控制旋转

    thingJs使用角度控制物体的旋转

    1.世界坐标系下

    obj.angles = [0, 45, 0];//设置世界坐标系下Y轴方向旋转45度
    

    2.父物体坐标系下

    obj.localAngles = [0, 45, 0];//设置父物体坐标系下Y轴向旋转45度
    

    3.自身坐标系下

    //使用rotate,可输入角度和轴向。设置沿给定轴向转一定角度,传入的旋转轴是自身坐标系下的轴方向
    obj.rotate(45, [0,1,0]);
    
    //沿自身x轴向旋转,等同于 obj.rotate( 30, [1,0,0]) 
    obj.rotateX(30);
    
    //沿自身y轴向旋转,等同于 obj.rotate( 90, [1,0,0]) 
    obj.rotateY(90);
    
    //沿自身z轴向旋转,等同于 obj.rotate( -45, [1,0,0]) 
    obj.rotateZ(-45);
    

    4.使用lookAt接口方法,使得物体的观察方向一直对准一个位置

    //让物体面向[0,1,0],该坐标是世界坐标系下
    obj.lookAt([0,1,0])
    //一直看向摄像机
    obj.lookAt(app.camera);
    //一直看向一个物体
    obj.lookAt(obj);
    //让物体一直面向一个物体,同时物体沿自身Y轴向再旋转90度
    obj.lookAt(obj, [0,90,0]);
    //取消lookAt的功能
    obj.lookAt(null);
    

    正常情况下,子物体会随着父物体旋转而一起旋转,如果想控制子物体不随父物体旋转,可通过设置子物体的 inheritAngles 属性为 false 而实现

  • 控制缩放

    只提供自身坐标系下的缩放控制

    obj.scale = [1,2,1];//让物体在Y轴方向上放大两倍
    //默认原始状态[1,1,1]
    
    //鼠标触摸控制楼层缩放
    app.query('.Floor').forEach(floor => {
        app.on('mouseEnter', function () {
            floor.scale = [2,2,2];
        });
        app.on('mouseLeave', function () {
            floor.scale = [1,1,1];
        })
    })
    

    正常情况下,子物体会随着父物体缩放而一起缩放,如果想控制子物体不随父物体缩放,可通过设置子物体的 inheritScale 属性为 false 而实现

  • 控制移动

    • 物体连接
    var car = app.query('car01')[0];
    // 旋转180度,并保存下叉车当前位置
    car.rotateY(180);
    car.initPos = car.position;
    
    // 创建箱子
    var box = app.create({ type: 'Box', position: [22, 0, 1], center: 'Bottom' });
    
    // 叉车移动到箱子位置
    car.moveTo({
        position: box.position,
        time: 2000,
        // 移动到目的地之后把箱子放在叉车的叉臂上
        complete: function () {
            // 可通过 subNodes 获取所有的 子节点
            //console.log(car.subNodes);
    
            // 叉车拿箱子
            car.add({
                object: box,
                basePoint: 'Box001_0',
                offset: [0, 0.2, -1]
            });
    
            // 旋转180度
            car.rotateY(180);
    
            // 返回原地
            car.moveTo({
                position: car.initPos,
                time: 2000,
                complete: function () {
                    // 将箱子从叉车上移除
                    // 即重新将箱子 add 到另一父物体下
                    campus.add(box);
    
                    car.rotateY(180);
                    car.moveTo({
                        position: [22, 0, 0],
                        time: 2000
                    })
    
                }
            });
        }
    });
    
    • 沿路径移动
    // 创建一个路径
    var path = [
        [35,1,10],
        [-10,1,10],
        [-30,1,10],
    ];
    //创建一条线
    var line = app.create({
        type: 'RouteLine',
        points: path,
        style: {
            color: '#58DEDE'
        }
    });
    // 让车沿路线运动
    var car = app.query('car01')[0];
    // 延迟500毫秒后进行
    setTimeout(function () {
        car.movePath({
            'orientToPath': true,      // 物体移动时沿向路径方向
            'orientToPathDegree': 90,   // 沿向路径方向偏移一定角度
            'path': path,               // 路径点数组
            'time': 12000,              // 路径总时间
            'loopType': THING.LoopType.Repeat // 是否循环
        });
    }, 500);
    

    取消移动

    obj.stopMoving();
    
  • 控制效果

    style属性大部分物体类型,都可以通过style来设置效果

    设置透明度(虚化)

    obj.style.opacity = 0.5;(0-1)
    

    设置颜色效果及勾边效果

    obj.style.color = "#00ff00";//可使用十六进制或者rgb
    obj.style.wireframe = true;//给物体开启线框模式
    obj.style.wireframe = false;//给物体关闭线框模式
    obj.style.outlineColor = "#00ff00";//可使用十六进制或者rgb
    obj.style.boundingBox = true;//打开包围盒
    obj.style.boundingBoxColor = '#00ff00';//设置包围盒颜色
    obj.plan.style.color = '#00ff00';//设置楼层地板颜色
    obj.wall.style.color = '#00ff00';//设置楼层墙体颜色
    

    设置动画效果

    obj.style.alwaysOnTop = true;//是否让物体显示在最上层
    obj.fadeIn();//淡入效果
    obj.fadeOut();//淡出效果
    

    设置楼层展开合并

    // 水平展开或垂直展开(可尝试修改后运行看效果)
    var isHorzMode = false;
    // 是否显示天花板
    var isHideRoof = true;
    
    // 展开建筑楼层
    function test_expand() {
        building.floors.visible = true;
        building.facades.visible = false;
        building.expandFloors({
            'time': 1000,
            'length': isHorzMode ? -30 : 10,
            'horzMode': isHorzMode,
            'hideRoof': isHideRoof,
            'complete': function () {
                console.log('expand complete ');
            }
        });
    }
    
    // 恢复建筑楼层
    function test_unexpand() {
        building.unexpandFloors({
            'time': 500,
            'complete': function () {
                building.floors.visible = false;
                building.facades.visible = true;
                console.log('unexpand complete ');
            }
        });
    }
    
  • 坐标转换

    三套坐标体系坐标相互之间的转换,方便控制或获取物体位置

    // 将输入的物体自身坐标系下坐标转换成世界坐标
    obj.selfToWorld(pos) 
    // 将输入的世界坐标转换成物体自身坐标系下坐标
    obj.worldToSelf(pos) 
    // 将输入的世界坐标转换成父物体坐标
    obj.worldToLocal(pos)
    // 将输入的父物体坐标转换成世界坐标
    obj.localToWorld(pos)
    
  • 位移旋转缩放动画

    我们可以使用 moveTo 设置一个移动动画,rotateTo 设置一个旋转动画,scaleTo 设置一个缩放动画,用 movePath 可设置让物体沿一条路径移动

    moveTo

    obj.moveTo({
        position: [10, 0, 10],
        orientToPath: true,
        orientToPathDegree: 90,
        time: 12000,
        delayTime: 500,
        complete: function() {
            console.log("moveto completed");
        }
    });
    
    参数说明
    position在世界坐标系下设置目标位置
    offsetPosition在自身坐标系下设置目标位置
    orientToPath物体是否朝向移动的方向
    orientToPathDegree沿向路径方向偏移一定角度
    time移动总时间
    speed速度,和time任选其一设置
    lerpType插值类型,参见本页 下方 ,默认是null,等同于THING.LerpType.Linear.None
    delayTime延迟移动
    loopType循环类型,参见本页 下方 ,默认是 null,等同于 'no',或者是THING.LoopType.No
    complete完成时的回调, * repeat 和 pingpong模式下没有回调
    rotateTo
    obj.rotateTo({
        'angles': [0, 90, 0],   
        'time': 12000,           
        'delayTime': 500,         
        'complete': function () {
            console.log('scale completed');  
        }
    });
    //angles:设置旋转角度
    //除了 orientToPath 和 orientToPathDegree 这两个参数不需要,其它参数和 moveTo 是一样的
    

    scaleTo

    obj1.scaleTo({
        scale: [1, Math.randomFloat(2.0, 5.0), 1],
        time: 5000,
    });
    //scale :在自身坐标系下三个轴向目标缩放值
    //其它参数和 rotateTo 是一样的
    
  • 模型动画

    var cabinet = app.query('.Thing')[0];
    
    //查看 模型有哪些动画
    console.log(cabinet.animationNames);
    //开始播放动画
    cabinet.playAnimation('open1');
    
    cabinet.playAnimation({
        name: 'open1',
        reverse: true,//倒播动画
        loopType: THING.LoopType.Repeat,//循环播放动画
    });
    
    //同时播放多个动画
    cabinet.playAnimation({
        name: ['open1', 'open2'], // 同时播放多个动画
        loopType: THING.LoopType.PingPong, // 先正向播放后,再反向播放,如此循环
        speed: 0.4 //设置播放速率
    });
    
  • 物体编辑

    显示旋转控制轴

    referenceObj = app.query('car03')[0];
    
    //单击选中物体
    referenceObj.on('singleclick', function (ev) {
        var object = ev.object;
        app.selection.select(object);
    });
    
    //添加、显示旋转控制轴
    referenceObj.on(THING.EventType.Select, function (ev) {
        var object = ev.object;
        // 判断物体是否有控制轴 
        if (!(object.hasControl('axisControl'))) {
            //添加控制轴
            object.addControl(new THING.AxisTransformControl(object), 'axisControl');
            // 改变控制轴 Mode 为旋转模式
            object.getControl('axisControl').mode = 'rotate';
    
        } else {
            // 将控制轴设置为显示
            object.getControl('axisControl').visible = true;
        }
    });
    
    //  隐藏旋转控制轴
    referenceObj.on(THING.EventType.Deselect, function (ev) {
        var object = ev.object;
        // 物体如果有控制轴
        if (object.hasControl('axisControl')) {
            // 控制轴设置为隐藏
            object.getControl('axisControl').visible = false;
        }
    });
    

    拖拽物体,改变位置

    var testCar = app.query('car01')[0];
    
    // 拖拽物体 改变物体位置
    testCar.on(THING.EventType.Drag, function (ev) {
        if (ev.picked)
            ev.object.position = ev.pickedPosition;
    }, '物体拖拽');
    
    // 拖拽结束
    testCar.on(THING.EventType.DragEnd, function (ev) {
        console.log('拖拽结束');
        // do something
    }, '结束拖拽')
    

    单击清空选择集

    //单击清空选择集
    app.on('singleclick', function (ev) {
        var object = ev.object;
        // 当 Pick 的物体不在 Selection集 中
        if (!ev.picked || !app.selection.has(object)) {
            // 清空 Selection集
            app.selection.clear();
        }
    }, '单击清空选择集');
    

    拖拽模型到场景并创建、编辑

    /**
    * 说明:物体场景编辑功能
    * 操作:拖动左上方按钮来创建场景中的物体,并且进行编辑。
    *      鼠标右键: 取消移动轴
    */
    THING.Utils.dynamicLoad(['examples/css/sample_ThingEdit.css'], function () {
        var panel =
            `<div class="model">
                <ul>
                    <li title="卡车" id="truck" data-url="https://www.thingjs.com/static/models/truck">
                        <img src="/guide/image/model/mox_01.jpg" alt="">
                    </li>
                    <li title="铲车" id="forklift" data-url="https://www.thingjs.com/static/models/forklift2">
                        <img src="/guide/image/model/mox_02.jpg" alt="">
                    </li>
                </ul>
            </div>`
        $('#div3d').append($(panel));
    });
    var app = new THING.App({
        url: 'https://www.thingjs.com/static/models/simplebuilding'
    });
    
    // 加载场景后执行
    var thing = null;
    app.on('load', function () {
        // 按钮拖拽事件(dragIcon为教程页面提供的工具)
        document.getElementsByClassName('model')[0].style.display = 'block';
        dragIcon('truck', dragStart);
        dragIcon('forklift', dragStart);
    });
    
    // 物体跟随鼠标移动
    app.on('mousemove', function (ev) {
        if (thing) {
            var worldPosition = app.picker.pickWorldPosition(ev.clientX, ev.clientY);
            if (!worldPosition) {
                worldPosition = app.camera.screenToWorld(ev.clientX, ev.clientY);
            }
    
            thing.position = worldPosition;
        }
    });
    
    // 鼠标右键点击取消所有控制轴
    app.on('mousedown', function (ev) {
        if (ev.button == 2) {
            app.query('.Thing').forEach(function (object) {
                object.removeControl('axisControl');
            });
        }
    });
    
    // 鼠标抬起取消物体移动
    app.on('mouseup', function (ev) {
        if (thing) {
            // 重新让物体开启拾取
            thing.pickable = true;
    
            // 点击物体可以开启/关闭控制轴
            thing.on('singleclick', function (ev) {
                var object = ev.object;
                if (object.hasControl('axisControl')) {
                    object.removeControl('axisControl');
                }
                else {
                    object.addControl(new THING.AxisTransformControl(object), 'axisControl');
                }
    
                app.query('.Thing').not(object).forEach(function (object) {
                    object.removeControl('axisControl');
                });
            });
    
            // 出发点击事件来给物体添加控制轴
            thing.trigger('singleclick');
    
            thing = null;
        }
    
        // 开启摄像机转动
        app.camera.enableRotate = true;
    })
    
    // 创建物体并且开始拖动
    function dragStart(url, x, y) {
        // 创建物体
        thing = app.create({
            type: 'Thing',
            url,
            position: app.camera.screenToWorld(x, y),
        });
    
        // 防止拖动过程中获取时间坐标的时候,对位置进行重复拾取
        thing.pickable = false;
    
        // 关闭摄像机转动
        app.camera.enableRotate = false;
    }
    
    // 关联拖动图标
    function dragIcon(id, callback1) {
        var dom = document.getElementById(id);
        var url = dom.getAttribute('data-url');
        dom.onmousedown = function (e) {
            callback1(url, e.pageX, e.pageY);
        }
    }
    

七、拾取和选择

1.通过属性和接口获取鼠标拾取(Pick)的物体

if (app.picker.isChanged()) {
    //通过app.picker.objects 得到当前拾取的物体
    console.log(app.picker.objects);
    //通过app.picker.previousObjects 得到之前拾取的物体
    console.log(app.picker.previousObjects);
}

2.通过事件获取鼠标拾取(Pick)的物体

// 鼠标拾取的物体勾边
app.on(THING.EventType.Pick, '.Thing' ,function(ev) {
    ev.object.style.outlineColor = '#FF0000';
});
// 鼠标离开物体取消勾边
app.on(THING.EventType.Unpick,'.Thing', function(ev) {
    ev.object.style.outlineColor = null;
});

//也可以通过 mouseover 和 mouseleave 来实现
// 改变颜色
app.on(THING.EventType.MouseOver,'.Thing', function () {
    app.picker.objects.style.color = '#ff0000';
});

// 还原颜色
app.on(THING.EventType.MouseLeave, '.Thing',function () {
    app.picker.previousObjects.style.color = null;
});

//当 Pick 发生变化时会触发 PickChange 事件,也可以通过事件的回调参数获取当前和之前的拾取物体
app.on(THING.EventType.PickChange,function (ev) {
    ev.objects.style.color = '#ff0000';
    ev.previousObjects.style.color = null;
});

3.区域 Pick 物体

//由于框选比较消耗性能,因此预先设置框的“候选集”,只在候选集中框选
var things = app.query('.Thing');
app.picker.areaCandidates = things;

//启动框选 传入 鼠标按下时开始框选的屏幕坐标
app.picker.startAreaPicking({
    x: x,
    y: y
});
//结束框选
app.picker.endAreaPicking();

//通过 pickedResultFunc 设置拾取对象回调函数
app.on(THING.EventType.EnterLevel, '.Building', function () {
    app.picker.pickedResultFunc = function () {
        //do something
    }
})

4.选择物体

//将物体加入到选择集中
app.selection.select(obj);
//将物体从选择集中删除
app.selection.deselect(obj);
//清空选择集
app.selection.clear();

八、事件

ThingJS内置了很多事件,包含:鼠标点击、键盘输入、层级变化等。事件分为全局事件和局部事件

全局绑定,通过app.on绑定事件

// 不添加条件,鼠标点击即可触发
app.on('click', (ev) => {
    console.log(ev, 'you click')
})
// 添加指定条件,比如:只有Thing类型物体才会触发
app.on('click', '.Thing', (ev) => {
    console.log('you click' + ev.object.id)
})
// 添加多重条件,比如:对建筑、楼层触发点击事件
app.on('click', '.Building || .Floor', (ev) => {
    console.log('you click' + ev.object.type)
})

局部绑定,针对一个对象或Selector集合,通过on接口绑定

// 当这个对象被点击,即可触发
const obj = app.query('car')
obj.on('click', (ev) => {
    console.log(ev.object.name)
})
// 添加特定条件,比如:当这个对象为marker类型,或带有marker类型的子孙,即可触发
obj.on('click', '.Marker', (ev) => {
    console.log(ev.object.name)
})
// 查询到obj下所有的marker物体(集合),注册click事件
obj.query('.Marker').on('click', (ev) => {
    console.log(ev.object.name)
})

卸载事件

// 注册事件
app.on('click', () => {
    console.log('you click')
})
// 卸载事件
app.off('click')
// 注册带tag的事件
app.on('click', '.Building', () => {
    console.log('you click')
}, 'tagname')
// 卸载所有Building的点击事件
app.off('click', '.Building')
// 只卸载带有该tag名的Building的点击事件
app.off('click', '.Building', 'tagname')

暂停和恢复事件

app.on('click', '.Building', (ev) => {
    console.log('you click')
}, 'tagname)
// 暂停事件
app.pauseEvent('click', '.Building', 'tagname')
// 恢复事件
app.resumeEvent('click', '.Building', 'tagname')

注意:卸载/暂停/恢复事件时,第二个参数必须传条件,如果没有条件,有需要传tag,将条件传null

ThingJSAPI使用总结(超详细)

九、摄像机

了解摄影机的基本操作

app.camera.position; 镜头位置
app.camera.target; 目标点(被拍摄物位置)
app.camera.lookAt(); “盯着”某一位置或物体看(可传参数:位置、对象、null)
app.camera.fit(); 设置最佳看点(注意:不能设置回调)
app.camera.flyTo(); 设置摄像机飞行
app.camera.flying; 判断摄影机是否在飞行(返回值为布尔值)
app.camera.stopFlying(); 停止摄影机飞行
app.camera.rotateAround(); 让摄影机环绕某坐标点或某物体旋转飞行
app.camera.followObject(obj); 摄影机跟随物体
app.camera.stopFollowingObject(); 停止摄影机跟随物体
app.camera.inputEnabled = false; 关闭所有默认交互操作(旋转、平移、缩放)
app.camera.enablePan = false; 关闭平移操作
app.camera.enableRotate = false; 关闭旋转操作
app.camera.enableZoom = false; 关闭缩放操作
app.camera.mousePanSpeed = 10; 摄像机鼠标平移速度(默认值0.1)
app.camera.keyPanSpeed = 10; 摄像机键盘平移速度(默认值0.1)
app.camera.yAngleLimitRange =[30, 60]; 摄影机水平角度范围([最大值,最小值])
app.camera.xAngleLimitRange = [0, 90]; 摄影机俯仰角度范围
app.camera.viewMode = 属性值; 摄影及观察模式(2D/3D切换)
THING.CameraView.TopView   顶视图
THING.CameraView.Normal     3D视图

转载自:https://juejin.cn/post/7244033665209876541
评论
请登录