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
、model
um
: unmonitor
f
: fire
HT
简写函数列表如下:
GraphView#addInteractorListener
= GraphView#mi
GraphView#removeInteractorListener
= GraphView#umi
GraphView#fireInteractorEvent
= GraphView#fi
Graph3dView#addInteractorListener
= Graph3dView#mi
Graph3dView#removeInteractorListener
= Graph3dView#umi
Graph3dView#fireInteractorEvent
= Graph3dView#fi
SelectionModel#addSelectionChangeListener
= SelectionModel#ms
SelectionModel#removeSelectionChangeListener
= SelectionModel#ums
SelectionModel#getFirstData
= SelectionModel#fd
SelectionModel#getLastData
= SelectionModel#ld
SelectionModel#contains
= SelectionModel#co
SelectionModel#setSelection
= SelectionModel#ss
SelectionModel#appendSelection
= SelectionModel#as
SelectionModel#selectAll
= SelectionModel#sa
SelectionModel#removeSelection
= SelectionModel#rs
SelectionModel#clearSelection
= SelectionModel#cs
DataModel#getSelectionModel
= DataModel#sm
DataModel#addDataModelChangeListener
= DataModel#mm
DataModel#removeDataModelChangeListener
= DataModel#umm
DataModel#addDataPropertyChangeListener
= DataModel#md
DataModel#removeDataPropertyChangeListener
= DataModel#umd
DataModel#addHierarchyChangeListener
= DataModel#mh
DataModel#removeHierarchyChangeListener
= DataModel#umh
firePropertyChange
= fp
addPropertyChangeListener
= mp
removePropertyChangeListener
= ump
getPosition
= p
setPosition
= p
getTranslateX
= tx
setTranslateX
= tx
getTranslateY
= ty
setTranslateY
= ty
getStyle
= s
setStyle
= s
getAttr
= a
setAttr
= a
invalidate
= iv
invalidateModel
= ivm
getSelectionModel
= sm
getLogicalPoint
= lp
Toolbar#setValue
= v
Toolbar#getValue
= v
FormPane#setValue
= v
FormPane#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)
返回元素在数组中得索引,若不存在返回-1
each(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
,无返回false
getHost()
和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)
获取和设置是否自动隐藏滚动条,默认为true
HT
组件的坐标原点默认在左上角,大部分组件都有平移功能,平移的可视化效果就是滚动,
分为水平横坐标平移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
事件参数,返回对应的逻辑坐标点,简写为lp
getDataAt(pointOrEvent, filter)
传入逻辑坐标点或者交互event
事件参数,返回当前点下的图元,filter
可进行过滤getSelectedDataAt(pointOrEvent)
传入逻辑坐标点或者交互event
事件参数,返回当前点下已选中的图元。getDatasInRect(rect, intersects, selectable)
获取逻辑坐标区域内的图元。rect
代表逻辑坐标区域。intersects
指定是相交选中还是包含选中,true
表示相交,false
表示包含。selectable
指定图元是否要求可选中,为空代表不要求,可否选中通过GraphView.isSelectable
函数判断moveSelection(xOffset, yOffset)
将选中图元水平移动xOffset
,垂直移动yOffset
GraphView
上有一系列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
内部,默认为true
opacity
属性用于控制整个图元的透明度,可取值为0~1
body.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
阴影水平偏移,默认值为3
shadow.offset.y
阴影垂直偏移,默认值为3
shadow.blur
阴影模糊级别,默认值为6
boder
样式在图元的边缘绘制边框效果,用于告警或提示的作用,可通过重载GraphView.getBorderColor(data)
函数自定义。
border.color
边框颜色,默认值为空代表不绘制border.width
边框宽度,默认值为2
border.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
类型起作用,正值代表凸起,负值代表凹陷,默认值为0
shape.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
虚线偏移,默认值为0
shape.dash.color
虚线颜色shape.dash.width
虚线宽度,默认为空代表采用shape.border.width
值 shape.dash.3d
虚线是否显示3d
效果,默认值为false
shape.dash.3d.color
虚线3d
效果颜色,为空采用默认白色,呈现3d
效果时连线的中间部分为该颜色shape.dash.3d.accuracy
虚线3d
效果精确度,该值越小3d
渐进效果越好但影响性能,一般情况无需修改shape.corner.radius
该参数指定roundRect
类型的圆角半径,默认为空系统自动调节,可设置正数值shape.polygon.side
多边形边数,该参数指定polygon
类型的变数,默认值为6
shape.arc.from
圆弧形起始弧度,默认值为Math.PI
shape.arc.to
圆弧形结束弧度,默认值为2*Math.PI
shape.arc.close
圆弧形是否闭合,默认为true
shape.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-serif
label.color
文字颜色,可通过GraphView.getLabelColor(data)
自定义label.background
背景颜色,默认为空,可通过GraphView.getLabelBackground(data)
自定义label.opacity
文字透明度,值范围0~1
label.position
文字显示位置,参见positionlabel.offset.x
文字水平偏移,对于Edge
意味着沿着连线方向水平偏移label.offset.y
文字垂直偏移,对于Edge
意味着沿着连线方向垂直偏移label.rotation
文字旋转弧度,如setStyle('label.rotation', Math.PI/2)
label.max
默认为空表示不限制最大宽度,如果设为正值,则文字最大显示宽度不会超过此值label.align
文字水平对齐,可设置left
,center
或right
label.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-serif
note.color
标注文字颜色note.background
标注背景填充颜色,可通过GraphView.getNoteBackground(data)
自定义note.border.width
标注边框宽度,默认值为1
note.border.color
标注边框颜色note.opacity
透明度,值范围0~1
note.position
标注显示位置,参见positionnote.offset.x
标注显示位置水平偏移note.offset.y
标注显示位置垂直偏移note.max
默认为空代表不限制最大宽度,如果设为正值,则文字最大显示宽度不会超过此值note.align
标注水平对齐,可设置left
,center
或right
note.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
的基础上,水平偏移offsetX
offsetY
将icons
的位置在position
的基础上,垂直偏移offsetY
direction
可取值为west
、east
、north
、south
之一,指定icons
的排列方向keepOrien
连线方向改变时,icons
会自动调整方向以保持最好的阅读效果(比如文字),此属性为true
表示禁止自动调整方向gap
指定`图标之间的距离rotation
指定图标的旋转弧度rotationFixed
该参数使得图标旋转不考虑Edge
连线角度,只根据rotation
决定,默认值为false
width
指定每个图标的宽度height
指定每个图标的高度opacity
透明度,值范围0~1
stretch
图标绘制拉伸方式,默认为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
和centerUniform
group.repeat.image
填充重复背景的图片,注意这里的图片不支持矢量group.position
组合并是的位置,默认值为17
代表居中group.toggleable
决定组是否允许双击进行展开和合并的切换,默认为true
group.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
和right
group.depth
组展开后的边缘的深度,0
代表平面效果,正值代表凸起效果,负值代表凹陷效果,默认值为1
group.background
组展开后的背景颜色 group.gradient
组展开后背景渐近类型,可选值参见shape,group.gradient.color
组展开后背景渐近颜色 group.border.color
组展开后的边框颜色group.border.width
组展开后的边框宽度,group.border.pattern
组展开后的边框显示虚线样式,Array
类型,例如[5, 5]
group.border.cap
组展开后的边框末端线帽的样式,可选参数为butt|round|square
group.border.join
组展开后的边框当两条线交汇时创建边角的类型,可选参数为bevel|round|miter
edge.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
连线宽度,默认值为2
edge.offset
连线端点和图元中心点的距离,默认值为20
edge.group
对于成组的连线,可通过此属性归为不同的组,以达到独立展开合并的效果,默认值为0
edge.gap
成组连线之间的间距,默认值为12
edge.toggleable
决定是否响应双击进行展开合并的切换,默认值为true
edge.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
是否显示虚线,默认值为false
edge.dash.pattern
虚线样式,默认值为[16, 16]
edge.dash.offset
虚线偏移,默认值为0
edge.dash.color
虚线颜色 edge.dash.width
虚线宽度,默认为空代表采用edge.width
值 edge.dash.3d
虚线是否显示3d
效果,默认值为false
edge.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
的设计技巧,开始对这方面技术进行关注和运用。
系统的学习一门技术我们还是推荐阅读书籍,但是写出一本优秀经典的书籍一般需要几个月甚至几年时间, 对于紧跟前沿的程序员还需要一定的途径获取最新的技术资讯,在此强烈推荐三处针对前端技术开发的周刊订阅, 每周只需看看这三封周刊邮件,基本不会错过地球上前端开发领域正在发生的有趣事件: