索引
插件已不再维护,建议使用渲染元素实现
ht.Node
目前支持用图片、矢量来渲染,但是对于某些应用(比如SCADA),经常需要在界面中嵌入一些可交互元素(文本框,按钮等),
此时普通Node
就有些力不从心了。为此HT
扩展实现了ht.HtmlNode
,它可以将HTML
元素渲染到拓扑上,既保持HTML
元素的可交互性,
又能实现普通Node
的缩放旋转等功能。
HtmlNode
提供了如下API
:
html
通过setHtml
和getHtml
操作,表示要渲染的HTML
内容,可以是HTML
文本字符串、DOM
对象或HT
组件(如GraphView
)context
通过setContext
和getContext
操作,用于数据绑定padding
通过setPadding
和getPadding
操作,表示HTML
内容边框和Node
选中边框之间的距离,默认为6
scalable
通过setScalable
和getScalable
操作,表示HTML
内容是否可拉伸。值为boolean
型(默认为true
):true
表示当调整HtmlNode
的宽高时,用transform
样式调整HTML
内容的缩放false
表示当调整HtmlNode
的宽高时,改变HTML
内容的第一个元素的with
和height
使其适应HtmlNode
的尺寸。这意味着:如果此参数设为false
,用户设置的
HTML
内容应该只有一个根元素setDraggerImage
、getDraggerImage
用于设置或获取HtmlNode
右上角的拖拽图标,必须是通过ht.Default.setImage
注册的图片的名字,默认为node_dragger
setDraggerImageWidth
、getDraggerImageWidth
用于设置或获取HtmlNode
右上角拖拽图标的宽度setDraggerImageHeight
、getDraggerImageHeight
用于设置或获取HtmlNode
右上角拖拽图标的高度GraphView
上扩展的API
:
adjustHtmlNodeIndex
,默认为true
,表示自动调整HTML
内容的z-index
,让其在选中时置顶显示;设为false
可提高性能HtmlNode
的html
属性可以设置为HTML
文本,然后可以按照操作一般Node
的方式设置position
、width
、height
等属性。
示例:
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\
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
还提供了简单的双向数据绑定:
将数据从HtmlNode
的属性更新至HTML
元素:
HtmlNode
集成Handlebars框架以实现将HtmlNode
的属性更新至HTML
元素。Handlebars
可以在运行时将模板中的关键字替换为属性值,Handlebars
的详细介绍参考这里:http://handlebarsjs.com/
将HTML
元素中的值更新至HtmlNode
属性:
HtmlNode
在HTML
元素的根结点监听change
事件,并根据事件目标的bind
属性将其value
反填回HtmlNode
的context
属性上,
如下面示例:
示例:
<!--引入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);
setHtml
的参数可以是以下三种类型:
HTML
文本字符串,如setHtml("<div>html text</div>")
DOM
对象,开发人员可以通过DOM API
创建HTML
元素,然后通过setHtml
交给HtmlNode
渲染HT
对象,HtmlNode
也可以渲染GraphView
,TablePane
等HT
组件,需要注意:如果渲染HT
组件,最好将scalable
设为false
示例:
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});
}
});
}
}