VectorLayer类
# 概述
本文主要介绍VectorLayer
类和CanvasVectorLayerRenderer
图层渲染器
# 源码剖析
# VectorLayer
类
VectorLayer
就是继承了BaseVectorLayer
类,重写了createRender
方法,返回CanvasVectorLayerRenderer
的实例对象。
class VectorLayer extends BaseVectorLayer {
constructor(options) {
super(options);
}
createRenderer() {
return new CanvasVectorLayerRenderer(this);
}
}
2
3
4
5
6
7
8
# CanvasVectorLayerRenderer
渲染器
VectorLayer
类中createRenderer
方法实际是重写Layer
类(即BaseVectorLayer
的父类)中定义的同名方法,返回一个CanvasVectorLayerRenderer
类的实例对象,该类继承了CanvasLayerRenderer
类。
CanvasVectorLayerRenderer
类的构造函数各变量含义如下面代码片段所示
CanvasVectorLayerRenderer
类的实现如下:
class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
constructor(vectorLayer) {
// 绑定样式图像变化后的处理方法,确保函数的上下文始终指向当前渲染器的实例对象
this.boundHandleStyleImageChange_ = this.handleStyleImageChange_.bind(this);
// 是否正在进行动画或者交互状态
this.animatingOrInteracting_;
// 记录击中检测的图像数据,在进行对象碰撞检测时会用到,初始化为null
this.hitDetectionImageData_ = null;
// 是否已裁剪,初始化为false
this.clipped_ = false;
// 记录已渲染的Features,初始化为null
this.renderedFeatures_ = null;
// 记录已渲染的修订版本号,初始化为-1
this.renderedRevision_ = -1;
// 记录已渲染的分辨率,初始化为NaN
this.renderedResolution_ = NaN;
// 记录已渲染的视图范围,初始化为[Infinity, Infinity, -Infinity, -Infinity]
this.renderedExtent_ = createEmpty();
// 记录包裹的已渲染范围,初始化为[Infinity, Infinity, -Infinity, -Infinity]
this.wrappedRenderedExtent_ = createEmpty();
// 记录已渲染的旋转角度
this.renderedRotation_;
// 记录已渲染的中心,初始值为null
this.renderedCenter_ = null;
// 记录已渲染的像素比率,初始值为1
this.renderedPixelRatio_ = 1;
// 记录已渲染的渲染顺序,初始值为null
this.renderedRenderOrder_ = null;
// 记录已渲染的去重操作
this.renderedFrameDeclutter_;
// 重播组,初始值为null
this.replayGroup_ = null;
// 记录重播组已更改,初始值为true
this.replayGroupChanged = true;
// 是否启用裁剪,初始值为true
this.clipping = true;
// 目标上下文,即canvas 绘图的上下文
this.targetContext_ = null;
// 透明度,初始值为1
this.opacity_ = 1; //
}
renderWorlds(executorGroup, frameState, declutterable) {}
setDrawContext_() {}
resetDrawContext_() {}
renderDeclutter(frameState) {}
renderDeferredInternal() {}
renderFrame(frameState, target) {}
getFeatures(pixel) {}
forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
matches
) {}
handleFontsChanged() {}
handleStyleImageChange_() {}
prepareFrame() {}
renderFeature() {}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# CanvasVectorLayerRenderer
方法解释
renderWorlds
方法
renderWorlds
方法用于在多个世界上渲染矢量数据,即地图在水平轴上可以循环延申。它接受三个参数executorGroup
(负责渲染执行的对象,进行实际渲染的操作),frameState
(帧状态信息),declutterable
(表示是否参与去重,true
需要去重,false
不进行去重处理)
renderWorlds
方法内部就是先获取帧状态的各种信息中心、分辨率、像素比等等,然后调用this.getLayer
获取图层源以及投影坐标系是否支持在水平方向延申,从而确定渲染的起始位置,最后执行一个循环,根据当前视图的世界范围,逐个渲染每个世界.
renderWorlds
方法中的核心部分在循环体,该循环体先是调用LayerRenderer
类(即CanvasLayerRenderer
类的父类)中的getRenderTransform
方法获取渲染转换矩阵,它会根据当前视图的中心、分辨率、旋转角度等信息,计算出当前世界的变换矩阵。然后根据frameState.declutter)
确定变换矩阵,再通过executorGroup.execute
触发实际的渲染操作,这是一个很复杂的过程,在以后会分析到。
renderWorlds(executorGroup, frameState, declutterable) {
const extent = frameState.extent;
const viewState = frameState.viewState;
const center = viewState.center;
const resolution = viewState.resolution;
const projection = viewState.projection;
const rotation = viewState.rotation;
const projectionExtent = projection.getExtent();
const vectorSource = this.getLayer().getSource();
const declutter = this.getLayer().getDeclutter();
const pixelRatio = frameState.pixelRatio;
const viewHints = frameState.viewHints;
const snapToPixel = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
const context = this.context;
const width = Math.round((getWidth(extent) / resolution) * pixelRatio);//渲染区域宽度
const height = Math.round((getHeight(extent) / resolution) * pixelRatio);//渲染区域高度
const multiWorld = vectorSource.getWrapX() && projection.canWrapX();//是否水平方向延申
const worldWidth = multiWorld ? getWidth(projectionExtent) : null; //世界的宽度
const endWorld = multiWorld
? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1
: 1; //计算需要渲染世界的数量
let world = multiWorld
? Math.floor((extent[0] - projectionExtent[0]) / worldWidth)
: 0;//当前视图所在的世界的起始位置(即开始渲染的世界编号)
do {
let transform = this.getRenderTransform(
center,
resolution,
0,
pixelRatio,
width,
height,
world * worldWidth
);
if (frameState.declutter) {
transform = transform.slice(0);
}
executorGroup.execute(
context,
[context.canvas.width, context.canvas.height],
transform,
rotation,
snapToPixel,
declutterable === undefined
? ALL
: declutterable
? DECLUTTER
: NON_DECLUTTER,
declutterable ? declutter && frameState.declutter[declutter] : undefined
);
} while (++world < endWorld);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
setDrawContext_
setDrawContext_
方法目的就是用于设置canvas
的上下文环境,该上下文环境就是调用createCanvasContext2D
方法创建了一个canvas element
,然后返回一个2d context
。
setDrawContext_() {
if (this.opacity_ !== 1) {
this.targetContext_ = this.context;
this.context = createCanvasContext2D(
this.context.canvas.width,
this.context.canvas.height,
canvasPool,
);
}
}
2
3
4
5
6
7
8
9
10
- 和
resetDrawContext_
方法
resetDrawContext_
方法就是重置canvas
的上下文环境或状态,releaseCanvas
就是释放画布资源,清空canvas
上的数据,以便以后复用或回收。
resetDrawContext_() {
if (this.opacity_ !== 1) {
const alpha = this.targetContext_.globalAlpha;
this.targetContext_.globalAlpha = this.opacity_;
this.targetContext_.drawImage(this.context.canvas, 0, 0);
this.targetContext_.globalAlpha = alpha;
releaseCanvas(this.context);
canvasPool.push(this.context.canvas);
this.context = this.targetContext_;
this.targetContext_ = null;
}
}
2
3
4
5
6
7
8
9
10
11
12
renderDeclutter
方法
renderDeclutter
方法就是接受一个参数frameState
帧状态,如果this.replayGroup_
或者图层不需要去重,则返回;否则调用renderWorlds
方法进行帧状态的渲染。
renderDeclutter(frameState) {
if (!this.replayGroup_ || !this.getLayer().getDeclutter()) {
return;
}
this.renderWorlds(this.replayGroup_, frameState, true);
}
2
3
4
5
6
renderDeferredInternal
方法
renderDeferredInternal
方法是内部由 Openlayers 内部调用延迟渲染的方法,其实现如下
renderDeferredInternal(frameState) {
if (!this.replayGroup_) {
return;
}
this.replayGroup_.renderDeferred();
if (this.clipped_) {
this.context.restore();
}
this.resetDrawContext_();
}
2
3
4
5
6
7
8
9
10
若this.replayGroup_
不存在,则返回;否则执行this.replayGroup_.renderDeferred()
方法,然后判断是否已裁剪,若已裁剪,则保存canvas
的上下文;最后调用了resetDrawContext_
重置画布绘制的上下文。
renderFrame
方法
renderFrame
方法是核心方法,用于渲染图层,接受两个参数frameState
帧状态和目标元素target
。
renderFrame
方法先是通过layerIndex
获取layerState
图层状态,以及图层的透明度,然后调用prepareContainer
方法设置this.context.canvas
的transform
属性,然后判断this.replayGroup_
是否存在,若不存在则判断图层是否存在prerender
和postrender
类型的监听事件,若不存在,则返回;若存在,则设置画布的上下文,然后调用this.preRender
进行预渲染