openlayers源码阅读笔记(二)—— 地图渲染器ol.CompositeMapRenderer
上一节中讲到地图渲染是调用了渲染器的renderFrame方法,这一节我们来看地图渲染器CompositeMapRenderer类,直译过来就是复合地图渲染器。
1. 创建div.ol-layers元素
构造函数很简单,就是创建了div.ol-layers,并插入到viewport中。
/**
* @classdesc
* Canvas map renderer.
* @api
*/
class CompositeMapRenderer extends MapRenderer {
/**
* @param {import("../PluggableMap.js").default} map Map.
*/
constructor(map) {
super(map);
/**
* @type {import("../events.js").EventsKey}
*/
this.fontChangeListenerKey_ = listen(
checkedFonts,
ObjectEventType.PROPERTYCHANGE,
map.redrawText.bind(map)
);
/**
* @private
* @type {HTMLDivElement}
*/
this.element_ = document.createElement('div');
const style = this.element_.style;
style.position = 'absolute';
style.width = '100%';
style.height = '100%';
style.zIndex = '0';
this.element_.className = CLASS_UNSELECTABLE + ' ol-layers';
const container = map.getViewport();
container.insertBefore(this.element_, container.firstChild || null);
/**
* @private
* @type {Array<HTMLElement>}
*/
this.children_ = [];
/**
* @private
* @type {boolean}
*/
this.renderedVisible_ = true;
}
......
}
2. CompositeMapRenderer.renderFrame方法
renderFrame(frameState) {
if (!frameState) {
if (this.renderedVisible_) {
this.element_.style.display = 'none';
this.renderedVisible_ = false;
}
return;
}
this.calculateMatrices2D(frameState);
this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState);
.......
}
renderFrame接收一个framestate参数,他会先检测framestate是否为真值,如果不是,则取消渲染。渲染之前他会先调用调用this.calculateMatrices2D方法和派发一个PRECOMPOSES事件。this.calculateMatrices2D方法是根据地图的尺寸、view的分辨率、旋转角、中心纵坐标来计算framestate中的坐标和像素单位相互转换的矩阵coordinateToPixelTransform和pixelToCoordinateTransform。
renderFrame(frameState) {
.......
const layerStatesArray = frameState.layerStatesArray.sort(function (a, b) {
return a.zIndex - b.zIndex;
});
const viewState = frameState.viewState;
......
}
通过layer的zIndex给图层排序,这决定了图层的渲染顺序和我们看到的屏幕上的图层的上下层级,最后渲染的图层会出现在地图最顶层。
renderFrame(frameState) {
.......
this.children_.length = 0;
......
}
this.children_是div.ol-layers的子元素,也就是显示图层的canvas元素,这一句将div.ol-layers的子元素清空。
renderFrame(frameState) {
.......
let previousElement = null;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
frameState.layerIndex = i;
if (
!inView(layerState, viewState) ||
(layerState.sourceState != SourceState.READY &&
layerState.sourceState != SourceState.UNDEFINED)
) {
continue;
}
const layer = layerState.layer;
const element = layer.render(frameState, previousElement);
if (!element) {
continue;
}
if (element !== previousElement) {
this.children_.push(element);
previousElement = element;
}
......
}
遍历图层状态数组,若数据源状态sourceState不为READY或UNDEFINED则不渲染此图层。
调用layer.render方法,该方法接收两个参数framestate和一个HTML元素,并且返回一个HTML元素。该HTML元素就是将呈现在页面上的地图canvas元素,每一个layer将本图层需要呈现的像素渲染到上一个layer(若有)渲染之后的canvas上,最终会得到一个我们想要呈现到页面上的canvas。如果我们在new Layer()的时候给图层设置了className属性,则该图层会新建一个canvas,而不是在previousElement上继续渲染。最后this.children_储存的就是所有需要添加到页面呈现的canvas元素。
renderFrame(frameState) {
.......
replaceChildren(this.element_, this.children_);
this.dispatchRenderEvent(RenderEventType.POSTCOMPOSE, frameState);
if (!this.renderedVisible_) {
this.element_.style.display = '';
this.renderedVisible_ = true;
}
this.scheduleExpireIconCache(frameState);
}
最后调用window.replaceChildren将this.element_也就是div.ol-layers的子元素替换成this.children_中的canvas元素。
3. 总结
ol.CompositeMapRenderer类在构造函数中创建了存放图层canvas的节点div.ol-layers;在地图渲染时会按顺序调用图层的render方法;最后将所有需要呈现图层渲染好的canvas元素插入到div.ol-layers中。
下一节分享ol.layer及其render函数。