HT for Web HtmlNode 手册

索引


插件已不再维护,建议使用渲染元素实现

HtmlNode 说明

ht.Node目前支持用图片、矢量来渲染,但是对于某些应用(比如SCADA),经常需要在界面中嵌入一些可交互元素(文本框,按钮等), 此时普通Node就有些力不从心了。为此HT扩展实现了ht.HtmlNode,它可以将HTML元素渲染到拓扑上,既保持HTML元素的可交互性, 又能实现普通Node的缩放旋转等功能。

HtmlNode提供了如下API

GraphView上扩展的API

简单示例

HtmlNodehtml属性可以设置为HTML文本,然后可以按照操作一般Node的方式设置positionwidthheight等属性。

示例:

var node = new ht.HtmlNode();
node.setPosition(200, 100);
node.setHtml("<div class='wrapper'>\n\
    <img width='20' height='24' src='data:image/png;base64,'>\n\
    &nbsp;&nbsp;&nbsp; Hello, I can <i>render</i> any HTML!</div>");
dataModel.add(node);

上例中HtmlNode将一段HTML文本渲染到拓扑上,HTML元素不但可以响应用户动作,而且可以利用拓扑组件对其进行缩放旋转等操作。需要注意: 为了使HTML元素响应用户动作,HtmlNode屏蔽了在body上的拖拽移动功能,当HtmlNode被选中时,其右上角会出现一个十字方向标记, 拖拽它即可实现拖拽移动HtmlNode

数据绑定

怎样修改已经展现出来的HTML内容?最直接的方式是给HTML元素指定id,通过document.getElementById('id')获取到DOM对象,然后对其进行操作。 除此之外,HtmlNode还提供了简单的双向数据绑定:

示例:

<!--引入handlebars-->
<script src="../../../lib/handlebars.js"></script>

<!--创建HTML模板,{{}}可以将HtmlNode上的属性同步到HTML元素中,而HTML元素的bind属性用于将value同步至HtmlNode-->
<script id="controlpanel-template" type="text/x-handlebars-template">
<div class="panel">
    <div class="title">Dashboard</div>
    <div class="content">
        <p><span class="label">KPI:</span><span class="value">{{value}}</span></p>
        <p>
        <span class="label">Stop:</span>
        <span class="value"><input type="checkbox" {{#if isStopped}}checked{{/if}} bind="isStopped"></span>
        </p>
    </div>
</div>
</script>

......    

var node = new ht.HtmlNode();
node.setPosition(200, 100);
node.setHtml(document.getElementById("controlpanel-template").innerHTML);
node.setContext(context);
dataModel.add(node);

/*
* 每隔一秒刷新HtmlNode的context属性,HtmlNode自动将其同步到HTML元素
*/
setInterval(function() {
    if(!context.isStopped){
        context.value=parseInt(100*Math.random());
        graphView.invalidateData(node);
    }
}, 1000);

支持的HTML类型

setHtml的参数可以是以下三种类型:

示例:

var htmlNode = new ht.HtmlNode();
htmlNode.setPosition(200, 50);
htmlNode.setName("Rendering HTML text");
htmlNode.setHtml("<div class='htmlWrapper'>Node's new name:<input type='text' value='{{value}}' bind='value'>\n\
                  <input type='button' value='Modify' nodeid='{{nodeid}}' onclick='modifyNodeName(event)'/></div>");
htmlNode.setContext({
    value:htmlNode.getName(),
    nodeid:htmlNode.getId()
});
dataModel.add(htmlNode);

var div=document.createElement("div");
div.className="domWrapper";
div.innerHTML="Rendering DOM";
var domNode = new ht.HtmlNode();
domNode.setHtml(div);
domNode.setPosition(200, 150);
dataModel.add(domNode);

var htNode = new ht.HtmlNode();
htNode.setHtml(createTable());
htNode.setPosition(500,100);
htNode.setName("Alarm Statistics(TablePane)");
htNode.setScalable(false);
dataModel.add(htNode);

综合应用

接下来看一个综合应用,有些应用需要在图元的周围显示一些附属信息,比如状态图标、统计表或Chart等。接下来的示例中有两个Node, 一个作为实际展示的Node,另一个作为统计表跟随在第一个Node右侧。为了节省空间,统计表应该可以展开合并,看下面的示例:

首先定义两个模板,作为HtmlNode合并和展开时的HTML内容:

<script id="table-template" type="text/x-handlebars-template">
    <div class="wrapper" nodeid="{{id}}" onclick="tableIconClickHandler(event)">
    <div class="table_title">Real-time monitoring<span class="shrink"></span></div>
    <table class="list_table">
    {{#each rows}}
        <tr>
            <td>{{id}}</td>
            <td>{{temperature}}</td>
            <td>{{pressure}}</td>
            <td>{{time}}</td>
        </tr>
    {{/each}}
    </table>
    </div>
</script>

<script id="table-min-template" type="text/x-handlebars-template">
    <span class="open" nodeid="{{id}}" onclick="tableIconClickHandler(event)"></span>
</script>

接下来声明Host节点和统计表节点:

var host = new ht.Node();
host.setName("This is Host");
dataModel.add(host);


var tableNode = new ht.HtmlNode();
tableNode.setHtml(document.getElementById("table-template").innerHTML);
tableNode.setPosition(230, 100);
tableNode.setContext({
    id: tableNode.getId(),
    rows: [
        {
            id: 1,
            temperature: 23.3,
            pressure: 231.6,
            time: "18:12"
        },
        {
            id: 2,
            temperature: 29.3,
            pressure: 231,
            time: "18:13"
        },
        {
            id: 3,
            temperature: 25,
            pressure: 211.6,
            time: "18:14"
        },
        {
            id: 4,
            temperature: 22.4,
            pressure: 111.6,
            time: "18:15"
        },
        {
            id: 5,
            temperature: 37.4,
            pressure: 171.6,
            time: "18:19"
        }
    ]
});
tableNode.setWidth(300);
tableNode.setHeight(200);
dataModel.add(tableNode);
tableNode.setPosition({x: host.getPosition().x + host.getWidth() / 2 + tableNode.getWidth() / 2 + 10, 
                       y: host.getPosition().y - host.getHeight() / 2 + tableNode.getHeight() / 2});

最后还有一个函数处理用户在统计表右上角的点击事件:

function tableIconClickHandler(e) {
    var maxInnerHTML = document.getElementById("table-template").innerHTML,
            minInnerHTML = document.getElementById("table-min-template").innerHTML;

    if (e.target.className === "shrink") {
        var nodeid = e.currentTarget.getAttribute("nodeid"),
                node = dataModel.getDataById(nodeid),
                host = node.getHost(),
                newWidth = 16 + node._padding,
                newHeight = 16 + node._padding,
                originWidth = node._originWidth,
                originHeight = node._originHeight,
                differenceWidth = originWidth - newWidth,
                differenceHeight = originHeight - newHeight;

        ht.Default.startAnim({
            finishFunc: function() {//动画执行结束时调用,改变统计表的内容和位置
                node.setHtml(minInnerHTML);
                node.setPosition({x: host.getPosition().x + host.getWidth() / 2 + node.getWidth() / 2 + 10, 
                                  y: host.getPosition().y - host.getHeight() / 2 + node.getHeight() / 2});
            },
            action: function(t) {//动画每前进一帧就都会调用此方法,在这里调整统计表的尺寸的位置
                node.setWidth(originWidth - differenceWidth * t);
                node.setHeight(originHeight - differenceHeight * t);
                node.setPosition({x: host.getPosition().x + host.getWidth() / 2 + node.getWidth() / 2 + 10, 
                                  y: host.getPosition().y - host.getHeight() / 2 + node.getHeight() / 2});
            }
        });
    } else if (e.target.className === "expand") {
        var nodeid = e.currentTarget.getAttribute("nodeid"),
                node = dataModel.getDataById(nodeid),
                host = node.getHost(),
                originWidth = node._originWidth,
                originHeight = node._originHeight;

        node.setHtml(maxInnerHTML);

        var newWidth = node.getWidth(),
                newHeight = node.getHeight(),
                differenceWidth = newWidth - originWidth,
                differenceHeight = newHeight - originHeight;

        node.setWidth(originWidth);
        node.setHeight(originHeight);

        ht.Default.startAnim({
            action: function(t) {
                node.setWidth(originWidth + differenceWidth * t);
                node.setHeight(originHeight + differenceHeight * t);
                node.setPosition({x: host.getPosition().x + host.getWidth() / 2 + node.getWidth() / 2 + 10,
                                  y: host.getPosition().y - host.getHeight() / 2 + node.getHeight() / 2});
            }
        });
    }
}

欢迎交流 service@hightopo.com