界面

ThingJS 是一个先进的 PaaS 开发平台,开发者可以方便、安全地基于云端的各种工具组件随时随地进行开发。

ThingJS 界面概述

为了便于开发者在ThingJS下进行界面开发,ThingJS 提供的界面体系结构目录如下:

上述ThingJS界面体系中,进行3D场景可视化的区域,我们定义为3D容器,如下图所示:

在3D“容器”内

提供了3D和2D的界面展示能力,如下图所示:

3D 界面

  • Marker:可以将图标、Canvas绘制的图片,展现在3D场景中或绑定在3D物体上。
  • WebView:可以将页面嵌入到3D场景中。

2D 界面

  • 原生界面:用户可以使用js代码编写原生的界面,将dom元素插入到相应的节点中。
  • 快捷界面库:内置各种组件模块,供用户进行拼接组装使用。
  • UIAnchor:可以将普通的2D界面“挂接”到某个3D物体对象上,使之随物体移动。

在3D“容器”外

提供通栏组件(如上通栏、侧通栏)。如果用户想基于 ThingJS 做一套独立的应用系统,可使用通栏组件作为系统级别的菜单。

3D 界面

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

Marker 物体

Marker 物体可以添加一个图片放置到你希望的位置,也可以将这个图片作为孩子添加到对象身上,随着对象一同移动。

例子 1:

 app.create({
    type: "Marker",
    offset: [0, 2, 0],
    size: [4, 4],
    url: "https://thingjs.com/static/images/warning1.png",
    parent: app.query("car01")[0]
}); 

参数:

  • type : 通知系统创建 Marker 物体;
  • offset : 设置自身坐标系下偏移量为[0, 2, 0];
  • size : 设置 Marker 物体大小,也可以添单独数字如 4,等同于[4,4],大小是以米计算的;
  • url : 图片的 url;
  • parent :指定 Marker 的父物体;

运行结果见下图。Marker 默认是受距离远近影响,呈现近大远小的 3D 效果,也会在 3D 空间中实现前后遮挡。

例子 2:

app.create({
    type: "Marker",
    offset: [0, 8, 0],
    size: 2,
    keepSize: true,
    url: "https://thingjs.com/static/images/reminder.png",
    parent: app.query(".Building")[1]
}); 

参数:

  • keepSize: 控制是否受距离远近影响,呈现近大远小的 3D 效果。如果设置 true,表示保持大小,不随距离近大远小,此时 size 的单位是屏幕的像素点;
  • offset : 设置自身坐标系下偏移量为[0, 2, 0];
  • size : 设置 Marker 物体大小,也可以添单独数字如 4,等同于[4,4],大小是以米计算的;
  • url : 图片的 url;
  • parent :指定 Marker 的父物体;

运行结果见下图:

我们还可以使用 h5 的 canvas 手动创建动态图。

例子 3:

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;
}

app.on('load', function (ev) {
    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)
    })
}) 

参数:

  • canvas: 接收 canvas 作为贴图显示

运行结果见下图,在 Marker 上点击时,会改变标记上的数字:

WebView 物体

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

例子 4:

 var webView01 = app.create({
    type: 'WebView',
    url: 'https://www.thingjs.com',
    position: [10, 13, -5],
    width: 1920 * 0.01, // 3D 中实际宽度 单位 米
    height: 1080 * 0.01, // 3D 中实际高度 单位 米
    domWidth: 1920, // 页面宽度 单位 px
    domHeight: 1080// 页面高度 单位 px
}); 

2D html 界面

JS 编写原生界面

var template =
`<div style='position:absolute;left:20px;top:20px;padding: 8px;width:100px;text-align: center;background: rgba(0,0,0,0.5);'>
<p id="p1" style='color:white'>Hello World!</p>
<button style='margin:4px;padding:4px' onclick='changeLevel()'>Into Level</button></div>`;// 插入到 ThingJS 内置的 2D 界面 div 中$('#div2d').append($(template));

查看示例

ThingJS 为了让大家快速编写界面,我们提供一个“快捷界面库”,可快速创建界面。

UIAnchor

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

示例如下:

var uiAnchor = app.create({
    type: "UIAnchor",
    parent: app.query("car02")[0],
    element: document.getElementById("XXXX"),
    localPosition: [0, 2, 0],
    pivot: [0.5, 1]
}); 

参数:

  • element :要绑定的页面的 element 对象
  • pivot :指定页面的哪个点放到 localPosition 位置上,0.5 相当于 50%

查看示例

删除UIAnchor方法为:

uiAnchor.destroy(); 
help
注意事项:

删除后,其对应的 panel 也会被删除

显示和隐藏UIAnchor方法为:

uiAnchor.visible = true / false; 

通过 js 编写的界面:

可以利用 UIAnchor 连接到 3D 物体上。

也可以通过快捷界面库,创建 Panel 以 UIAnchor 的方式连接到物体上。

查看示例

快捷界面库

THING.widget 是一个支持动态数据绑定的轻量级界面库。

可通过界面库中的 Panel 组件创建一个面板,并可向该面板中添加文本、数字、单选框、复选框等其他组件。

效果如下:

创建面板

var panel = new THING.widget.Panel({
    // 设置面板样式
    template: 'default',
    // 角标样式
    cornerType: "none",
    // 设置面板宽度
    width: "300px",
    // 是否有标题
    hasTitle: true,
    // 设置标题名称
    titleText: "我是标题",
    // 面板是否允许有关闭按钮
    closeIcon: true,
    // 面板是否支持拖拽功能
    dragable: true,
    // 面板是否支持收起功能
    retractable: true,
    // 设置透明度
    opacity: 0.9,
    // 设置层级
    zIndex: 99
}); 
  • width: 如果写百分比字符串则表示相对宽度(相对于3D容器的宽度)
  • template:目前,模板样式提供两个样式 default 和 default2,如下图:
  • cornerType: cornerType 是指角标样式,依次是:没有角标 none ,没有线的角标 noline ,折线角标 polyline ;依次见下图:
help
注意事项:

角标样式都不区分大小写

如果 panel 面板设置了关闭按钮 则点击关闭按钮时 会将面板设置为隐藏,如需再次打开该面板 则调用 panel.visible = true; 显示面板即可。

面板属性及方法介绍

// 获取面板标签
panel.domElement;

// 修改面板标题
panel.titleText='修改标题';

// 设置/获取面板相关属性
panel.visible = true / false;
panel.position = [10, 10];//设置panel面板的位置
panel.zIndex = 9;
panel.opacity = 0.5;

// 删除面板
panel.destroy(); 

面板事件

// 常用事件类型均支持
panel.on("click", callback);
// 'close'事件为面板关闭时触发
panel.on("close", callback); 

面板中的数据 可通过各组件实现双向绑定

var dataObj = {
    pressure: "0.14MPa",
    temperature: "21°C",
    checkbox: { 设备1: false, 设备2: false, 设备3: true, 设备4: true },
    radio: "摄像头01",
    open1: true,
    height: 10,
    maxSize: 1.0,
    iframe: "https://www.3dmomoda.com",
    progress: 1,
    img: "https://www.thingjs.com/guide/image/new/logo2x.png",
    button: false
}; 

逐条添加组件

var press = panel.addString(dataObj, 'pressure').caption('水压').isChangeValue(true);
var height = panel.addNumber(dataObj, 'height').caption('高度');
var maxSize = panel.addNumberSlider(dataObj, 'maxSize').step(0.25).min(1).max(10);
var open1 = panel.addBoolean(dataObj, 'open1').caption('开关01');
var radio = panel.addRadio(dataObj, 'radio', ['摄像头01', '摄像头02']);
var check = panel.addCheckbox(dataObj, 'checkbox').caption({ "设备2": "设备2(rename)" });
var iframe = panel.addIframe(dataObj, 'iframe').caption('视屏');
var img = panel.addIframe(dataObj, 'img').caption('图片');
var button = panel.addImageBoolean(dataObj, 'button').caption('仓库编号').url('https://www.thingjs.com/static/images/example/icon.png'); 

界面库可设置 `Caption` 的字体颜色:如下例所示:

var press = panel.addString(dataObj, 'pressure').caption("水压").isChangeValue(true);
var water = panel.addString(dataObj, 'temperature').caption("水温").isChangeValue(true);
var check = panel.addCheckbox(dataObj, 'checkbox').caption({ "设备2": "设备2(rename)" });
var open1 = panel.addBoolean(dataObj, 'open1').caption("开关01");
var height = panel.addNumber(dataObj, 'height').caption("高度");
var maxSize = panel.addNumberSlider(dataObj, 'maxSize').caption("maxSize").step(0.25).min(1).max(10);
var iframe = panel.addIframe(dataObj, 'iframe').caption('视屏'); 

效果如下所示:

删除组件

panel.remove(press);
panel.remove(radio);
...... 

组件方法介绍

组件通用方法:

// 设置名称
element.caption("水压");
// 设置值是否允许改变
element.isChangeValue(true);
// 设置显隐
element.show(true); 

NumberSlider 组件还有如下方法:

// 设置滑动器步幅
element.step(0.25);
// 设置滑动器最小值
element.min(-10);
// 设置滑动器最大值
element.max(10);
// 将绝对数值转为百分比
element.isPercentage(true); 

Iframe 组件还有如下方法:

// 设置iframe高度
element.setHeight("300px"); 

组件事件

change 事件

press.on("change", function(ev) {
    console.log(ev);
});
//特别注意 checkBox 返回的是数组。所以 是check[0], 其他都是对象
check[0].on("change", function(ev) {
    console.log(ev);
}); 

快捷界面库示例

这里我们将列举几个界面的示例,供大家参考学习。

图标按钮面板

创建图片按钮面板:

var toolbar = new THING.widget.Panel({ width: "163px"}); 
help
注意事项:

图片按钮面板可设置两种标题的展示方式,通过 captionPos 进行设置,默认显示在图片下方,也可以设置成鼠标滑入效果。

// 设置为鼠标 hover 效果:
var toolbar = new THING.widget.Panel({ width: "163px",captionPos:'hover'}); 

如下图所示:

面板中的数据:

//绑定物体
var obj = {
    warehouseCode: false,
    temperature: false,
    humidity: false,
    statistics: false,
    status: false,
    insect: false,
    cerealsReserve: false,
    video: false,
    cloud: true,
    orientation: true
}; 

添加按钮并引入字体图标文件:

var button0 = toolbar.addImageBoolean(dataObj, 'warehouseCode').caption('仓库编号').url('#momoda_lc-icontubiao10');
var button1 = toolbar.addImageBoolean(dataObj, 'temperature').caption('温度检测').url('#momoda_lc-icontubiao2');
var button2 = toolbar.addImageBoolean(dataObj, 'humidity').caption('湿度检测').url('#momoda_lc-icontubiao5')
var button3 = toolbar.addImageBoolean(dataObj, 'statistics').caption('能耗统计').url('#momoda_lc-icontubiao20');
var button4 = toolbar.addImageBoolean(dataObj, 'status').caption('保粮状态').url('#momoda_lc-icontubiao');
var button5 = toolbar.addImageBoolean(dataObj, 'insect').caption('虫害').url('#momoda_lc-icontubiao11');
var button6 = toolbar.addImageBoolean(dataObj, 'cerealsReserve').caption('粮食储存').url('#momoda_lc-icontubiao21');
var button7 = toolbar.addImageBoolean(dataObj, 'video').caption('视屏监控').url('#momoda_lc-icontubiao9');
var button8 = toolbar.addImageBoolean(dataObj, 'cloud').caption('温度云图').url('#momoda_lc-icontubiao16');
var button9 = toolbar.addImageBoolean(dataObj, 'orientation').caption('人车定位').url('#momoda_lc-icontubiao10'); 

按钮添加注册事件:

button0.on('change', function (state) {
    // boolean 类型 返回按钮是否是点击状态
    console.log(state);
}); 

面板效果图:

Tab 面板

创建图片按钮面板:

var panel = THING.widget.Panel({
    template: "default",
    hasTitle: true,
    titleText: "粮仓信息",
    closeIcon: true,
    dragable: true,
    retractable: true,
    width: "380px"
}); 

面板中的数据:

var obj = {
    '基本信息信息': {
        '品种': "小麦",
        '库存数量': "6100",
        '保管员': "张三",
        '入库时间': "19:02",
        '用电量': "100",
        '单仓核算': "无"
    },
    '粮情信息': {
        '仓房温度': "26",
        '粮食温度': "22"
    },
    '报警信息': {
        '温度': "22",
        '火灾': "无",
        '虫害': "无"
    },
    '添加组件': {
    }
}; 

数据通过 `addTab` 添加到面板中:

panel.addTab(obj); 

通过 `link` 添加到 Tab 的某个标签页中:

// 加载复选框组件
var check = panel.addCheckbox(dataObj, 'checkbox').caption({ "设备2": "设备2(rename)" }).link("添加组件");
// 复选框需逐个添加change事件
check[0].on('change', function(ev) {
    console.log(ev);
});
check[1].on('change', function(ev) {
    console.log(ev);
})
// 加载单选框组件
var radio = panel.addRadio(dataObj, 'radio', ['摄像头01', '摄像头02']).link("添加组件");
radio.on('change', function(ev) {
    console.log(ev);
})
// 加载开关组件(适用于Boolean类型数据)
var open1 = panel.addBoolean(dataObj, 'open').caption('开关01').link("添加组件");
open1.on('change', function(ev) {
    console.log(ev);
})

// 加载数字型进度条组件
var numberSlider = panel.addNumberSlider(dataObj, 'value').step(0.5).min(1).max(10).isChangeValue(true).link("添加组件");
numberSlider.on('change', function(ev) {
    console.log(ev);
})

// 加载iframe组件
var iframe = panel.addIframe(dataObj, 'iframe').caption('视频').link("添加组件");
// 设置iframe高度
iframe.setHeight("400px"); 

tab 面板效果图:

进度条

进度条这里主要有两种:

  1. 数值型进度条

    创建Panel:

    var panel = new THING.widget.Panel({
        titleText: "数值型进度条",
        width: '400px',
        hasTitle: true
    }); 

    添加进度条并绑定数据:

    panel.position = [10, 10];
    panel.addNumberSlider(dataObj, '海拔').step(1).min(0).max(123);
    panel.addNumberSlider(dataObj, '气温').step(1).min(-20).max(40).isChangeValue(true).on('change', function (value) {
        console.log('气温 ' + value);
    });
    panel.addNumberSlider(dataObj, '人口数量').step(1).min(0).max(123).isChangeValue(true);
    panel.addNumberSlider(dataObj, '人口比例').step(1).min(0).max(123).isChangeValue(true).isPercentage(true); 

    参数如下:

    • isChangeValue 可与进度条交互滑动;
    • isPercentage 将绝对数值转为百分比。

    数值型进度条效果图:

  2. 导航类进度条

    创建Panel:

    var panel2 = new THING.widget.Panel({
        titleText: "导航型进度条",
        width: '400px',
        hasTitle: true
    }); 

    添加进度条并绑定数据:

    var progress = bar.addProgress(dataObj, 'progress', [
        { name: '2号楼', describe: '教学楼' },
        { name: '3号楼', describe: '实验楼' },
        { name: '5号楼', describe: '室内篮球场' },
        { name: '餐厅', describe: '五星级' },
        { name: '大讲堂', describe: '开讲了' }
    ]); 

    进度条变化:

    progress.on('change', function (id) {
        console.log(id);
    }); 

    进度条单位播放时间:

     progress.time(3000) // 单位为毫秒,默认为5000ms 

    更新:

    setInterval(function () {
        if (dataObj.progress >= 4)
            dataObj.progress = 0;
        else
            dataObj.progress++;
    }, 1000); 

    导航型进度条效果图:

iframe 引用页面

在“快捷界面库”一章中,讲到了创建一个 Panel 后,再创建一个 iframe 组件,可以把外部页面资源引用进来,示例如下:

// 界面组件
var panel = new THING.widget.Panel({
    'width':'400px'
});

// 创建数据对象 
var dataObj = {
    'iframe': 'https://forum.thingjs.com/'
};

var iframe = panel.addIframe(dataObj, 'iframe').caption('iframe');
// 设置 iframe 高度
iframe.setHeight('800px');
// 通过 domElement 获取包裹 iframe 的 div 从而获取真正的 iframe Dom节点
var iframeDom = iframe.domElement.getElementsByTagName('iframe')[0];
// 然后可以用原始的 iframe 相关属性 和 方法 进行开发
// 比如 设置是否有滚动条
//iframeDom.scrolling = "auto"; 

借助此能力,用户可以把自己开发的页面嵌入到 ThingJS 中,以便更灵活的进行页面开发。

此方式引入的 iframe 不能用于 创建 UIAnchor ,仅作为普通页面元素,嵌入集成到 ThingJS 中。

如果要进行 iframe 与 ThingJS 在线开发环境页面之间的通信,那么也需要分“同域”和“跨域”两种情况进行讨论。

同域 iframe 通信

对于从 ThingJS 网站中上传的页面,与 ThingJS 在线开发环境 属于同域。

对于这种情况,在 ThingJS 开发环境中,得到引用的 iframe Dom 节点后,直接通过 contentWindow 获取并调用相应子页面内的全局函数即可,比如:

// 调用同域的iframe页面内的 changeText 方法
iframeDom.contentWindow.changeText('from ThingJS'); 

相应的,在 iframe 子页面中也可以调用 ThingJS 在线开发页面中的函数方法,比如:

// iframe页面中通过 window.parent 获取并调用 ThingJS 在线开发环境中的函数方法
window.parent.changeLevel('car01'); 

查看示例

上述示例的 iframe 页面代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <p id="p1" style='color:white'>Hello World!</p>
    <button onclick='callFunctionInMain()'>Into Level</button>
    <script>
        function callFunctionInMain(){
            var name=document.getElementById("p1").innerHTML;
            // 调用 ThingJS 在线开发中的函数方法
            window.parent.changeLevel(name);
        }
        // ThingJS 中调用此方法
        function changeText(value){
            document.getElementById("p1").innerHTML = value;
        }
    </script>
</body>
</html>

跨域 iframe 通信

对于跨域的 iframe 无法直接相互调用函数。

可以利用 HTML5 提供的 postMessage 接口实现跨域 iframe 页面间的相互函数方法调用。

关于postMessage 的详细 API ,点击 此处 查看

例如,ThingJS 向 iframe 引用的子页面 (https://localhost:3000/pages/index.html) 发送消息

iframeDom.contentWindow.postMessage 

postMessage 方法的第一个参数是具体的信息内容;

第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送;

在 iframe 页面中,通过 message 事件,监听 ThingJS 页面发送的消息。

window.addEventListener('message', function(event) {
console.log(event.data); 

message 事件的 event 事件对象,提供以下三个属性:

  • event.data: 消息内容
  • event.origin: 消息发送方窗口的 origin(域名、协议和端口)
  • event.source:消息发送窗口的对象引用

查看示例

上述示例的 iframe 页面代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p id="p1" style='color:white'>Hello World!</p>
<button onclick='callFunctionInMain()'>Into Level</button>
<script>
    function callFunctionInMain(){

        var name=document.getElementById("p1").innerHTML;

        var message = {
            'funcName': 'changeLevel',// 所要调用父页面里的函数名
            'param': {
                'name': name
            }
        }

        //向父窗体发送消息 
        //第一个参数是具体的信息内容,
        //第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送
        window.parent.postMessage(message, 'https://www.thingjs.com');
    }

    function changeText(value){
        document.getElementById("p1").innerHTML = value;
    }

    // 监听从父窗体传来的消息
    window.addEventListener('message', function (e) {

        var data=e.data;// e.data为传递过来的数据 
        var funcName=data.funcName;// 需要调用的函数名
        var param=data.param;
        window[funcName](param.name);

        console.log(e.origin);  //e.origin 为调用 postMessage 时消息发送方窗口的 origin(域名、协议和端口)
        console.log(e.source);  //e.source 为对发送消息的窗口对象的引用,可以使用此来在具有不同origin的两个窗口之间建立双向通信            
    })
</script>
</body>
</html>

如果用户想基于 ThingJS 做一套独立的应用系统,可使用通栏组件作为系统级别的菜单。

创建Banner

// 创建侧通栏
var banner = new THING.widget.Banner({column:'left'}); 
// 创建侧通栏
var banner = new THING.widget.Banner({column:'left'}); 

参数中 column 代表通栏类型,'top' 为上通栏(默认),' left' 为左侧通栏;

通栏中的按钮绑定数据:

var dataObj = {
    warehouseCode: false,
    temperature: false,
    humidity: false,
    statistics: false,
    status: false,
    insect: false,
    cerealsReserve: false,
    video: true,
    cloud: true
}; 

通栏中添加按钮

var img0 = banner.addImageBoolean(dataObj, 'warehouseCode').caption('仓库编号').url('#momoda_lc-icontubiao10');
var img1 = banner.addImageBoolean(dataObj, 'temperature').caption('温度检测').url('#momoda_lc-icontubiao2');
var img2 = banner.addImageBoolean(dataObj, 'humidity').caption('湿度检测').url('#momoda_lc-icontubiao5');
var img3 = banner.addImageBoolean(dataObj, 'statistics').caption('能耗统计').url('#momoda_lc-icontubiao20');
var img4 = banner.addImageBoolean(dataObj, 'status').caption('保粮状态').url('#momoda_lc-icontubiao');
var img5 = banner.addImageBoolean(dataObj, 'insect').caption('虫害').url('#momoda_lc-icontubiao11');
var img6 = banner.addImageBoolean(dataObj, 'cerealsReserve').caption('粮食储存').url('#momoda_lc-icontubiao21');
var img7 = banner.addImageBoolean(dataObj, 'video').caption('视屏监控').url('#momoda_lc-icontubiao9');
var img8 = banner.addImageBoolean(dataObj, 'cloud').caption('温度云图').url('#momoda_lc-icontubiao16'); 

效果如图所示:

面板定位

绝对位置与相对位置

在“快捷界面库”一章中已经提到,对于面板组件 Panel ,可通过设置 position 属性来确定它在3D容器内的位置。

当 position 里的值为数字时,以像素为单位进行绝对定位。

当 position 里的值为百分比字符串时,则以3D容器大小进行相对定位。

// 创建面板配置项
var panel = new THING.widget.Panel({
    hasTitle: true, // 是否允许有面板title
    titleText: "面板", // 设置title标题名称
    width: '200px'
});
// 创建数据对象用于绑定面板数据
var dataObj = {
    temperature: '24℃',
    humidity: '70%',
    aqi: '良',
};
// 添加数据
panel.addString(dataObj, 'temperature').caption('气温');
panel.addString(dataObj, 'humidity').caption('湿度');
panel.addString(dataObj, 'aqi').caption('空气质量'); 

当 position 里的值为百分比字符串时,则以3D容器大小进行相对定位。

// 距3D容器左侧 100px , 顶部 100px
panel.position=[100,100];
// 距3D容器左侧 200px , 相对顶部 60%
panel.position=[200,'60%']
// 相对3D容器左侧 30% , 距离顶部 200px
panel.position=['30%',200]
// 相对3D容器左侧 50% , 相对顶部 50%
panel.position=['50%','50%'] 

面板锚点

ThingJS 快捷界面库中提供的 Panel 面板可根据面板的锚点进行定位。

对于一个 Panel 面板而言,目前提供四个锚点类型进行定位,分别为:

  • 左上角(TopLeft)
  • 左下角 (BottomLeft)
  • 右上角 (TopRight)
  • 右下角 (BottomRight)

默认基于左上角进行定位。

可通过 positionOrigin 属性设置锚点类型

// 基于左上(默认)
panel.positionOrigin = 'TL';// top-left
// 基于左下
panel.positionOrigin = 'BL';// bottom-left
// 基于右上
panel.positionOrigin = 'TR';// top-right
// 基于右上
panel.positionOrigin = 'BR';// bottom-right 

例如,当设置面板锚点为右下角时,设置位置效果如下:

ThingJS 引用 Echarts

引用步骤

  • 加载 echarts.js

    加载 echarts.js 的目的是为了引用 Echarts 图表库中的图表。

    通过 THING.Utils.dynamicLoad 方法 加载 js ,方法的执行需要两个参数:

    • 第一个参数为 echarts.js 的 URL (链接地址);
    • 第二个参数为加载 echarts.js 之后调用的回调方法
    THING.Utils.dynamicLoad(['https://cdn.bootcss.com/echarts/4.1.0.rc2/echarts.js'],function() {}) 
  • 创建需要的 DOM 节点

    DOM 节点为需要在场景中显示的节点。

    通过 document.createElement 创建新的 DOM 节点。

    由于 DEMO 的需求,此处创建 3 个 DOM 节点,这三个 DOM 节点分别是:

    • 背景颜色 div;
    • 标题 div;
    • 底部图表 div;
    //背景颜色
    var bottomBackground = document.createElement('div');
    //标题
    var bottomFont = document.createElement('div');
    //图表
    var bottomDom = document.createElement('div');
    //背景样式右下角对齐
    var backgroundStyle = 'bottom:0px; position: absolute;right:0px;height:400px;width:600px;background: rgba(41,57,75,0.74);';
    //字体样式
    var fontStyle = 'position: absolute;top:0px;right:0px;color:rgba(113,252,244,1);height:78px;width:600px;line-height: 45px;text-align: center;top: 20px;';
    //图表DIV样式
    var chartsStyle = 'position: absolute;top:80px;right:0px;width:600px;height:300px;';
    
    //设置样式
    bottomBackground.setAttribute('style', backgroundStyle);
    bottomFont.setAttribute('style', fontStyle);
    bottomDom.setAttribute('style', chartsStyle);
    
    //底部标题文字
    bottomFont.innerHTML = '温度降水量平均变化图'; 
  • 初始化 Echarts

    加载 echarts.js 完成以后,已经将 Echarts 引入到场景中了。通过以下两步可以得到图表实例:

    • 调用 window.echarts 获取 Echarts;
    • 通过 init 方法创建图表实例,传入的参数为需要 Echarts 图表的 DOM 节点,返回的是图表实例;

    具体初始化方法可以参照 Echarts 官网五分钟上手 Echarts

    let bottomCharts = window.echarts.init(bottomDom) 
  • 配置图表的属性

    图表的各项属性 options 代表的含义可以点击 Echarts 官网 配置项手册

    • 最好在 Echarts 官网或者 ChartBuilder 官网上,将图表的 options 配置完毕,这样可以快速查看配置的效果
    • 调用 setOptions 方法将配置好的 options 传入图表
    let echartOptions = {
    "tooltip": {
        "trigger": "axis",
        "axisPointer": {
            "type": "cross",
            "crossStyle": {
                "color": "#999"
            }
        }
    },
    "legend": {
        "textStyle": {
            "color": "auto"
        },
        "data": [
            "蒸发量",
            "降水量",
            "平均温度"
        ]
    },
    "xAxis": [
        {
            "axisLabel": {
                "textStyle": {
                    "color": "#fff"
                }
            },
            "type": "category",
            "data": [
                "1月",
                "2月",
                "3月",
                "4月",
                "5月",
                "6月",
                "7月",
                "8月",
                "9月",
                "10月",
                "11月",
                "12月"
            ],
            "axisPointer": {
                "type": "shadow"
            }
        }
    ],
    "yAxis": [
        {
            "type": "value",
            "name": "水量",
            "min": 0,
            "max": 250,
            "interval": 50,
            "splitLine": {
                "lineStyle": {
                    "type": "dotted"
                },
                "show": true
            },
            "nameTextStyle": {
                "color": "#fff"
            },
            "axisLabel": {
                "textStyle": {
                    "color": "#fff"
                },
                "formatter": "{value} ml"
            }
        },
        {
            "splitLine": {
                "lineStyle": {
                    "type": "dotted"
                },
                "show": true
            },
            "type": "value",
            "name": "温度",
            "min": 0,
            "max": 25,
            "interval": 5,
            "nameTextStyle": {
                "color": "#fff"
            },
            "axisLabel": {
                "textStyle": {
                    "color": "#fff"
                },
                "formatter": "{value} °C"
            }
        }
    ],
    "series": [
        {
            "name": "蒸发量",
            "type": "bar",
            "data": [
                2,
                4.9,
                7,
                23.2,
                25.6,
                76.7,
                135.6,
                162.2,
                32.6,
                20,
                6.4,
                3.3
            ]
        },
        {
            "name": "降水量",
            "type": "bar",
            "data": [
                2.6,
                5.9,
                9,
                26.4,
                28.7,
                70.7,
                175.6,
                182.2,
                48.7,
                18.8,
                6,
                2.3
            ]
        },
        {
            "name": "平均温度",
            "type": "line",
            "yAxisIndex": 1,
            "data": [
                2,
                2.2,
                3.3,
                4.5,
                6.3,
                10.2,
                20.3,
                23.4,
                23,
                16.5,
                12,
                6.2
            ]
        }
    ],
    "color": [
        "#2b908f",
        "#90ee7e",
        "#f45b5b",
        "#7798BF",
        "#aaeeee",
        "#ff0066",
        "#eeaaee",
        "#55BF3B",
        "#DF5353",
        "#7798BF",
        "#aaeeee"
    ]
    }
    bottomCharts.setOption(echartOptions); 
  • 将节点放到 app dom 下
    bottomBackground.appendChild(bottomFont);
    bottomBackground.appendChild(bottomDom);
    app.domElement.appendChild(bottomBackground); 
在线咨询
QQ交流群
微信公众号