HT是基于HTML5标准的企业应用图形界面一站式解决方案,
其包含通用组件、拓扑组件和3D渲染引擎等丰富的图形界面开发类库,提供了完全基于HTML5的矢量编辑器、拓扑编辑器及
3D场景编辑器等多套可视化设计工具,和完善的类库开发手册、工具使用手册、及针对HTML5技术如何进行大规模团队开发的客户深度培训手册。
易用、轻量、高性能和跨平台四原则是我们永不停止的追求
<script src="ht.js"></script>
HT的核心开发类库只有一个ht.js的JavaScript(以下简称js)文件,调用ht.Default.getVersion()可获得当前版本号。
我们将确保所有版本向下兼容,这意味着升级产品时,只需要更新ht.js文件,无需修改任何代码即可完成升级。
核心ht.js类库包含了数据模型、树表等通用组件、2D拓扑组件、矢量和3D渲染引擎等核心功能组件,
同时HT提供了众多扩展插件,如对话框、菜单和表单等,可满足其他需求的类库或组件供用户选择使用。
无限制,任意文本编辑器皆可。
任何支持HTML5标准的浏览器。多年前的Chrome、Firefox、Safari和Opera版本都已经支持HTML5,
iOS和Android等主流移动设备的浏览器也都已支持HTML5,IE需要IE9及以上的版本,
如果采用HT for Web 3D则需要IE11及以上版本支持,建议尽量采用最新版本浏览器。
如果项目环境必须使用IE6,IE7和IE8等老版IE浏览器,或因采用HT for Web 3D,
而现场环境无法升级到IE11,则可以考虑安装Google Chrome Frame插件,
在页面嵌入以下Tag代码片段,该页面就会采用Chrome来渲染。
<meta http-equiv="X-UA-Compatible" content="chrome=1">
使用Google Chrome Frame还需要注意一下几点:
Google Chrome Frame不支持直接从本地local file方式打开页面,必须把页面部署到Web服务器发布方式打开Google Chrome Frame不支持64位的浏览器:Currently, 64-bit versions of IE are not supported. It's worth pointing out that 32-bit IE is the default on 64-bit Windows 7.Google Chrome Frame不支持iframe方式: At this point ChromeFrame only supports the meta tag detection on top level URLs.可采用嵌入OBJECT元素的解决方案,绕开Google Chrome Frame不支持iframe的问题
<OBJECT ID="ChromeFrame" WIDTH=500 HEIGHT=500 CODEBASE="http://www.google.com"
CLASSID="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
<PARAM NAME="src" VALUE="http://www.google.com">
<embed ID="ChromeFramePlugin" WIDTH=500 HEIGHT=500 NAME="ChromeFrame"
SRC="http://www.google.com" TYPE="application/chromeframe">
</embed>
</OBJECT>
Google Chrome Frame已于2014年1月停止支持和更新,目前Google Chrome Frame以发展到31的版本,
这个版本已满足HT的2D和3D所需的Canvas功能,因此HT的客户可采用Google Chrome Frame解决兼容IE老版本问题。
其他问题可参考Google Chrome Frame的Developer Guide
和Troubleshooting
部分老版本的Android终端系统,对canvas的支持存在bug会出现没擦除干净有残影,以下是段workaround代码:
ht.Default.viewListener = function(view, kind){
var canvas = view._canvas;
if(canvas && kind === 'beginValidate'){
canvas.width = 0;
canvas.height = 0;
canvas.style.width = 0;
canvas.style.height = 0;
}
};
HT基于JavaScript语言,由于JavaScript动态语言的灵活性,开发工具在拼写和错误提示方面功能较弱,
因此HT为一些常用的函数提供了简写的命名方式,熟记以下常用函数简写可提高编码效率。
HT简写的几个字母有如下含义:
m: monitor、modelum: unmonitorf: fireHT简写函数列表如下:
GraphView#addInteractorListener = GraphView#miGraphView#removeInteractorListener = GraphView#umiGraphView#fireInteractorEvent = GraphView#fiGraph3dView#addInteractorListener = Graph3dView#miGraph3dView#removeInteractorListener = Graph3dView#umiGraph3dView#fireInteractorEvent = Graph3dView#fiSelectionModel#addSelectionChangeListener = SelectionModel#msSelectionModel#removeSelectionChangeListener = SelectionModel#umsSelectionModel#getFirstData = SelectionModel#fdSelectionModel#getLastData = SelectionModel#ldSelectionModel#contains = SelectionModel#coSelectionModel#setSelection = SelectionModel#ssSelectionModel#appendSelection = SelectionModel#asSelectionModel#selectAll = SelectionModel#saSelectionModel#removeSelection = SelectionModel#rs SelectionModel#clearSelection = SelectionModel#cs DataModel#getSelectionModel = DataModel#smDataModel#addDataModelChangeListener = DataModel#mmDataModel#removeDataModelChangeListener = DataModel#umm DataModel#addDataPropertyChangeListener = DataModel#mdDataModel#removeDataPropertyChangeListener = DataModel#umdDataModel#addHierarchyChangeListener = DataModel#mh DataModel#removeHierarchyChangeListener = DataModel#umh firePropertyChange = fp addPropertyChangeListener = mpremovePropertyChangeListener = umpgetPosition = psetPosition = pgetTranslateX = txsetTranslateX = txgetTranslateY = tysetTranslateY = tygetStyle = ssetStyle = sgetAttr = asetAttr = ainvalidate = ivinvalidateModel = ivmgetSelectionModel = smgetLogicalPoint = lpToolbar#setValue = vToolbar#getValue = vFormPane#setValue = vFormPane#getValue = v以下为常见简写示例:
graphView.getDataModel().getSelectionModel().setSelection(data) = graphView.dm().sm().ss(data)
graphView.getDataModel().getSelectionModel().addSelectionChangeListener(func) = graphView.dm().sm().ms(func)
dataModel.getSelectionModel().getLastData().setAttr('age', 35) = dataModel.sm().ld().a('age', 35)
详见数据模型手册
HT沿袭常规面对对象语言设计风格,对类和包采用骆驼式命名法,类名以大写开头的,包名以小写开头。
整个框架只占有全局变量'ht',对于常规的前端页面开发意味着ht.js运行后会占用window.ht,
如果ht.js运行在WebWork环境将占用self.ht变量,
如果ht.js运行于Node.js环境将占用module.exports.ht变量。
HT整体框架层次很“扁平化”,ht.*包含模型和系统相关类,ht.widget.*包含通用组件相关类,ht.graph.*包含2D图形组件相关类,
ht.graph3d.*包含3D图形组件相关类,考虑到js语言以及HTML5应用特殊性,尽量减少类包层次,简短包函数命名是HT框架API设计的特点,
这样能减少用户学习指数,降低编码工作量,有助于整体开发包精小。
JavaScript语言未提供严谨的面对对象类的语言级支持,为此HT设计了一套类封装体系供用户选择采用,
详见序列化手册
HT提供了ht.List的数组类,该类型对Array数组进行了封装,提供了更易记易用的函数接口:
new ht.List(array/list/object)构造函数可传入Array和ht.List数组,也可以加入单个object元素size()返回数组元素总数isEmpty()判断数组是否为空add(item, index)添加元素,index指定插入位置,为空代表插入到最后addAll(array)添加array中的所有元素,支持Array和ht.List类型get(index)返回索引index位置的元素slice(start, end)返回从start为起始到end结束的新ht.List对象数组,end为空代表到最后一个元素 remove(item)删除指定元素,返回该元素所在的索引 removeAt(index)删除索引位置的元素,返回删除元素对象set(index, item)在指定索引位置设置元素clear()清除数组所有元素contains(item)判断元素是否在数组中indexOf(item)返回元素在数组中得索引,若不存在返回-1each(func, scope)顺序遍历所有元素,可指定函数调用scope域 reverseEach(func, scope)反序遍历所有元素,可指定函数调用scope域 toArray(matchFunc, scope)根据matchFunc返回所有匹配元素的新Array数组,可指定函数调用scope域 toList(matchFunc, scope)根据matchFunc返回所有匹配元素的新ht.List数组,可指定函数调用scope域 sort(sortFunc)根据sortFunc比较逻辑重新排列数组元素位置reverse()对数组元素的顺序取反HT提供了事件通知管理器ht.Notifier类,可对其添加监听函数、删除监听函数,以及派发事件到所有监听函数:
add(listener, scope)添加监听函数,scope域参数可选remove(listener, scope)删除监听函数,scope域参数必须和add时的参数一致contains(listener, scope)判断是否包含指定监听函数,scope域参数必须和add时的参数一致fire(event)派发事件到所有监听函数ht.Default对象定义了所有组件的默认参数值,以及一系列的工具类函数,详见风格手册
ht.Style对象定义了ht.Data类型的默认style属性值,详见风格手册
ht.Color对象定义了HT系统的所有默认颜色,详见风格手册
ht.JSONSerializer序列化类,提供了对DataModel数据模型的JSON格式序列化和反序列化功能,
详见序列化手册
ht.Data(简称为Data,下文中将以省去ht.包头的方式进行介绍)是HT最基础的数据类型,用户可将业务信息存储在Data对象属性上,
目前HT提供了Node、Edge、Tab、Column等子类,这些类型在不同的视图组件中具有不同的显示效果:TreeView树形组件上Data代表一个树节点;
TableView表格组件上Data代表一行记录,列代表Data对象的属性;GraphView图形组件上Node代表一个图形元素;
定义页签时TabView采用Tab类型存储页签信息;定义表格列时TableView采用Column存储列信息。
详见数据模型手册
ht.Node类型是GraphView和Graph3dview呈现节点图元的基础类,继承于Data类。
以下为Node的GraphView拓扑图相关函数属性,与Graph3dView相关函数属性详见3D手册。
Node除了显示图片外,还能显示多种预定义图形,详见shape章节。
getPosition()和setPosition({x:100, y:200})获取和设置图元中心点坐标getImage()和setImage(image)获取和设置图片信息,在GraphView拓扑图中图片一般以position为中心绘制getWidth()和setWidth(width)获取和设置图元宽度,若未设置则为image对应的图片宽度getHeight()和setHeight(height)获取和设置图元高度,若未设置则为image对应的图片高度getSize()和setSize(10, 20 | {width:10, height:20})获取和设置图元宽高尺寸信息getRect()和setRect(x,y,width,height | {x:10,y:20,width:30,height:40})获取和设置图元矩形区域getRotation()和setRotation(Math.PI/2)获取和设置图元的旋转角度,围绕中心点顺时针旋转getAnchor()和setAnchor(x, y | {x:0.5,y:0.5})获取和设置图元的锚点,锚点影响着节点坐标位置,锚点同时也是旋转和缩放的中心点getAttaches()获取吸附到自身的所有节点的ht.List类型数组getEdges()获取所有和节点模型上关联的连线ht.List类型数组getSourceEdges()获取所有模型上起始于该节点的连线ht.List类型数组getTargetEdges()获取所有模型上结束于该节点的连线ht.List类型数组getAgentEdges()获取当前节点图形上代理关联的连线ht.List类型数组getSourceAgentEdges()获取所有图形上代理起始于该节点的连线ht.List类型数组getTargetAgentEdges()获取所有图形上代理结束于该节点的连线ht.List类型数组hasAgentEdges()判断当前节点是否图形上有代理连线,有返回true,无返回falsegetHost()和setHost(host)获取和设置吸附宿主对象,当节点吸附上宿主图元时,宿主移动或旋转时会带动所有吸附者onHostChanged(oldHost, newHost)当吸附宿主对象发生变化时回调该函数,可重载做后续处理handleHostPropertyChange(event)当吸附宿主对象属性发生变化时回调该函数,可重载做后续处理isHostOn(node)判断该图元是否吸附到指定图元对象上isLoopedHostOn(node)判断是否与指定图元形成环状吸附,如A吸附B,B吸附C,C吸附回A,则A,B和C图元相互环状吸附
以上示例中创建了air11图元吸附上了air13图元,air11改变了尺寸,air13设置了旋转:
ht.Default.setImage('mac', 'res/mac-air.png');
air11 = new ht.Node();
air11.setName('11-inch MacBook Air');
air11.setImage('mac');
air11.setSize(80, 43);
air11.setPosition(100, 70);
dataModel.add(air11);
air13 = new ht.Node();
air13.setName('13-inch MacBook Air');
air13.setImage('res/mac-air.png');
air13.setPosition(260, 70);
air13.setRotation(Math.PI/2);
dataModel.add(air13);
air11.setHost(air13);
代码将GraphView设置为可编辑,同时只允许air11能编辑大小,air13能旋转,该功能通过设置过滤器实现
graphView.setEditable(true);
graphView.setRectEditableFunc(function(data){
return data === air11;
});
graphView.setRotationEditableFunc(function(data){
return data === air13;
});
锚点是Node上一个重要的概念,节点绘制的是一个矩形区域,而锚点是决定了矩形区域中哪个位置是节点的坐标点位置,锚点值是一个百分比数值,{x:0,y:0}是在区域左上角,{x:1,y:1}是在区域右下角,默认是以{x:0.5,y:0.5}为锚点,也就是图元中心点,如果大于1或者小于0则锚点会在节点矩形区域之外。可以通过node.getAnchor和node.setAnchor获取和设置,也可以通过node.getAnchorX、node.setAnchorX、node.getAnchorY、node.setAnchorY方法单独设置获取。
以上案例中可以看出,每行节点的纵坐标是一致的,但是由于锚点的不同导致旋转缩放上的差异。
ht.Edge类型用于连接起始和目标两个Node节点,两个节点间可以有多条Edge存在,也允许起始和目标为同一节点。
连线的agent指的是目前图形上真正代理连接该连线的节点,当节点位于关闭的Group之内时,Group将代理内部的节点进行连接。
Edge的更多样式属性参见连线风格。
getSource()和setScource(node)获取和设置起始节点getTarget()和setTarget(node)获取和设置目标节点isLooped()判断连线的起始和目标是否为同一节点getSourceAgent()获取图形上连接的起始节点getTargetAgent()获取图形上连接的目标节点getEdgeGroup()获取ht.EdgeGroup类型对象,起始和目标节点间有多条连线时才有值toggle()实现对当前起始和目标节点之间多条连线的展开合并的切换,影响edge.expanded的style属性isEdgeGroupHidden()判断当前连线是否在连线组中被隐藏getEdgeGroupSize()返回当前连线所在连线组的连线数 getEdgeGroupIndex()返回当前连线所在连线组的索引isEdgeGroupAgent()判断当前连线是否为所在连线组的代理 可通过new ht.Edge(source, target)直接在构造函数中传入source和target节点对象,
也可构建Edge对象之后再分别设置,该示例重载了graphView.getLabel函数,自定义了图元文字标签,
实现同组多连线合并时,代理连线文字能体现连线组信息:
var edge = new ht.Edge();
edge.setSource(source);
edge.setTarget(target);
dataModel.add(edge);
edge = new ht.Edge(source, target);
edge.toggle();
dataModel.add(edge);
edge = new ht.Edge(source, source);
dataModel.add(edge);
graphView.getLabel = function(data){
if(data instanceof ht.Edge){
if(data.isEdgeGroupAgent()){
return data.getEdgeGroupSize() + ' become 1';
}
}
return data.getName();
};
ht.Group类型用于作为父容器包含孩子图元,在GraphView拓扑图上可通过双击进行展开合并,合并时会自定隐藏子孙图元节点,
如果有子节点有连线连接到外部时,合并的Group将代理进行连接。Group的移动会带动孩子节点跟随,
孩子的位置和大小变化也会影响Group的展开图形和position位置。参见Group样式属性。
isExpanded()和setExpanded(true/false)获取和设置Group对象的展开和关闭状态toggle()函数可对展开合并状态进行切换style的ingroup属性决定是否能被Group包含,默认为true示例中创建了Group对象,通过group.setExpanded(true)设置为展开状态,并未对Group对象指定位置,
而是由后续添加的孩子节点位置来自动影响Group位置。添加孩子通过addChild或setParent都是可选的:
var group = new ht.Group();
group.setName('Double click on me');
group.setExpanded(true);
dataModel.add(group);
var node1 = new ht.Node();
node1.setName('Node1');
node1.setPosition(80, 80);
group.addChild(node1);
dataModel.add(node1);
var node2 = new ht.Node();
node2.setName('Node2');
node2.setPosition(180, 80);
node2.setParent(group);
dataModel.add(node2);
构建了一个style的ingroup属性为false的图元,该图元将游离于Group之外,Group移动会带动其跟随,
但该节点位置或大小变化不会影响Group对象:
var node4 = new ht.Node();
node4.setName('The Special One');
node4.setStyle('ingroup', false);
node4.setPosition(290, 100);
group.addChild(node4);
dataModel.add(node4);
以下代码构建了一个label文字不影响Group的孩子图元,为实现该功能重载了graphView.getBoundsForGroup函数,
对于node3的图元仅返回其node3.getRect()大小,其他图元继续保持原始函数的逻辑,
该例先缓存了默认函数var oldFunc = graphView.getBoundsForGroup,
然后在重载函数中通过oldFunc.call(this, child)的方式进行调用,这使用HT常见的重载技巧,
这样的方式无需定义新类就可以进行函数重载,且可以根据需要调用原始函数逻辑:
var node3 = new ht.Node();
node3.setPosition(130, 140);
node3.s({
'label.font': 'bold 21px arial',
'label.color': 'white',
'label.offset.y': 8,
'label.background': '#E74C3C'
});
node3.setName('HT for Web');
node3.setParent(group);
dataModel.add(node3);
var oldFunc = graphView.getBoundsForGroup;
graphView.getBoundsForGroup = function(child){
if(child === node3){
return node3.getRect();
}
return oldFunc.call(this, child);
};
详见形状手册
详见形状手册
ht.Grid类型一般用于作为容器,对附属节点(attachNode.setHost(grid))进行网格布局,附属节点可为Grid类型,进而实现嵌套式布局。
setStyle('grid.row.count', 1)设置行数setStyle('grid.column.count', 1)设置列数setStyle('grid.row.percents', [0.1, 0.2, 0.3, 0.4])设置每行高度百分比,默认为null代表均分setStyle('grid.column.percents', [0.1, 0.2, 0.3, 0.4])设置每列宽度百分比,默认为null代表均分setStyle('grid.border', 1)设置容器四边缘的厚度,如果四边厚度不均衡可以通过以下四个参数具体控制setStyle('grid.border.left', 0)设置容器左边缘的厚度setStyle('grid.border.right', 0)设置容器右边缘的厚度setStyle('grid.border.top', 0)设置容器上边缘的厚度setStyle('grid.border.bottom', 0)设置容器下边缘的厚度setStyle('grid.gap', 1)设置容器内单元格的间隙setStyle('grid.background', '#E5BB77')设置容器背景色setStyle('grid.depth', 1)设置容器四边缘的深度,0代表平面效果,正值代表凸起效果,负值代表凹陷效果setStyle('grid.cell.depth', -1)设置单元格四边缘的深度,0代表平面效果,正值代表凸起效果,负值代表凹陷效果setStyle('grid.cell.border.color', '#868686')设置单元格边框颜色,该属性在grid.cell.depth值为0时起效setStyle('grid.block', 'undefined')设置是否显示块边框,默认值为undefined代表不绘制,v代表绘制列块,h代表绘制行块setStyle('grid.block.padding', 3)设置块边框距离单元格内容间距setStyle('grid.block.width', 1)设置块边框绘制宽度 setStyle('grid.block.color', '#868686')设置块边框绘制颜色 Node类型,可设置以下attach相关参数:setStyle('attach.row.index', 0)设置节点所在单元格行位置setStyle('attach.column.index', 0)设置节点所在单元格列位置setStyle('attach.row.span', 1)设置节点行跨越数setStyle('attach.column.span', 1)设置节点列跨越数setStyle('attach.padding', 0)设置四边与单元格距离,正值代表超出单元格,负值代表小于单元格setStyle('attach.padding.left', 0)设置左边与单元格距离,正值代表超出单元格,负值代表小于单元格setStyle('attach.padding.right', 0)设置右边与单元格距离,正值代表超出单元格,负值代表小于单元格setStyle('attach.padding.top', 0)设置上边与单元格距离,正值代表超出单元格,负值代表小于单元格setStyle('attach.padding.bottom', 0)设置下边与单元格距离,正值代表超出单元格,负值代表小于单元格ht.SubGraph类型与Group类型有相似之处,他们都会影响孩子图元的呈现方式,不同于Group类型与孩子节点在同层界面展示,
SubGraph类型将其孩子包括子孙节点呈现于下一层界面,在GraphView组件上表现为双击SubGraph图元将进入新的界面内容,
在新的界面内容下双击背景可以返回SubGraph图元所在的界面,SubGraph可无限制层层嵌套。
GraphView上与SubGraph相关函数如下:
getCurrentSubGraph()和setCurrentSubGraph(subGraph)获取和设置当前子网,默认值为空代表处于最顶层upSubGraph()进入当前所在子网的上一层子网详见页签组件手册
详见表格组件手册
详见属性组件手册
详见数据模型手册
详见数据模型手册
HT框架的组件指的是可视化可交互的视图控件,HT框架基于HTML5技术,因此HT组件的可视化部分本质就是HTML的元素,
大部分HT组件与DataModel数据模型绑定,用户通过操作纯js的语言即可驱动可视化组件,这样屏蔽了HTML底层图形技术复杂性。
HT对HTML5技术封装的目的在于提高开发效率和可维护性,但并不意味着不允许用户直接操作HTML原生元素,
有HTML5开发经验的程序员,在了解HT系统机制的前提下,大可运用各种HTML5的技术对HT组件做自定义扩展。
所有HT组件最根层都为一个div组件,可通过组件的getView()函数获得,
默认和自定义交互事件监听一般添加在该div上(getView().addEventListener(type ,func, false)),
渲染层一般由canvas提供。
用户可直接对根div和cavnas层设置css背景等样式,
也可以添加新的HTML组件到根div层上,作为canvas的兄弟组件一起呈现。
HT组件一般都以设置position为absolute的绝对定位方式,box-sizing属性都以设置为border-box。
HT的组件大部分都提供了isDisabled()和setDisabled(true/false, iconURL)函数可使整个组件处于不可用状态,
一般用于远程加载数据过程暂时让组件处于不可操作状态,iconURL在这种情况下一般可设置代表正在加载状态的gif图片路径。
处于disabled状态的组件会生成一个div遮挡住整个组件,通过ht.Default.disabledBackground可修改遮挡组件背景色。
HT的组件的渲染大部分由中间的canvas组件实现,具体的组件会提供相应的扩展函数供自定义,
例如ListView提供drawRow供函数自定义行绘制,GraphView通过矢量机制自定义图元image在拓扑上的呈现等,
基于canvas渲染的组件如ListView、PropertyView、TreeView、
TableView、TreeTableView、GraphView等组件,
都提供了绘制canvas最底层和最顶层的画笔接口:
addTopPainter(function(g){})和removeTopPainter(func)添加和删除绘制在最顶层的画笔addBottomPainter(function(g){})和removeBottomPainter(func)添加和删除绘制在最底层的画笔HT的所有组件都没有采用HTML自带的滚动条功能,完全由HT组件内部自绘制实现,滚动条可自动隐藏,
在组件平移或鼠标滑过边界时动态出现,默认透明的滚动条cover在内容之上,不会影响组件界面布局:
getScrollBarColor()和setScrollBarColor(color)获取和设置滚动条颜色getScrollBarSize()和setScrollBarSize(6)获取和设置滚动条宽度isAutoHideScrollBar()和setAutoHideScrollBar(true/false)获取和设置是否自动隐藏滚动条,默认为trueHT组件的坐标原点默认在左上角,大部分组件都有平移功能,平移的可视化效果就是滚动,
分为水平横坐标平移translateX属性,和垂直纵坐标平移translateY属性,两者默认值都为0。
有些组件只能水平平移,例如TabView和TableHeader;有些组件只能垂直平移,例如ListView和TreeView;
而GraphView和TableView等则水平和垂直皆可;可重载adjustTranslateX和adjustTranslateY函数改变平移逻辑:
setTranslate(x, y, anim)设置新的水平平移和垂直平移值,anim代表是否动画参见动画章节getTranslateX()和setTranslateX(10)获取和设置当前水平平移值,简写为tx()和tx(10)getTranslateY()和setTranslateY(10)获取和设置当前垂直平移值,简写为ty()和ty(10)translate(tx, ty)在当前值基础上增加水平和垂直平移值adjustTranslateX(value)该函数传入即将设置的水平平移值,返回最终设置值,可重载限制水平平移范围adjustTranslateY(value)该函数传入即将设置的垂直平移值,返回最终设置值,可重载限制垂直平移范围HT的组件一般都会嵌入BorderPane、SplitView和TabView等容器中使用,而最外层的HT组件则需要用户手工将getView()
返回的底层div元素添加到页面的DOM元素中,这里需要注意的是,当父容器大小变化时,如果父容器是BorderPane和SplitView
等这些HT预定义的容器组件,则HT的容器会自动递归调用孩子组件invalidate函数通知更新。但如果父容器是原生的html元素,
则HT组件无法获知需要更新,因此最外层的HT组件一般需要监听window的窗口大小变化事件,调用最外层组件invalidate函数进行更新。
为了最外层组件加载填充满窗口的方便性,HT的所有组件都有addToDOM函数,其实现逻辑如下,其中iv是invalidate的简写:
addToDOM = function(){
var self = this,
view = self.getView(),
style = view.style;
document.body.appendChild(view);
style.left = '0';
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize', function () { self.iv(); }, false);
}
改变HT系统默认属性,需要通过全局的htconfig变量名指定,HT系统只在初始化时读取htconfig的配置信息,
因此该属性必须在引入ht.js包之前初始化好,运行状态时修改htconfig变量不会再起作用,示例代码如下:
<script>
htconfig = {
Color: {
label: '#000',
labelSelect: '#FFF'
},
Default: {
toolTipDelay: 100,
toolTipContinual: true
},
Style: {
'select.color': '#E74C3C',
'select.width': 3
}
};
</script>
<script src="ht.js"></script>
可配置的参数分为三大类,具体属性说明参见风格手册
ht.Color对象属性上ht.Default对象属性上ht.Style对象属性上图片是图形组件的重要资源,树上的图标,拓扑上图元等都可用图片来绘制,HT支持PNG和JPG等常规图片格式,
如Node章节的示例所示,图片有两种使用方式:
air13.setImage(res/mac-air.png)ht.Default.setImage('mac', 'res/mac-air.png')进行注册,再将注册名称设置到模型上air11.setImage('mac')直接设置路径方式开发方便,无需提前注册图片,但数据模型序列化
时图片路径占用内存较多,将来图片路径变化后不利于管理维护,两种方式都是正确的使用方式,可根据项目情况选中不同方式或混合使用。
如果采用url的路径方式HT内部会自动加载图片,并在onload之后自动更新相应的视图组件。
HT的框架下图片被赋予了更广泛的含义,HT提供了自定义JSON格式的矢量描述形式,以HT标准定义的JSON矢量格式,
也可以作为图片进行注册和使用,HT的矢量方式比传统图片格式更节省空间,缩放不失真,最强大之处在于矢量的所有图形参数,
皆可与Data模型上的数据动态绑定,具体说明参见矢量手册。
ht.Default.setImage函数有以下几种调用方式:
setImage('hightopo', 'www.hightopo.com/logo.png')通过url方式注册setImage('hightopo', 'data:image/png;base64,iVBORw...Jggg==')通过base64方式注册setImage('www.hightopo.com/logo.png')只用url一个参数,该方式图片的name和url路径一样setImage('hightopo', 200, 80, 'www.hightopo.com/logo.png')通过url方式注册,指定图片宽高setImage('hightopo', 200, 80, 'data:image/png;base64,iVBORw...Jggg==')通过base64方式注册,指定图片宽高setImage('hightopo', img|canvas)直接注册img和canvas可绘制html元素方式采用直接注册img的html元素的方式,用户需确保img资源已加载,这种方式下HT不会监听其onload事件,
因此不会自动通知视图组件更新。
ht.Default.getImage(name, color)可获取对应的图片元素,在图片加载过程该函数返回空,只有onload之后才能得到相应图元元素,
color为颜色参数,一般为空,如果有颜色值则HT内部会构建出对图片进行color染色后的新图片
HT还提供了以下几种针对图片的绘制函数,以下参数中image为可绘制的img或canvas元素,也可为矢量的json格式,
由于矢量可动态绑定数据模型具有染色功能,因此绘制矢量时可传入data数据、view组件和color染色参数:
ht.Default.drawImage(g, image, x, y, width, height, data, view, color)将图片image以填充满的方式绘制在指定矩形区域内ht.Default.drawCenterImage(g, image, x, y, data, view, color)以x和y为中心位置绘制image图片ht.Default.drawStretchImage(g, image, stretch, x, y, w, h, data, view, color)在矩形位置内绘制图片,stretch类型如下:fill图片填充满整个矩形区域,如果图片宽高比例和矩形不一致会导致图片拉伸失真uniform图片始终保持原始宽高比例不变化,并尽量填充满矩形区域centerUniform当矩形区域大于图片尺寸时图片以原始大小绘制在中心位置,空间不够时采用uniform的绘制方式在HT的数据模型驱动图形组件的设计架构下,动画可理解为将某些属性由起始值逐渐变到目标值的过程,
HT提供了ht.Default.startAnim的动画函数,其示例代码如下。
ht.Default.startAnim({
frames: 12, // 动画帧数
interval: 10, // 动画帧间隔毫秒数
easing: function(t){ return t * t; }, // 动画缓动函数,默认采用`ht.Default.animEasing`
finishFunc: function(){ console.log('Done!') }, // 动画结束后调用的函数。
action: function(v, t){ // action函数必须提供,实现动画过程中的属性变化。
node.setPosition( // 此例子展示将节点`node`从位置`p1`动画到位置`p2`。
p1.x + (p2.x - p1.x) * v,
p1.y + (p2.y - p1.y) * v
);
}
});
ht.Default.startAnim支持Frame-Based和Time-Based两种方式的动画,以上代码为Frame-Based方式,
这种方式用户通过指定frames动画帧数,以及interval动画帧间隔参数控制动画效果。
以下代码为Time-Based方式,该方式用户只需要指定duration的动画周期的毫秒数即可,HT将在指定的时间周期内完成动画,
不同于Frame-Based方式有明确固定的帧数,即action函数被调用多少次,Time-Based方式帧数或action函数被调用次数取决于系统环境,
一般来说系统配置更好的机器,更高效的浏览器则调用帧数越多,动画过程更平滑。由于js语言无法精确控制interval时间间隔,
采用Frame-Based不能精确控制动画时间周期,即使相同的frames和interval参数在不同的环境,可能会出现动画周期差异较大的问题,
因此HT默认采用Time-Based的方式,如果不设置duration和frames参数,则duration参数将被系统自动设置为ht.Default.animDuration值。
ht.Default.startAnim({
duration: 500, // 动画周期毫秒数,默认采用`ht.Default.animDuration`
action: function(v, t){
...
}
});
startAnim函数会返回一个anim对象,可调用anim.stop(true)终止动画,其中的参数shouldBeFinished代表是否完全未达到的目标改变,
如果为true则会调用anim.action(anim.easing(1))。同时anim还具有anim.pause()和anim.resume()可中断和继续动画功能,
以及anim.isRunning()函数判断动画是否正在进行。
注:当anim对象调用stop方法时,anim中定义的finishFunc方法会被执行。
action函数的第一个参数v代表通过easing(t)函数运算后的值,t代表当前动画进行的进度[0~1],一般属性变化根据v参数进行。
以上示例展示了点击背景图元动画移动到点击位置,点击图元自身进行旋转和缩放的动画效果,
ht.Default.startAnim中得easing参数是用于让用户定义函数,通过数学公式控制动画,
如匀速变化、先慢后快等效果,可参考http://easings.net/,
示例代码easing.js定义了一系列的动画函数可供选用:
var Easing = {
swing: function (t) {
return ( -Math.cos(t * PI) / 2 ) + 0.5;
},
/**
* Begins slowly and accelerates towards end. (quadratic)
*/
easeIn: function (t) {
return t * t;
},
/**
* Begins quickly and decelerates towards end. (quadratic)
*/
easeOut: function (t) {
return ( 2 - t) * t;
},
// ...
}
示例中构建通过graphView.setInteractors(null)去掉所有默认交互功能,通过view.addEventListener添加监听器,
同时构建了html的select元素用于选择不同easing效果,将其添加到graphView.getView()的div组件上,
因此在自定义交互事件中需要对点击在select元素做过滤处理,其中graphView.getLogicalPoint(e)根据交互事件返回逻辑坐标位置。
该例子点击图元时触发图元围绕自身中心旋转一周,同时图元由大变小再恢复原尺寸,该逻辑通过设置frames为30帧和interval为16毫秒间隔的
Frame-Based方式完成动画;点击背景区域触发图元移动到指定的点击位置,该移动过程通过设置duration为500周期的Time-Based方式完成动画。
var select = document.createElement('select');
select.style.position = 'absolute';
select.style.top = '10px';
select.style.right = '10px';
view.appendChild(select);
for(var name in Easing){
var option = document.createElement('option');
option.innerHTML = name;
if(name === 'easeOut'){
option.setAttribute('selected', 'true');
}
select.appendChild(option);
}
graphView.setInteractors(null);
var type = "ontouchend" in document ? 'touchstart' : 'mousedown';
isAnimating = false;
view.addEventListener(type, function(e){
e.preventDefault();
if(isAnimating || e.target === select || !ht.Default.isLeftButton(e)){
return;
}
isAnimating = true;
var data = graphView.getDataAt(e);
var easing = Easing[select.value];
var finishFunc = function(){
isAnimating = false;
};
if(data === toy){
var size = toy.getSize();
ht.Default.startAnim({
frames: 30,
interval: 16,
easing: easing,
finishFunc: finishFunc,
action: function(v){
toy.setRotation(Math.PI * v);
var r = Math.abs(v - 0.5) * 2;
toy.setSize(size.width * r, size.height * r);
}
});
}else{
var p2 = graphView.getLogicalPoint(e);
var p1 = toy.getPosition();
anim = ht.Default.startAnim({
duration: 500,
easing: easing,
finishFunc: finishFunc,
action: function(v){
toy.setPosition(
p1.x + (p2.x - p1.x) * v,
p1.y + (p2.y - p1.y) * v
);
}
});
}
}, false);
示例直接修改了底层div组件的style背景色,同时增加了顶部和底部的画笔,绘制了click anywhere you want ..的文字信息,
通过移动图元可发现topPainter绘制的内容呈现在图元之上,bottomPainter绘制的内容呈现在图元之下。
view.style.background = '#FCFCFC';
graphView.addTopPainter(function(g){
ht.Default.drawText(g, 'click anywhere you want ..', '24px Arial', 'lightgray', 50, 100, 0, 0, 'left');
});
graphView.addBottomPainter(function(g){
ht.Default.drawText(g, 'click anywhere you want ..', '24px Arial', 'lightblue', 200, 180, 0, 0, 'left');
});
HT很多组件的函数也带有动画功能,如setTranslate(x, y, anim)、
zoomIn(anim)、rotate(leftRight, upDown, anim)等函数都带有anim的参数选项,该参数可传入两种类型:
true和false的boolean类型,true代表启动动画,并采用默认动画效果,false代表不启动动画json的对象结构,采用该类型参数代表启动动画,而json结构的属性与ht.Default.startAnim类型,
可传人duration、frames、interval、easing和finishFunc等动画控制参数。除了通过
ht.Default.startAnim调用启动动画外,DataModel上也具有启动调度任务的函数, 可扩展实现为流动、闪烁、大小变化等动画效果,详见调度手册, 如需更强大的描述性动画控制可参考动画插件。
详见列表组件手册
详见树组件手册
详见表格组件手册
详见树表组件手册
详见工具条手册
详见分割组件手册
详见边框面板手册
详见折叠组件手册
详见页签组件手册
拓扑图形组件ht.graph.GraphView(以下简称GraphView)是HT框架中2D功能最丰富的组件,其相关类库都在ht.graph包下。
GraphView具有基本图形的呈现和编辑功能,拓扑节点连线及自动布局功能,电力和电信等行业预定义对象,具有动画渲染等特效,
因此其应用面很广泛,可作为监控领域的绘图工具和人机界面,可作为一般性的图形化编辑工具,可扩展成工作流和组织图等企业应用。
改变zoom属性值(默认为1)可实现GraphView的缩放功能,以放大或缩小的方式查看拓扑图形组件的全貌或细节。
默认鼠标滚轮和平板上的双指头pinch手势可改变zoom的值。按空格键可重置zoom到默认值1
(该操作同时也重置了translateX和translateY为0)。
以下为缩放相关默认全局参数,可通过 htconfig 自定义:
ht.Default.zoomIncrement = 1.3调用zoomIn和zoomOut函数的缩放步进ht.Default.scrollZoomIncrement = 1.05鼠标滚轮缩放步进ht.Default.pinchZoomIncrement = 1.08触屏双指缩放步进ht.Default.zoomMax = 20最大放大值ht.Default.zoomMin = 0.01最小缩小值 以下为GraphView缩放相关函数,以下函数中的point参数代表进行缩放的中心基准,
一般会传入graphView.getLogicalPoint(event)的返回值,即已当前鼠标点为中心进行缩放,
该参数为空时则以当前可见矩形区域的中心进行缩放。
zoomIn(anim, point)放大zoomOut(anim, point)缩小 zoomReset(anim, point)设置缩放值为1 scrollZoomIn(point)滚轮放大时调用scrollZoomOut(point)滚动缩小时调用pinchZoomIn(point)触屏双指放大时调用pinchZoomOut(point)触屏双指缩小时调用getZoom()和setZoom(value, anim, point)获取和设置缩放值、最终设置的值会调用adjustZoom进行控制adjustZoom(value)传入即将修改的缩放值,返回最终运行设置的缩放值,可重载进行自定义ajustZoom函数默认实现如下,对ht.Default上配置的最大值和最小值进行了限制:
adjustZoom = function(value){
if(value < ht.Default.zoomMin){
return ht.Default.zoomMin;
}
if(value > ht.Default.zoomMax){
return ht.Default.zoomMax;
}
return value;
};
GraphView默认内置了一些交互器,以实现基本的选择、单双击、缩放、平移和编辑等交互功能,内置的交互器有:
Interactor交互器基类,提供了基础功能函数,如交互事件派发,监听函数添加和清除,拖拽操作封装,自动平移滚动等功能DefaultInteractor实现Group、Edge和SubGraph图元的默认双击响应,手抓图平移,滚轮缩放,键盘响应等功能SelectInteractor实现图元可单选和框选功能。默认拖动背景是平移,按Ctrl键可以进行框选(Mac下为Command键)MoveInteractor实现被选中图元的移动功能EditInteractor实现对图元的大小改变和角度旋转,以及Shape和Edge类型图元的多点编辑等功能TouchInteractor实现移动设备上的Touch交互功能ScrollBarInteractor实现滚动条的显示和交互功能可通过GraphView#setInteractors(list)组合这些交互器,用户也可以基于Interactor扩展自己的交互器,
以下代码为GraphView#setEditable(false/true)的实现,GraphView构造函数会调用setEditable(false),
因此默认只有基本的操作功能不具备编辑功能,需要编辑功能可调用setEditable(true)实现。
setEditable: function (editable) {
var self = this;
if (editable) {
self.setInteractors([
new ScrollBarInteractor(self),
new SelectInteractor(self),
new EditInteractor(self),
new MoveInteractor(self),
new DefaultInteractor(self),
new TouchInteractor(self)
]);
} else {
self.setInteractors([
new ScrollBarInteractor(self),
new SelectInteractor(self),
new MoveInteractor(self),
new DefaultInteractor(self),
new TouchInteractor(self, {editable: false})
]);
}
},
TouchInteractor该类的第二个参数可传入json对象控制部分功能的开启和关闭,默认为开启
selectable是否允许选中movable是否允许移动pannable是否允许平移pinchable是否允许缩放editable是否允许编辑内置的Interactor在交互过程会派发出事件,可通过GraphView#addInteractorListener进行监听,简写为mi
graphView.addInteractorListener(function (e) {
if(e.kind === 'clickData'){
console.log(e.data + '被单击');
}
else if(e.kind === 'doubleClickData'){
console.log(e.data + '被双击');
}
else if(e.kind === 'clickBackground'){
console.log('单击背景');
}
else if(e.kind === 'doubleClickBackground'){
console.log('双击背景');
}
else if(e.kind === 'beginRectSelect'){
console.log('开始框选图元');
}
else if(e.kind === 'betweenRectSelect'){
console.log('正在框选图元');
}
else if(e.kind === 'endRectSelect'){
console.log('结束框选图元');
}
else if(e.kind === 'beginMove'){
console.log('开始移动图元');
}
else if(e.kind === 'betweenMove'){
console.log('正在移动图元');
}
else if(e.kind === 'endMove'){
console.log('结束移动图元');
}
else if(e.kind === 'beginPan'){
console.log('开始手抓图平移');
}
else if(e.kind === 'betweenPan'){
console.log('正在手抓图平移');
}
else if(e.kind === 'endPan'){
console.log('结束手抓图平移');
}
else if(e.kind === 'beginEditRect'){
console.log('开始编辑图元大小和位置');
}
else if(e.kind === 'betweenEditRect'){
console.log('正在编辑图元大小和位置');
}
else if(e.kind === 'endEditRect'){
console.log('结束编辑图元大小和位置');
}
else if(e.kind === 'beginEditPoint'){
console.log('开始编辑多边形Shape或多点Edge的具体点');
}
else if(e.kind === 'betweenEditPoint'){
console.log('正在编辑多边形Shape或多点Edge的具体点');
}
else if(e.kind === 'endEditPoint'){
console.log('结束编辑多边形Shape或多点Edge的具体点');
}
else if(e.kind === 'beginEditRotation'){
console.log('开始旋转图元');
}
else if(e.kind === 'betweenEditRotation'){
console.log('正在旋转图元');
}
else if(e.kind === 'endEditRotation'){
console.log('结束旋转图元');
}
else if(e.kind === 'moveLeft'){
console.log('左方向键左移图元一个像素');
}
else if(e.kind === 'moveRight'){
console.log('右方向键右移图元一个像素');
}
else if(e.kind === 'moveUp'){
console.log('上方向键上移图元一个像素');
}
else if(e.kind === 'moveDown'){
console.log('下方向键下移图元一个像素');
}
else if(e.kind === 'toggleNote'){
console.log('切换note标注的展开合并');
}
else if(e.kind === 'toggleNote2'){
console.log('切换note2标注的展开合并');
}
else if(e.kind === 'beginEditPoints'){
console.log('开始进入曲线的点编辑状态');
}
else if(e.kind === 'endEditPoints'){
console.log('结束曲线的点编辑状态');
}
else if(e.kind === 'hover'){
console.log('鼠标停留');
}
else if(e.kind === 'onClick'){
console.log('单击图元');
}
else if(e.kind === 'onDoubleClick'){
console.log('双击图元');
}
else if(e.kind === 'onContextMenu'){
console.log('右击图元');
}
else if(e.kind === 'onDown'){
console.log('在图元处按下');
}
else if(e.kind === 'onUp'){
console.log('在图元处放开');
}
else if(e.kind === 'onMove'){
console.log('鼠标在图元上移动');
}
else if(e.kind === 'onEnter'){
console.log('鼠标进入图元');
}
else if(e.kind === 'onHover'){
console.log('鼠标在图元上悬停');
}
else if(e.kind === 'onLeave'){
console.log('鼠标离开图元');
}
else if(e.kind === 'onBeginDrag'){
console.log('图元开始拖拽');
}
else if(e.kind === 'onDrag'){
console.log('图元拖拽');
}
else if(e.kind === 'onEndDrag'){
console.log('图元结束拖拽');
}
else if(e.kind === 'onScroll'){
console.log('鼠标图元上滚动');
}
});
注意:后面的如onClick的on开头的事件,必须设置data.s('interactive', true);来开启节点交互功能才会触发,也可以通过重载gv.isInteractive方法来定义节点可交互逻辑。
节点开启交互后,就会阻止图纸/场景上默认的交互行为,比如在交互节点上无法触发平移,可以使用data.s('preventDefaultWhenInteractive', false);来关闭这一机制,也可以通过重载 gv.preventDefaultWhenInteractive方法定义。
关于交互还有个逻辑坐标点(LogicalPoint)概念,可平移和缩放的组件一般都具有getLogicalPoint(event)函数,
根据交互事件返回相应坐标点信息,简单的理解逻辑坐标点和用户设置model的坐标是一致的,而真正显示在屏幕的坐标点,
需要通过zoom和translate的转换,在GraphView改变zoom和translate的过程,图元模型的数据并未改变,
也就是逻辑坐标值不变,仅仅是视图组件的呈现效果变化,DataModel中所有图元的逻辑坐标信息如position、points、
width和height等保持不变,因此当自定义交互事件处理时,常需要调用以下交互相关的屏幕坐标和逻辑坐标转换函数:
getLogicalPoint(event)传入交互event事件参数,返回对应的逻辑坐标点,简写为lpgetDataAt(pointOrEvent, filter)传入逻辑坐标点或者交互event事件参数,返回当前点下的图元,filter可进行过滤getSelectedDataAt(pointOrEvent)传入逻辑坐标点或者交互event事件参数,返回当前点下已选中的图元。getDatasInRect(rect, intersects, selectable)获取逻辑坐标区域内的图元。rect代表逻辑坐标区域。intersects指定是相交选中还是包含选中,true表示相交,false表示包含。selectable指定图元是否要求可选中,为空代表不要求,可否选中通过GraphView.isSelectable函数判断moveSelection(xOffset, yOffset)将选中图元水平移动xOffset,垂直移动yOffsetGraphView上有一系列on*类型的回调函数,可重载做后续处理或改变默认实现逻辑:
onDataClicked(data, event)图元被点击时回调onDataDoubleClicked(data, event)图元被双击时回调onEdgeDoubleClicked(edge, event)连线图元被双击时回调,默认实现调用edge.toggle()onGroupDoubleClicked(group, event)组类型图元被双击时回调,默认实现调用group.toggle()onSubGraphDoubleClicked(subGraph, event)子网图元被双击时回调,默认实现进入子网graphView.setCurrentSubGraph(subGraph)onBackgroundClicked(event)背景单击时回调onBackgroundDoubleClicked(event)背景双击时回调,默认实现调用upSubGraph()进入上一层子网onCurrentSubGraphChanged(event)当前子网变化时回调,默认实现调用reset()恢复默认缩放和平移值onAutoLayoutEnded()自动布局动画结束后时回调onMoveEnded()移动图元位置结束时回调onPanEnded()手抓图平移拓扑图结束时回调onRectSelectEnded()框选结束时回调onTranslateEnded()平移动画结束时回调onZoomEnded()缩放拓扑图动画结束时回调onPinchEnded()触屏进行双指缩放结束时回调onSelectionChanged()选中变化时回调,默认实现会使得该选中图元出现在拓扑图上的可见范围除了调用GraphView封装的函数外,用户也可以直接添加原生的html组件监听事件,如Node章节的例子,
通过graphView.getView().addEventListener直接对底层div添加监听,以下代码有几点需要注意:
ht.Default.isTouchable ? 'touchend' : 'mouseup'判断为桌面及触屏终端做周全的事件类型考虑graphView.getDataAt(e)直接查找到事件下的图元ht.Default.isDoubleClick(e)判断单击和双击的事件区分在前面提到的HT封装的onDataClicked和on**Clicked等事件,都是在mousedown和touchstart时触发,
如果需要监听在mouseup和touchend的放手后事件处理,可通过对html原生的事件进行监听,
应用较多的是在点击图元需要弹开对话框的情况,如果直接在HT封装的on*Clicked事件处理内直接调用弹出对话框之类操作,
会影响HT后续的交互事件处理,所以交互事件内影响界面的功能,可选择监听在mouseup和touchend里面处理,
有些情况下甚至需要再调用ht.Default.callLater(function(){})的方式进行处理:
var eventType = ht.Default.isTouchable ? 'touchend' : 'mouseup';
graphView.getView().addEventListener(eventType, function(e){
var data = graphView.getDataAt(e);
if(data && ht.Default.isDoubleClick(e)){
alert(data.getName() + ' is double clicked.');
}
});
以下示例自定义了创建Node、Edge和Shape的三种继承于ht.graph.Interactor交互器:
过滤机制贯穿HT框架,而GraphView对过滤机制的运用尤为集中,合理运用这些过滤器可以灵活控制是否允许图元可见、移动、编辑等逻辑。
isMovable: function (data) {
if(data instanceof ht.Edge){
return false;
}
return this._movableFunc ? this._movableFunc(data) : true;
},
以上代码是GraphView.isMovable(data)函数的简化版,MoveInteractor类在处理拖动时会调用此函数来决定图元是否可移动,
this._movableFunc属性是通过GraphView.setMovableFunc(func)设置的,由代码可知默认情况下Edge是不允许拖动的,
然后判断是否设置过movableFunc函数属性,如果设置了则以该函数的逻辑决定是否可移动,最后才返回true。
因此要自定义可否移动逻辑,有两个途径:
* 设置`GraphView.setMovableFunc(func)`函数属性。
* 重载`GraphView.isMovable(data)`,这种方式客户需要考虑原始`isMovable`的实现逻辑。
下面列表是常见的过滤器函数:
GraphView.isVisible(data)GraphView.setVisibleFunc(func)GraphView.isSelectable(data),默认实现返回GraphView.getSelectionModel().isSelectable(data) GraphView.getSelectionModel().setFilterFunc(func)GraphView.setSelectableFunc(func),该函数内部调用GraphView.getSelectionModel().setFilterFunc(func)GraphView.isEditable(data)GraphView.setEditableFunc(func)GraphView.isRectEditable(data)GraphView.setRectEditableFunc(func)GraphView.isRotationEditable(data)GraphView.setRotationEditableFunc(func)GraphView.isAnchorEditable(data)GraphView.setAnchorEditableFunc(func)GraphView.isPointEditable(data)GraphView.setPointEditableFunc(func)不少刚开始使用HT的用户对过滤器设置在视图组件上而不是数据模型上不解,如果将过滤器控制在模型上,
则所有共享同一模型的组件只能具有相同的过滤逻辑,HT这样的设计思想能使得不同的组件具有不同的过滤逻辑。
除在视图组件上设置过滤器外,GraphView的内置过滤机制也参考了以下style属性,用户可直接改变以下style达到对单个图元的控制效果:
2d.visible:默认值为true,控制图元在GraphView上是否可见2d.selectable:默认值为true,控制图元在GraphView上是否可选中2d.movable:默认值为true,控制图元在GraphView上是否可移动2d.editable:默认值为true,控制图元在GraphView上是否可编辑2d.move.mode:默认值为空,控制图元移动范围,可设置为如下参数: xy:可在xy平面移动 x:仅沿x轴移动 y:仅沿y轴移动 同样对于Graph3dView也有类似的控制参数
3d.visible:默认值为true,控制图元在Graph3dView上是否可见3d.selectable:默认值为true,控制图元在Graph3dView上是否可选中3d.movable:默认值为true,控制图元在Graph3dView上是否可移动3d.editable:默认值为true,控制图元在Graph3dView上是否可编辑3d.move.mode:默认值为空,控制图元移动范围,可设置为如下参数:xyz:可在三维空间移动xy:仅在xy平面移动 xz:仅在xz平面移动yz:仅在yz平面移动x:仅沿x轴移动 y:仅沿y轴移动 z:仅沿z轴移动 HT的数据Data可分为三种属性类型:
get/set或is/set类型,例如getName()、setName('ht')和isExpaned(),用于常用属性操作attr类型,通过getAttr(name)和setAttr(key, value)存取,该类型是HT预留给用户存储业务数据style类型,通过getStyle(name)和setStyle(name, value)进行操作,GraphView上图元样式由该类型属性控制以下代码为HT内部对style相关函数实现,默认图元的_styleMap为空,查询值时会参考ht.Style的全局默认值:
getStyleMap: function(){
return this._styleMap;
},
getStyle: function (name, checkDefault) {
if (checkDefault === undefined) {
checkDefault = true;
}
var value = this._styleMap ? this._styleMap[name] : undefined;
if (value === undefined && checkDefault) {
return ht.Style[name];
} else {
return value;
}
},
setStyle: function (name, newValue) {
if (!this._styleMap) {
this._styleMap = {};
}
var oldValue = this._styleMap[name];
if(newValue === undefined){
delete this._styleMap[name];
}else{
this._styleMap[name] = newValue;
}
if (this.fp('s:' + name, oldValue, newValue)) {
this.onStyleChanged(name, oldValue, newValue);
}
},
以下为部分style属性说明,更多属性由后续章节介绍:
image.stretch绘制图片的拉伸类型,默认为fill,可设为uniform或centerUniform,参见imageingroup决定图元是否要包含在展开的Group内部,默认为trueopacity属性用于控制整个图元的透明度,可取值为0~1body.color设置该颜色将改变图元中心渲染,可重载GraphView.getBodyColor(data)函数自定义:image图片的Node,将自动绘制成被body.color染色后的图片shape的矢量,如需要填充背景,则shape.background被body.color替代shape的矢量,如无需填充背景,则shape.border.color被body.color替代Edge的连线类型,edge.color会被body.color替代GraphView的图元被选中时默认会显示一个选中边框,选中边框的效果可以通过style上的select.*相关属性控制。
select.color选中边框颜色select.width选中边框宽度,默认值为1,此属性为0表示不绘制选中边框select.padding选中边框与Node.getRect()的间距,默认值为2,可为负值,此属性对ht.Shape和ht.Edge图元无意义。select.type选中边框的形状,默认值为rect,可选值参见shape章节,设置成shadow则显示为阴影效果。shadow.offset.x阴影水平偏移,默认值为3shadow.offset.y阴影垂直偏移,默认值为3shadow.blur阴影模糊级别,默认值为6boder样式在图元的边缘绘制边框效果,用于告警或提示的作用,可通过重载GraphView.getBorderColor(data)函数自定义。
border.color边框颜色,默认值为空代表不绘制border.width边框宽度,默认值为2border.padding边框离Node.getRect()的间距,默认值为2,可为负值,该属性对ht.Shape和ht.Edge类型图元无意义border.type边框的形状,默认值为rect,可选值参见shapeGraphView上的Node图元除了通过image属性设置显示为图片外,还可以设置为HT框架内置的各种多边形类型进行矢量绘制。
要显示成矢量格式可通过设置Node类型图元的style属性实现,相关style属性名说明如下:
shape字符串类型,决定shape的形状,默认值为空,代表用image绘制,支持类型如下:rect矩形circle圆形oval椭圆形roundRect四周圆角矩形star星形形triangle三角形hexagon六边形pentagon五边形diamond钻石形rightTriangle直角三角形parallelogram平行四边形trapezoid梯形polygon多边形arc圆弧形shape.border.width边框宽度,默认值为0表示不绘制边框shape.border.color边框颜色shape.border.cap边框末端线帽的样式,可选参数为butt|round|square
shape.border.join边框当两条线交汇时创建边角的类型,可选参数为bevel|round|miter
shape.border.pattern显示虚线样式,Array类型,例如[5, 5]shape.depth只对rect类型起作用,正值代表凸起,负值代表凹陷,默认值为0shape.background背景填充颜色,为null代表不填充背景shape.gradient渐近色类型:shape.background纯色填充背景。'linear.southwest','linear.southeast','linear.northwest','linear.northeast',
'linear.north','linear.south','linear.west','linear.east',
'radial.center','radial.southwest','radial.southeast','radial.northwest','radial.northeast',
'radial.north','radial.south','radial.west','radial.east',
'spread.horizontal','spread.vertical','spread.diagonal','spread.antidiagonal',
'spread.north','spread.south','spread.west','spread.east'shape.gradient.color背景渐近颜色 shape.repeat.image填充重复背景的图片,注意这里的图片不支持矢量shape.dash是否显示虚线,默认值为false shape.dash.pattern虚线样式,默认值为[16, 16]shape.dash.offset虚线偏移,默认值为0shape.dash.color虚线颜色shape.dash.width虚线宽度,默认为空代表采用shape.border.width值 shape.dash.3d虚线是否显示3d效果,默认值为falseshape.dash.3d.color虚线3d效果颜色,为空采用默认白色,呈现3d效果时连线的中间部分为该颜色shape.dash.3d.accuracy虚线3d效果精确度,该值越小3d渐进效果越好但影响性能,一般情况无需修改shape.corner.radius该参数指定roundRect类型的圆角半径,默认为空系统自动调节,可设置正数值shape.polygon.side多边形边数,该参数指定polygon类型的变数,默认值为6shape.arc.from圆弧形起始弧度,默认值为Math.PIshape.arc.to圆弧形结束弧度,默认值为2*Math.PIshape.arc.close圆弧形是否闭合,默认为trueshape.arc.oval圆弧形是否为椭圆,默认为false详见位置手册
GraphView的图元可以增加文字说明,如对图元setName('hello HT'),则图元下方将显示该hello HT文字。
上章position的例子文字信息并非通过setName设置,
而是通过setStyle('label', 'hello HT');的方式设置,最终显示文字由GraphView.getLabel函数决定:
getLabel: function (data) {
var label = data.getStyle('label');
return label === undefined ? data.getName() : label;
},
通过以上代码可知,style上的label属性优先级高于name属性,可重载GraphView.getLabel函数改变文字获取逻辑。
*View.getLabel的设计方式贯穿HT的所有组件,在ht.widget.*包下的ListView,TreeView,TabView等组件都采用类似的方式,
与GraphView有所不同的是,其他组件默认的实现逻辑都是返回data.toLabel()值,以下是Data#toLabel()的默认实现。
toLabel: function(){
return this._displayName || this._name;
}
通过以上代码可知,displayName属性的优先级高于name属性,且style属性一般只用于GraphView组件,
其他组件并不会考虑style上的label属性,当Tree和GraphView共享同一DataModel数据模型时,
需要在Tree和GraphView上分别显示不一样的文字,这种情况下就可以通过设置style上的label属性,
或者设置displayName属性即可达到不一样的效果,也可以直接重载组件的getLabel函数自定义逻辑。
HT默认除了label.*的属性外,还提供了label2.*的属性,用于满足一个图元需要显示双文字的情况,
label2和label的属性意义是一一对应的:
label文字内容,默认为空label.font文字字体类型,如:10px sans-seriflabel.color文字颜色,可通过GraphView.getLabelColor(data)自定义label.background背景颜色,默认为空,可通过GraphView.getLabelBackground(data)自定义label.opacity文字透明度,值范围0~1label.position文字显示位置,参见positionlabel.offset.x文字水平偏移,对于Edge意味着沿着连线方向水平偏移label.offset.y文字垂直偏移,对于Edge意味着沿着连线方向垂直偏移label.rotation文字旋转弧度,如setStyle('label.rotation', Math.PI/2)label.max默认为空表示不限制最大宽度,如果设为正值,则文字最大显示宽度不会超过此值label.align文字水平对齐,可设置left,center或rightlabel.position.fixed默认为false,为true时对Edge类型的文字起作用,保持文字始终在连线之上或之下的位置label.scale文字缩放值,默认值为1,参见位置手册例子note一般作为图元的标注,有提示和警告的作用,以冒泡的形式呈现,也可缩小合并成一个小标注。
note的参数通过style的note.*相关属性控制,与label一样,
为了满足一个图元双标注的需求,提供了note2.*第二个标注参数:
note标注文字内容,可通过GraphView.getNote(data)自定义note.expanded标注是否展开,默认为true,设置为false则合并缩成小标注note.font标注文字字体类型,如:10px sans-serifnote.color标注文字颜色note.background标注背景填充颜色,可通过GraphView.getNoteBackground(data)自定义note.border.width标注边框宽度,默认值为1note.border.color标注边框颜色note.opacity透明度,值范围0~1note.position标注显示位置,参见positionnote.offset.x标注显示位置水平偏移note.offset.y标注显示位置垂直偏移note.max默认为空代表不限制最大宽度,如果设为正值,则文字最大显示宽度不会超过此值note.align标注水平对齐,可设置left,center或rightnote.toggleable默认值为true表示允许双击展开合并切换,如设为false,表示不响应双击note.scale标注缩放值,默认值为1,参见位置手册例子 icon和note类似,展示在图元周围,有提示和警告的作用,不同的是note展示文字,而icon展示图片或
矢量。
addStyleIcon(name, icons) 增加一组icon,name参数指定这组icon的名称,icons参数描述icon的内容removeStyleIcon(name) 删除name参数对应的icons以上为ht.Data提供的操作icon的函数,这两个方法实际修改了style的icons属性,
用户也可以通过setStyle('icons', icons)和getStyle('icons')设置和获取icons。
addStyleIcon方法第二个参数icons为json格式对象,其属性含义如下:
names包含多个字符串的数组,每个字符串对应一张图片或矢量visible表示该组图片是否显示for3d代表该组图片仅用于Graph3dView的组件显示,不显示于GraphView组件position指定icons的位置,参考positionoffsetX将icons的位置在position的基础上,水平偏移offsetXoffsetY将icons的位置在position的基础上,垂直偏移offsetYdirection可取值为west、east、north、south之一,指定icons的排列方向keepOrien连线方向改变时,icons会自动调整方向以保持最好的阅读效果(比如文字),此属性为true表示禁止自动调整方向gap指定`图标之间的距离rotation指定图标的旋转弧度rotationFixed该参数使得图标旋转不考虑Edge连线角度,只根据rotation决定,默认值为falsewidth指定每个图标的宽度height指定每个图标的高度opacity透明度,值范围0~1stretch图标绘制拉伸方式,默认为fill,可设置为uniform和centerUniform的不失真方式positionFixed默认为false,为true时对Edge类型的图标起作用,保持图标始终在连线之上或之下的位置edge.addStyleIcon("flags", {
position: 17,
direction: 'east',
offsetX: -26,
gap: 10,
names: ['china', 'spain', 'usa']
});
edge.addStyleIcon("arrow1", {
position: 2,
width: 50,
height: 25,
keepOrien: true,
names: ['arrow']
});
edge.addStyleIcon("arrow2", {
position: 4,
width: 50,
height: 25,
positionFixed: true,
names: ['arrow']
});
以上示例中edge对象通过addStyleIcon的方式,设置一排由['china', 'spain', 'usa']组合的三个国旗图标,
以及两个arrow的矢量图标,以上代码可用以下代码替代:
edge.setStyle('icons', {
flags: {
position: 17,
direction: 'east',
offsetX: -26,
gap: 10,
names: ['china', 'spain', 'usa']
},
arrow1: {
position: 2,
width: 50,
height: 25,
keepOrien: true,
names: ['arrow']
},
arrow2: {
position: 4,
width: 50,
height: 25,
positionFixed: true,
names: ['arrow']
}
});
示例中node1通过node1.setStyle("icons", ...)注册了三个矢量圆球,通过重载graphView.onDataClicked,
并结合graphView.getIconInfoAt函数能得到点击的具体icon图标信息,进而改变连线箭头颜色
graphView.onDataClicked = function(data, e) {
if(data === node1){
var info = this.getIconInfoAt(e, data);
if(info){
edge.a('arrow.color', info.name.comps[0].background);
node1.a('select.index', info.index);
}
}
};
Group组类型图元可设置如下样式:
group.type组类型,默认为空,组张开时显示为上部分title文字标题,下部分为矩形填充,
还可设置为oval和rect等形状,可选值参见shape,
设置为非空类型时label不再显示为title标题状态,采用普通label的style属性进行显示和控制group.image默认为空,用于显示组展开的呈现图片效果group.image.stretch组展开图片拉伸模式,默认值为fill,还可取值uniform和centerUniformgroup.repeat.image填充重复背景的图片,注意这里的图片不支持矢量group.position组合并是的位置,默认值为17代表居中group.toggleable决定组是否允许双击进行展开和合并的切换,默认为truegroup.padding'组展开后四边缘与孩子图元的间距,默认值为8`group.padding.left'组展开后左边缘与孩子图元的间距,默认值为0`group.padding.right'组展开后右边缘与孩子图元的间距,默认值为0`group.padding.top'组展开后上边缘与孩子图元的间距,默认值为0`group.padding.bottom'组展开后下边缘与孩子图元的间距,默认值为0`group.title.font组展开后的title文字字体,仅对group.type为空的类型起作用group.title.color组展开后的title文字颜色,仅对group.type为空的类型起作用group.title.background组展开后的title背景颜色,仅对group.type为空的类型起作用group.title.align组展开后的title文字水平对齐方式,默认值为'left',可设置为center和rightgroup.depth组展开后的边缘的深度,0代表平面效果,正值代表凸起效果,负值代表凹陷效果,默认值为1group.background组展开后的背景颜色 group.gradient组展开后背景渐近类型,可选值参见shape,group.gradient.color组展开后背景渐近颜色 group.border.color组展开后的边框颜色group.border.width组展开后的边框宽度,group.border.pattern组展开后的边框显示虚线样式,Array类型,例如[5, 5]group.border.cap组展开后的边框末端线帽的样式,可选参数为butt|round|squaregroup.border.join组展开后的边框当两条线交汇时创建边角的类型,可选参数为bevel|round|miteredge.type字符串类型,决定连线走向样式:undefined默认值,代表连接成直线,多条时自动成组,自环时绘制成圆形points类型下连线的走向将由edge.points属性决定,用于绘制折线或曲线edge.points默认值为空,可设置ht.List或Array类型的{x:10, y:20}格式点对象数组edge.segments用于描述点连接样式的ht.List或Array类型数组,需结合起始点和结束点考虑,数组元素为整型值:moveTo,占用1个点信息,代表一个新路径的起点lineTo,占用1个点信息,代表从上次最后点连接到该点quadraticCurveTo,占用2个点信息,第一个点作为曲线控制点,第二个点作为曲线结束点bezierCurveTo,占用3个点信息,第一和第二个点作为曲线控制点,第三个点作为曲线结束点closePath,不占用点信息,代表本次路径绘制结束,并闭合到路径的起始点edge.color连线颜色edge.width连线宽度,默认值为2edge.offset连线端点和图元中心点的距离,默认值为20edge.group对于成组的连线,可通过此属性归为不同的组,以达到独立展开合并的效果,默认值为0edge.gap成组连线之间的间距,默认值为12edge.toggleable决定是否响应双击进行展开合并的切换,默认值为trueedge.center决定连线是否聚集到中心,默认值为false edge.pattern显示虚线样式,Array类型,例如[5, 5]edge.expanded判断当前处于展开还是合并状态,一般只用于读取,设置可调用Edge.toggle()函数同步修改组中其他连线参数edge.cap连线末端线帽的样式,可选参数为butt|round|square
edge.join折线交汇时创建边角的类型,可选参数为bevel|round|miter
edge.source.position默认值为17,起始点相对于起始图元的相对位置edge.source.anchor.x默认值为空,起始点相对于起始图元的横向相对比例(浮点类型,0为左侧,1是右侧,0.5为中心)edge.source.anchor.y默认值为空,起始点相对于起始图元的纵向相对比例(浮点类型,0为上方,1是下方,0.5为中心)edge.source.offset.x默认值为0,起始点的水平偏移edge.source.offset.y默认值为0,起始点的垂直偏移edge.target.position默认值为17,结束点相对于结束图元的相对位置edge.target.anchor.x默认值为空,结束点相对于结束图元的横向相对比例(浮点类型,0为左侧,1是右侧,0.5为中心)edge.target.anchor.y默认值为空,结束点相对于结束图元的纵向相对比例(浮点类型,0为上方,1是下方,0.5为中心)edge.target.offset.x默认值为0,结束点的水平偏移edge.target.offset.y默认值为0,结束点的垂直偏移 edge.dash是否显示虚线,默认值为falseedge.dash.pattern虚线样式,默认值为[16, 16]edge.dash.offset虚线偏移,默认值为0edge.dash.color虚线颜色 edge.dash.width虚线宽度,默认为空代表采用edge.width值 edge.dash.3d虚线是否显示3d效果,默认值为falseedge.dash.3d.color虚线3d效果颜色,为空采用默认白色,呈现3d效果时连线的中间部分为该颜色edge.dash.3d.accuracy虚线3d效果精确度,该值越小3d渐进效果越好但影响性能,一般情况无需修改对于
edge.type为points类型的连线,当edge.center为false,且edge.offset为0时,连线起始和结束线段将根据关联节点的矩形边缘进行裁剪。
Object-Oriented JavaScript
开发HTML5应用,特别是企业应用,JavaScript几乎占据绝大部分代码量,所以需要掌握js语法,内置的类和函数,
与其他面向对象语言不同的类继承模式,以及DOM的基本操作,对于这些方面知识此书是不错的选择。
Pro JavaScript Design Patterns
设计模式已经在各种软件开发中被广泛运用,但是很多耳熟能详的设计模式,用js语言的实现方式与Java和C#等传统面对对象语言的实现方式还是有很多差异,
导致很多初学者往往无从下手,或者设计出来的模式不是最佳的js实践,此书对接口、单类、继承、工厂等模式给出了js语言的可选择实现方式。
CSS3: The Missing Manual
HT产品提供了丰富的企业应用组件,做出一个常规的案例仅用js也足够,但更美观的界面效果离不开CSS技术的掌握和运用,
进行HT产品深度定制扩展很多时候也是离不开CSS技术。此书的第一二版就好评如潮,第三版更是针对HTML5进行了改版,
删除了以前针对老版IE浏览器的章节,增加了针对CSS3新特性的章节篇幅,是一本非常值得前端程序员通读的经典读物。
Responsive Web Design with HTML5 and CSS3 Responsive Web Design(RWD)是前端技术的新领域,最早提出“响应式Web设计”概念的鼻祖文章是
Responsive Web Design。
一个优秀的页面不仅要在PC浏览器下良好呈现,也需要考虑用户采用手机或平板进行浏览时的呈现效果,
因此程序员和设计师需要考虑不同屏幕尺寸等复杂情况,保持桌面鼠标以及移动设备手势操作都有较好的用户体验,
此书起了抛砖引玉的作用,可以让大家了解目前常见的RWD的设计技巧,开始对这方面技术进行关注和运用。
系统的学习一门技术我们还是推荐阅读书籍,但是写出一本优秀经典的书籍一般需要几个月甚至几年时间, 对于紧跟前沿的程序员还需要一定的途径获取最新的技术资讯,在此强烈推荐三处针对前端技术开发的周刊订阅, 每周只需看看这三封周刊邮件,基本不会错过地球上前端开发领域正在发生的有趣事件: