上一篇中,主要理清了图层Layer的渲染方式, 其中,以IntermediateCanvas的图片渲染方式主要采用context.drawImage()的方式实现,而VectorLayer的渲染则不同,本文将进一步说明。
在后续的版本中,该部分做了调整,当整体上的思路基本一致。
文章目录
导图
开始前,请掌握矢量数据的特性、类型、结构等相关知识。
一、ol.renderer.canvas.VectorLayer
接着上一章的论述,在composeFrame() 、 prepareFrame()渲染中实现
1、composeFrame
/**
* @inheritDoc
*/
ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) {
var replayGroup = this.replayGroup_;
replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids);
};
2、prepareFrame
ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, layerState) {
......
// 初始化ReplayGroup
var replayGroup = new ol.render.canvas.ReplayGroup(
ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution,
pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, vectorLayer.getRenderBuffer());
/**
* @param {ol.Feature} feature Feature.
* @this {ol.renderer.canvas.VectorLayer}
*/
var renderFeature = function(feature) {
......
var dirty = this.renderFeature(
feature, resolution, pixelRatio, styles, replayGroup);
......
renderFeature(features[i]);
......
// 执行完成
replayGroup.finish();
......
};
ol.renderer.canvas.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) {
if (!styles) {
return false;
}
var loading = false;
if (Array.isArray(styles)) {
for (var i = 0, ii = styles.length; i < ii; ++i) {
loading = ol.renderer.vector.renderFeature(
replayGroup, feature, styles[i],
ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
this.handleStyleImageChange_, this) || loading;
}
} else {
loading = ol.renderer.vector.renderFeature(
replayGroup, feature, styles,
ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
this.handleStyleImageChange_, this);
}
return loading;
};
二、ol.renderer.vector
/**
* @param {ol.render.ReplayGroup} replayGroup Replay group.
* @param {ol.Feature|ol.render.Feature} feature Feature.
* @param {ol.style.Style} style Style.
* @param {number} squaredTolerance Squared tolerance.
* @param {function(this: T, ol.events.Event)} listener Listener function.
* @param {T} thisArg Value to use as `this` when executing `listener`.
* @return {boolean} `true` if style is loading.
* @template T
*/
ol.renderer.vector.renderFeature = function(
replayGroup, feature, style, squaredTolerance, listener, thisArg) {
var loading = false;
var imageStyle, imageState;
imageStyle = style.getImage();
if (imageStyle) {
imageState = imageStyle.getImageState();
if (imageState == ol.ImageState.LOADED ||
imageState == ol.ImageState.ERROR) {
imageStyle.unlistenImageChange(listener, thisArg);
} else {
if (imageState == ol.ImageState.IDLE) {
imageStyle.load();
}
imageState = imageStyle.getImageState();
imageStyle.listenImageChange(listener, thisArg);
loading = true;
}
}
ol.renderer.vector.renderFeature_(replayGroup, feature, style,
squaredTolerance);
return loading;
};
/**
* @param {ol.render.ReplayGroup} replayGroup Replay group.
* @param {ol.Feature|ol.render.Feature} feature Feature.
* @param {ol.style.Style} style Style.
* @param {number} squaredTolerance Squared tolerance.
* @private
*/
ol.renderer.vector.renderFeature_ = function(
replayGroup, feature, style, squaredTolerance) {
var geometry = style.getGeometryFunction()(feature);
if (!geometry) {
return;
}
var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
var renderer = style.getRenderer();
if (renderer) {
ol.renderer.vector.renderGeometry_(replayGroup, simplifiedGeometry, style, feature);
} else {
var geometryRenderer =
ol.renderer.vector.GEOMETRY_RENDERERS_[simplifiedGeometry.getType()];
geometryRenderer(replayGroup, simplifiedGeometry, style, feature);
}
};
/**
* @const
* @private
* @type {Object.<ol.geom.GeometryType,
* function(ol.render.ReplayGroup, ol.geom.Geometry,
* ol.style.Style, Object)>}
*/
ol.renderer.vector.GEOMETRY_RENDERERS_ = {
'Point': ol.renderer.vector.renderPointGeometry_,
'LineString': ol.renderer.vector.renderLineStringGeometry_,
'Polygon': ol.renderer.vector.renderPolygonGeometry_,
'MultiPoint': ol.renderer.vector.renderMultiPointGeometry_,
'MultiLineString': ol.renderer.vector.renderMultiLineStringGeometry_,
'MultiPolygon': ol.renderer.vector.renderMultiPolygonGeometry_,
'GeometryCollection': ol.renderer.vector.renderGeometryCollectionGeometry_,
'Circle': ol.renderer.vector.renderCircleGeometry_
};
三、ol.render.canvas.ReplayGroup
1、replay
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {ol.Transform} transform Transform.
* @param {number} viewRotation View rotation.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
* to skip.
* @param {Array.<ol.render.ReplayType>=} opt_replayTypes Ordered replay types
* to replay. Default is {@link ol.render.replay.ORDER}
* @param {Object.<string, ol.DeclutterGroup>=} opt_declutterReplays Declutter
* replays.
*/
ol.render.canvas.ReplayGroup.prototype.replay = function(context,
transform, viewRotation, skippedFeaturesHash, opt_replayTypes, opt_declutterReplays) {
/** @type {Array.<number>} */
var zs = Object.keys(this.replaysByZIndex_).map(Number);
zs.sort(ol.array.numberSafeCompareFunction);
// setup clipping so that the parts of over-simplified geometries are not
// visible outside the current extent when panning
context.save();
this.clip(context, transform);
var replayTypes = opt_replayTypes ? opt_replayTypes : ol.render.replay.ORDER;
var i, ii, j, jj, replays, replay;
for (i = 0, ii = zs.length; i < ii; ++i) {
var zIndexKey = zs[i].toString();
replays = this.replaysByZIndex_[zIndexKey];
for (j = 0, jj = replayTypes.length; j < jj; ++j) {
var replayType = replayTypes[j];
replay = replays[replayType];
if (replay !== undefined) {
if (opt_declutterReplays &&
(replayType == ol.render.ReplayType.IMAGE || replayType == ol.render.ReplayType.TEXT)) {
var declutter = opt_declutterReplays[zIndexKey];
if (!declutter) {
opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)];
} else {
declutter.push(replay, transform.slice(0));
}
} else {
replay.replay(context, transform, viewRotation, skippedFeaturesHash);
}
}
}
}
context.restore();
};
2、finish
/**
* FIXME empty description for jsdoc
*/
ol.render.canvas.ReplayGroup.prototype.finish = function() {
var zKey;
for (zKey in this.replaysByZIndex_) {
var replays = this.replaysByZIndex_[zKey];
var replayKey;
for (replayKey in replays) {
replays[replayKey].finish();
// replays[replayKey].finish() 实际上调用的是PolygonReplay等的finsih()
}
}
};
/**
* @inheritDoc
*/
ol.render.canvas.ReplayGroup.prototype.getReplay = function(zIndex, replayType) {
var zIndexKey = zIndex !== undefined ? zIndex.toString() : '0';
var replays = this.replaysByZIndex_[zIndexKey];
if (replays === undefined) {
replays = {
};
this.replaysByZIndex_[zIndexKey] = replays;
}
var replay = replays[replayType];
if (replay === undefined) {
var Constructor = ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_[replayType];
replay = new Constructor(this.tolerance_, this.maxExtent_,
this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_);
replays[replayType] = replay;
}
return replay;
};
/**
* @const
* @private
* @type {Object.<ol.render.ReplayType,
* function(new: ol.render.canvas.Replay, number, ol.Extent,
* number, number, boolean, Array.<ol.DeclutterGroup>)>}
*/
ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_ = {
'Circle': ol.render.canvas.PolygonReplay,
'Default': ol.render.canvas.Replay,
'Image': ol.render.canvas.ImageReplay,
'LineString': ol.render.canvas.LineStringReplay,
'Polygon': ol.render.canvas.PolygonReplay,
'Text': ol.render.canvas.TextReplay
};
四、ol.render.canvas.Replay
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {ol.Transform} transform Transform.
* @param {number} viewRotation View rotation.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
* to skip.
*/
ol.render.canvas.Replay.prototype.replay = function(
context, transform, viewRotation, skippedFeaturesHash) {
this.viewRotation_ = viewRotation;
this.replay_(context, transform,
skippedFeaturesHash, this.instructions, undefined, undefined);
};
/**
* @private
* @param {CanvasRenderingContext2D} context Context.
* @param {ol.Transform} transform Transform.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
* to skip.
* @param {Array.<*>} instructions Instructions array.
* @param {function((ol.Feature|ol.render.Feature)): T|undefined}
* featureCallback Feature callback.
* @param {ol.Extent=} opt_hitExtent Only check features that intersect this
* extent.
* @return {T|undefined} Callback result.
* @template T
*/
ol.render.canvas.Replay.prototype.replay_ = function(
context, transform, skippedFeaturesHash,
instructions, featureCallback, opt_hitExtent) {
// When the batch size gets too big, performance decreases. 200 is a good
// balance between batch size and number of fill/stroke instructions.
var batchSize =
this.instructions != instructions || this.overlaps ? 0 : 200;
while (i < ii) {
var instruction = instructions[i];
var type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]);
var /** @type {ol.Feature|ol.render.Feature} */ feature, x, y;
switch (type) {
case ol.render.canvas.Instruction.BEGIN_GEOMETRY:
break;
case ol.render.canvas.Instruction.BEGIN_PATH:
break;
case ol.render.canvas.Instruction.CIRCLE:
break;
case ol.render.canvas.Instruction.CLOSE_PATH:
break;
case ol.render.canvas.Instruction.CUSTOM:
break;
case ol.render.canvas.Instruction.DRAW_IMAGE:
break;
case ol.render.canvas.Instruction.DRAW_CHARS:
break;
case ol.render.canvas.Instruction.END_GEOMETRY:
break;
case ol.render.canvas.Instruction.FILL:
break;
case ol.render.canvas.Instruction.MOVE_TO_LINE_TO:
break;
case ol.render.canvas.Instruction.SET_FILL_STYLE
break;
case ol.render.canvas.Instruction.SET_STROKE_STYLE:
break;
case ol.render.canvas.Instruction.STROKE:
break;
default:
break;
}
}
if (pendingFill) {
this.fill_(context);
}
if (pendingStroke) {
context.stroke();
}
return undefined;
};