图形管理类(Graph)
1 Graph类概述
图形管理类是 AnyGraph
图形开发引擎核心组件之一,是一个创建和管理图形的容器类。提供了以下几部分功能:
- 图层管理
- 图形渲染
- 图形交互操作
- 图形数据管理
- 控件和事件管理
2 Graph类的设计
(1) 类的关系
图形管理相关的类包括:Graph类、Layer类、View类、GraphRenderer类,其中Graph类是这几个类的核心,负责构建另外几个类,并提供外部访问api接口;GraphRenderer类是负责图形的渲染、Layer类负责图层属性、图层数据和图层的渲染、View类负责图形的视点控制。这几个类的关系如下:
classDiagram Graph --> GraphRenderer Graph --> View Graph *-- Layer class Graph{ name:String layers:Array addLayer(layer) removeLayer(layer) render() setView(view) getCoordinateFromPixel(pixel) getPixelFromCoordinate(coordinate) } class Layer{ source: Source renderer: LayerRenderer setStyle(style) getVisible() visibleAtResolution() setOffset(x, y) } class GraphRenderer{ mainCanvas:Canvas getSize() prepareFrame() composeBuffer(frameState) renderFrame() filter() } class View{ center: [float,float] resolutions:float zoom: int fill() calculateCenterZoom() getState() }
(2) 图层管理
在图形系统中,图层(Layers)是一种用于组织和管理图像内容的办法。图层可以将不同的图像元素分开,使得它们可以独立地进行绘制、编辑和操作。每个图层都可以包含一个或多个图像对象,这些对象可以是几何对象、文本、图像等。图层可以互相叠加,并且可以通过控制透明度、颜色和大小等属性来创建各种视觉效果。
你可以在同一个画板上绘制所有你需要的内容,也可以有选择性地将不同类别的元素分散于不同的画板上;例如在下图的场景中,将静态的地理背景与动态的人或动物分离,以及将地理背景中的草地、湖泊、树木置于不同的图层中,便能够在需要时实现精准修改与控制。
通过对图层的分层管理,继而控制图层的可见性,在绘图时快速隐藏暂时用不到的图层,在画布上仅显示需要的图层,更有利于排除绘图过程中的重叠元素与干扰项,让制图工作更简单和便捷。
(3) 图形渲染思路
在 AnyGraph
中图形渲染的核心思路是各个图层分别对应独立的Canvas画布,在画布中渲染各自图层中的数据,最后按图层顺序将各图层合并为完整的图形。
在各图层渲染后,将其合并到图形的时候可采用 ctx.drawImage()
或 ctx.putImageData()
两种方式。第一种方式渲染效率会更高,第二种方式可对像素进行处理,实现更为灵活的渲染效果。
1 | 1. ctx.drawImage() |
(4) 图形渲染过程
AnyGraph
的图形渲染过程如下:
graph LR A1-->B1 A2-->B1 A3-->B1 A4-->B1 B1 -.- E1 B1 --- C1 B1 --- C2 B1 --- C3 B1 -.- E2 C2-->D1 A2(API:立即刷新) A1(API:异步刷新) A3(API:重绘图形) A4(API:重绘图层) B1(渲染主程序) C1(计算矩阵) C2(合成图形) C3(渲染图形) E1(事件:RenderBefore) E2(事件:RenderAfter) D1(逐个图层合成)
上图可以清晰看出图形渲染过程中,最重要的是三个步骤:
-
计算矩阵:在该过程中根据图形的视点范围和当前显示的viewPort计算矩阵变换参数,为图形合成时的坐标转换做好准备。
-
合成图形:逐个图层的将图层中的几何对象渲染至图层Canvas中。
-
渲染图形:将各个图层Canvas合并至图形的Canvas中,并呈现出来。
Graph
类提供了四个发起图形渲染的API,分别为立即刷新、异步刷新、刷新图层。
立即刷新
顾名思义,立即刷新就是通知主线程立即开始图形渲染,立即刷新主要应用在当前视点范围内的数据发生变化后执行。
异步刷新
异步刷新是利用HTML中Window对象的RequestAnimalFrame特性,在窗口下一次刷新的时候执行图形渲染。异步刷新应用场景非常广泛,在图形窗口属性发生了改变后,在图层属性发生了变换后,在图形对象属性发生了变换后,都可以调用该方法,通知浏览器在下一次刷新的时候重新渲染图形,这么做的好处是,不论调用了多少次异步刷新命令,浏览器仅会在下一次窗口刷新时调用一次图形渲染命令。
重绘图层
重绘图层指仅仅重新绘制某一层,其他层仍使用上一次渲染后留下的缓存图像,从而在大数据量的场景中提高图像渲染效率,例如在图形的浮动层绘制橡皮线或拉框的场景中,仅仅只是重新绘制橡皮线或拉框,而不用重新绘制图形内容本身,从而提供了渲染效率。
重绘图形
重绘图形是指根据新的参数重新绘制整个图形,发出这个命令后,AnyGraph
将会重新获取各个图层中指定视点的数据,重新计算矩阵,重新进行坐标转换,重新将这些数据绘制至各个图层中,最后由GraphRenderer
将图形合成为图形并显示出来。
(5) 图形事件
图形事件分为两种情况,一种情况是指图形在渲染过程中触发的事件,既有图形相关事件,也有图形相关事件, AnyGraph
1.0触发的事件类型包括以下事件:
EventType.RenderBefore
: 图形渲染之前EventType.RenderAfter
: 图形渲染之后EventType.ComposeBefore
: 图层渲染之前EventType.ComposeAfter
: 图层渲染之后EventType.LayerModified
: 图层属性发生变化之后EventType.Loader
: 图层数据装载之后
另一种事件类型是指对Canvas操作的键盘、鼠标、触摸、系统等类型事件,目前可支持的事件包括:
- wheel
- click
- dblclick
- mouseUp
- mouseDown
- mouseMove
- mouseout
- mouseenter
- mouseover
- keydown
- touchstart
- touchmove
- touchend
3 Graph类的使用
类名为:Graph
,位于src
根目录。
Graph类是 AnyGraph
最核心的类,是图形处理的唯一入口,所有的图形渲染均通过该类产生。
(1) 初始化
1 | constructor(options) |
该类的构造函数接受一个 Object
类型的参数,其值包括:
名称 | 类型 | 说明 |
---|---|---|
target | String | HTML页面容器ID |
layers | Array |
图层集合 |
mouse | Boolean | 是否允许鼠标交互操作,缺省值为true |
defaultFullView | Boolean | 初始化后是否显示全图,缺省值为false |
enabledGeomEvent | Boolean | 是否允许对象事件,缺省值为false |
originAtLeftTop | Boolean | 图形坐标系是否为卡迪尔坐标系,缺省值为true |
view | View | 图形显示选项 |
bgColor | ColorString | 背景颜色 |
说明:
- target: 该参数为必填值,指的是
HTML
页面中的容器对象,AnyGraph
将在此容器对象中创建Canvas对象和其他辅助对象。在初始化前需先建立该容器对象,并指定容器宽和高属性; - layers: 图层列表,该属性为可选,在初始化之后可通过
addLayer()
增加图层; - mouse: 缺省值为true,图形对象构建后将具有通过鼠标进行缩放和漫游的功能;
- defaultFullView: 缺省值为false,图形数据加载之后优先根据
view
属性显示图形指定位置,如果没有指定view
属性,则判断该属性的值,如果该属性的值为true
则显示全图,否则显示Canvas画布坐标内的图形; - originAtLeftTop:缺省值为true,图形坐标系是否为卡迪尔坐标系;
- view: 图形显示选项, 图形数据加载之后优先根据该属性显示指定的位置;
- bgColor:图形背景颜色,默认为透明色;
- enabledGeomEvent:是否允许对象事件,缺省值为false;当该值为true时,图形中的每个
Geometry
对象均可提供各类鼠标事件,当数据量较大时可能会影响系统性能,因此建议在大数据量的情况下将该值设置为false;
HTML
在初始化时需在HTML中建立一个<div>
容器,并指定该容器的大小,HTML
如下所示:
1 | <body style="overflow: hidden; margin:0px;"> |
创建图形实例
在HTML
中的<script>
中通过以下js程序初始化图形对象,如下所示:
1 | let graph = new Graph({ |
创建图形实例后 AnyGraph
将会在上述HTML
页面的graphWrapper
容器内生成Canvas对象,该对象用于显示图形结果,如下图所示:
该容器中不仅仅包含生成的Canvas对象,还会包含一系列控件,这个可将根据需要通过graph提供的API添加和移除。
下面这段代码在初始化图形时,建立一个图层,并设置了图层的属性和数据文件的Url
,还指定显示图形的视点范围。
1 | <script type="module"> |
该代码执行后的效果如下所示:
(2) 图层管理
在Graph类中提供了以下几个与图层管理相关的API:
名称 | 说明 |
---|---|
addLayer(layer) | 增加图层 |
removeLayer(layer) | 移除图层 |
removeLayers() | 移除所有图层 |
getLayer(id) | 获取指定图层 |
getLayers() | 获取所有图层 |
queryGeomList(coord) | 查询图形中“包含”该坐标位置的点 |
removeGeom(geom) | 移除某个图层中的Geometry对象 |
新建图层
新建图层可采用下面两种方式:
- 方式一:
1 | let layer = graph.addLayer({"name":"背景层"}); |
- 方式二:
1 | let layer = new Layer({"name":"背景层"}); |
获取图层
- 获取指定图层
1 | let layer = graph.getLayer(layerId) |
- 获取所有图层
1 | let layerArray = graph.getLayers() |
删除图层
- 删除指定图层
1 | graph.removeLayer(layer); |
- 删除所有图层
1 | graph.removeLayers(); |
(3) 图形渲染
Graph类中通过GraphRenderer
调用Canvas类的API进行图形绘制。
Graph类提供了以下几个与图形渲染相关的API:
名称 | 说明 |
---|---|
render() | 异步图形渲染 |
renderSync() | 同步图形渲染 |
renderLayer(layer) | 重绘指定图层 |
showExtent(extent) | 设置图形的视点范围,并重绘图形 |
setView(view) | 设置中心点和密度,并重绘图形 |
图形刷新
图形刷新是指不改变图形显示的范围而重新渲染图形, AnyGraph
提供了三种方式刷新图形:
- 同步刷新当前图形
例如在绘制图形后,继续执行某些代码时可使用该方式。
1 | graph.renderSync() |
- 异步刷新当前图形
该方法通过 requestAnimationFrame()
调用刚刚介绍的renderSync()
方法绘制图形,因此仅会在下次屏幕刷新时绘制图形。使用该方法不用担心多次调用 render()
会造成重复绘制图形的问题。
1 | graph.render() |
- 立即刷新指定图层。
该方法仅会刷新指定图层,如果存在其他图层,则其他图层会使用上一次绘制的结果。适合于当数据量非常大的时候,可以减少其他图层的绘制时间,从而提高图形的渲染效率。
1 | graph.renderLayer(layer) |
显示图形指定位置
AnyGraph
还提供了两个根据指定位置显示图形的api。
- 按范围显示图形
1 | graph.showExtent(extent) |
- 按中心点和分辨率进行图形显示
1 | let view = graph.getView(); |
(4) 图形信息
名称 | 说明 |
---|---|
getFrameState() | 获取图形信息 |
getExtent() | 获取当前图形显示范围 |
getSize() | 获取图形的宽度和高度 |
getRenderObject() | 渲染画板对象 |
setBgColor(color) | 设置图形的背景颜色 |
getBgColor() | 获取背景颜色 |
getRenderer() | 获取渲染器 |
getView() | 返回当前视图 |
(5) 坐标转换
名称 | 说明 |
---|---|
getCoordinateFromPixel(pixel) | 像素坐标转图形坐标 |
getPixelFromCoordinate(coordinate) | 图形坐标转像素坐标 |
使用这两个方法可以将屏幕坐标转换为图形坐标,或者将图形坐标转换为屏幕坐标,示例如下:
1 | // 将屏幕坐标转换为图形坐标 |
需注意的是这里的图形坐标指的是当前打开图形的坐标。