我是靠谱客的博主 听话鼠标,最近开发中收集的这篇文章主要介绍Openlayers源码阅读(七):图层canvas渲染,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

上一篇中,ol.renderer.canvas.Map在renderFrame()中通过getLayerRenderer()初始化ol.renderer.canvas.Layer,并调用了ol.renderer.canvas.Layer的prepareFrame()、composeFrame方法。

文章目录

    • 导图
    • 一、ol.renderer.canvas.Layer
    • 二、ol.renderer.canvas.IntermediateCanvas
    • 三、ol.renderer.canvas.ImageLayer
    • 四、ol.renderer.canvas.TileLayer
    • 五、 ol.renderer.canvas.VectorLayer
    • 六、 ol.renderer.canvas.VectorTileLayer

导图

extends
extends
extends
extends
extends
ImageLayer
IntermediateCanvas
TileLayer
VectorTileLayer
Layer
VectorLayer

说明: 命名空间 ol.renderer.canvas

一、ol.renderer.canvas.Layer

ol.renderer.canvas.Layer 提供抽象类和抽象方法

/**
* @constructor
* @abstract
* @extends {ol.renderer.Layer}
* @param {ol.layer.Layer} layer Layer.
*/
ol.renderer.canvas.Layer = function(layer) {
ol.renderer.Layer.call(this, layer);
/**
* @protected
* @type {number}
*/
this.renderedResolution;
/**
* @private
* @type {ol.Transform}
*/
this.transform_ = ol.transform.create();
};
ol.inherits(ol.renderer.canvas.Layer, ol.renderer.Layer);
/**
* @abstract
* @param {olx.FrameState} frameState Frame state.
* @param {ol.LayerState} layerState Layer state.
* @param {CanvasRenderingContext2D} context Context.
*/
ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerState, context) {};
/**
* @abstract
* @param {olx.FrameState} frameState Frame state.
* @param {ol.LayerState} layerState Layer state.
* @return {boolean} whether composeFrame should be called.
*/
ol.renderer.canvas.Layer.prototype.prepareFrame = function(frameState, layerState) {};
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {olx.FrameState} frameState Frame state.
* @param {ol.LayerState} layerState Layer state.
* @param {ol.Transform=} opt_transform Transform.
* @protected
*/
ol.renderer.canvas.Layer.prototype.postCompose = function(context, frameState, layerState, opt_transform) {
this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, context,
frameState, opt_transform);
};
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {olx.FrameState} frameState Frame state.
* @param {ol.Transform=} opt_transform Transform.
* @protected
*/
ol.renderer.canvas.Layer.prototype.preCompose = function(context, frameState, opt_transform) {
this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, context,
frameState, opt_transform);
};
/**
* @param {ol.render.EventType} type Event type.
* @param {CanvasRenderingContext2D} context Context.
* @param {olx.FrameState} frameState Frame state.
* @param {ol.Transform=} opt_transform Transform.
* @private
*/
ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) {
var layer = this.getLayer();
if (layer.hasListener(type)) {
var width = frameState.size[0] * frameState.pixelRatio;
var height = frameState.size[1] * frameState.pixelRatio;
var rotation = frameState.viewState.rotation;
ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2);
var transform = opt_transform !== undefined ?
opt_transform : this.getTransform(frameState, 0);
var render = new ol.render.canvas.Immediate(
context, frameState.pixelRatio, frameState.extent, transform,
frameState.viewState.rotation);
var composeEvent = new ol.render.Event(type, render, frameState,
context, null);
layer.dispatchEvent(composeEvent);
ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2);
}
};

二、ol.renderer.canvas.IntermediateCanvas

实现了父类的composeFrame()的方法:

1、获取Image

2、对Image进行处理:范围裁剪、图像转换、透明度设置

3、context.drawImage()

ol.renderer.canvas.IntermediateCanvas.prototype.composeFrame = function(frameState, layerState, context) {
this.preCompose(context, frameState);
var image = this.getImage();
if (image) {
// clipped rendering if layer extent is set
var extent = layerState.extent;
var clipped = extent !== undefined &&
!ol.extent.containsExtent(extent, frameState.extent) &&
ol.extent.intersects(extent, frameState.extent);
if (clipped) {
this.clip(context, frameState, /** @type {ol.Extent} */ (extent));
}
var imageTransform = this.getImageTransform();
// for performance reasons, context.save / context.restore is not used
// to save and restore the transformation matrix and the opacity.
// see http://jsperf.com/context-save-restore-versus-variable
var alpha = context.globalAlpha;
context.globalAlpha = layerState.opacity;
// for performance reasons, context.setTransform is only used
// when the view is rotated. see http://jsperf.com/canvas-transform
var dx = imageTransform[4];
var dy = imageTransform[5];
var dw = image.width * imageTransform[0];
var dh = image.height * imageTransform[3];
context.drawImage(image, 0, 0, +image.width, +image.height,
Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
context.globalAlpha = alpha;
if (clipped) {
context.restore();
}
}
this.postCompose(context, frameState, layerState);
};

三、ol.renderer.canvas.ImageLayer

实现了父类的prepareFrame()的方法

ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, layerState) {
var pixelRatio = frameState.pixelRatio;
var size = frameState.size;
var viewState = frameState.viewState;
var viewCenter = viewState.center;
var viewResolution = viewState.resolution;
var image;
var imageLayer = /** @type {ol.layer.Image} */ (this.getLayer());
var imageSource = imageLayer.getSource();
var hints = frameState.viewHints;
var renderedExtent = frameState.extent;
if (layerState.extent !== undefined) {
renderedExtent = ol.extent.getIntersection(
renderedExtent, layerState.extent);
}
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
!ol.extent.isEmpty(renderedExtent)) {
var projection = viewState.projection;
if (!ol.ENABLE_RASTER_REPROJECTION) {
var sourceProjection = imageSource.getProjection();
if (sourceProjection) {
projection = sourceProjection;
}
}
var vectorRenderer = this.vectorRenderer_;
if (vectorRenderer) {
var context = vectorRenderer.context;
var imageFrameState = /** @type {olx.FrameState} */ (ol.obj.assign({}, frameState, {
size: [
ol.extent.getWidth(renderedExtent) / viewResolution,
ol.extent.getHeight(renderedExtent) / viewResolution
],
viewState: /** @type {olx.ViewState} */ (ol.obj.assign({}, frameState.viewState, {
rotation: 0
}))
}));
var skippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort();
if (vectorRenderer.prepareFrame(imageFrameState, layerState) &&
(vectorRenderer.replayGroupChanged ||
!ol.array.equals(skippedFeatures, this.skippedFeatures_))) {
context.canvas.width = imageFrameState.size[0] * pixelRatio;
context.canvas.height = imageFrameState.size[1] * pixelRatio;
vectorRenderer.composeFrame(imageFrameState, layerState, context);
this.image_ = new ol.ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas);
this.skippedFeatures_ = skippedFeatures;
}
} else {
image = imageSource.getImage(
renderedExtent, viewResolution, pixelRatio, projection);
if (image) {
var loaded = this.loadImage(image);
if (loaded) {
this.image_ = image;
}
}
}
}
if (this.image_) {
image = this.image_;
var imageExtent = image.getExtent();
var imageResolution = image.getResolution();
var imagePixelRatio = image.getPixelRatio();
var scale = pixelRatio * imageResolution /
(viewResolution * imagePixelRatio);
var transform = ol.transform.compose(this.imageTransform_,
pixelRatio * size[0] / 2, pixelRatio * size[1] / 2,
scale, scale,
0,
imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution,
imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution);
ol.transform.compose(this.coordinateToCanvasPixelTransform,
pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5],
pixelRatio / viewResolution, -pixelRatio / viewResolution,
0,
-viewCenter[0], -viewCenter[1]);
this.updateLogos(frameState, imageSource);
this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio;
}
return !!this.image_;
};

四、ol.renderer.canvas.TileLayer

实现了父类的prepareFrame()的方法

ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layerState) {
var pixelRatio = frameState.pixelRatio;
var size = frameState.size;
var viewState = frameState.viewState;
var projection = viewState.projection;
var viewResolution = viewState.resolution;
var viewCenter = viewState.center;
var tileLayer = this.getLayer();
var tileSource = /** @type {ol.source.Tile} */ (tileLayer.getSource());
var sourceRevision = tileSource.getRevision();
var tileGrid = tileSource.getTileGridForProjection(projection);
var z = tileGrid.getZForResolution(viewResolution, this.zDirection);
var tileResolution = tileGrid.getResolution(z);
var oversampling = Math.round(viewResolution / tileResolution) || 1;
var extent = frameState.extent;
if (layerState.extent !== undefined) {
extent = ol.extent.getIntersection(extent, layerState.extent);
}
if (ol.extent.isEmpty(extent)) {
// Return false to prevent the rendering of the layer.
return false;
}
var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
var imageExtent = tileGrid.getTileRangeExtent(z, tileRange);
var tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio);
/**
* @type {Object.<number, Object.<string, ol.Tile>>}
*/
var tilesToDrawByZ = {};
tilesToDrawByZ[z] = {};
var findLoadedTiles = this.createLoadedTileFinder(
tileSource, projection, tilesToDrawByZ);
var tmpExtent = this.tmpExtent;
var tmpTileRange = this.tmpTileRange_;
var newTiles = false;
var tile, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tile = tileSource.getTile(z, x, y, pixelRatio, projection);
if (tile.getState() == ol.TileState.ERROR) {
if (!tileLayer.getUseInterimTilesOnError()) {
// When useInterimTilesOnError is false, we consider the error tile as loaded.
tile.setState(ol.TileState.LOADED);
} else if (tileLayer.getPreload() > 0) {
// Preloaded tiles for lower resolutions might have finished loading.
newTiles = true;
}
}
if (!this.isDrawableTile_(tile)) {
tile = tile.getInterimTile();
}
if (this.isDrawableTile_(tile)) {
var uid = ol.getUid(this);
if (tile.getState() == ol.TileState.LOADED) {
tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
var inTransition = tile.inTransition(uid);
if (!newTiles && (inTransition || this.renderedTiles.indexOf(tile) === -1)) {
newTiles = true;
}
}
if (tile.getAlpha(uid, frameState.time) === 1) {
// don't look for alt tiles if alpha is 1
continue;
}
}
var childTileRange = tileGrid.getTileCoordChildTileRange(
tile.tileCoord, tmpTileRange, tmpExtent);
var covered = false;
if (childTileRange) {
covered = findLoadedTiles(z + 1, childTileRange);
}
if (!covered) {
tileGrid.forEachTileCoordParentTileRange(
tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
}
}
}
var renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling;
var hints = frameState.viewHints;
var animatingOrInteracting = hints[ol.ViewHint.ANIMATING] || hints[ol.ViewHint.INTERACTING];
if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && (
newTiles ||
!(this.renderedExtent_ && ol.extent.containsExtent(this.renderedExtent_, extent)) ||
this.renderedRevision != sourceRevision ||
oversampling != this.oversampling_ ||
!animatingOrInteracting && renderedResolution != this.renderedResolution
)) {
var context = this.context;
if (context) {
var tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection);
var width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling);
var height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling);
var canvas = context.canvas;
if (canvas.width != width || canvas.height != height) {
this.oversampling_ = oversampling;
canvas.width = width;
canvas.height = height;
} else {
if (this.renderedExtent_ && !ol.extent.equals(imageExtent, this.renderedExtent_)) {
context.clearRect(0, 0, width, height);
}
oversampling = this.oversampling_;
}
}
this.renderedTiles.length = 0;
/** @type {Array.<number>} */
var zs = Object.keys(tilesToDrawByZ).map(Number);
zs.sort(function(a, b) {
if (a === z) {
return 1;
} else if (b === z) {
return -1;
} else {
return a > b ? 1 : a < b ? -1 : 0;
}
});
var currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii;
var tileExtent, tileGutter, tilesToDraw, w, h;
for (i = 0, ii = zs.length; i < ii; ++i) {
currentZ = zs[i];
currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
currentResolution = tileGrid.getResolution(currentZ);
currentScale = currentResolution / tileResolution;
tileGutter = tilePixelRatio * tileSource.getGutter(projection);
tilesToDraw = tilesToDrawByZ[currentZ];
for (var tileCoordKey in tilesToDraw) {
tile = tilesToDraw[tileCoordKey];
tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent);
x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio / oversampling;
y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling;
w = currentTilePixelSize[0] * currentScale / oversampling;
h = currentTilePixelSize[1] * currentScale / oversampling;
this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ);
this.renderedTiles.push(tile);
}
}
this.renderedRevision = sourceRevision;
this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling;
this.renderedExtent_ = imageExtent;
}
var scale = this.renderedResolution / viewResolution;
var transform = ol.transform.compose(this.imageTransform_,
pixelRatio * size[0] / 2, pixelRatio * size[1] / 2,
scale, scale,
0,
(this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio,
(viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio);
ol.transform.compose(this.coordinateToCanvasPixelTransform,
pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5],
pixelRatio / viewResolution, -pixelRatio / viewResolution,
0,
-viewCenter[0], -viewCenter[1]);
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
projection, extent, z, tileLayer.getPreload());
this.scheduleExpireCache(frameState, tileSource);
this.updateLogos(frameState, tileSource);
return this.renderedTiles.length > 0;
};

drawTileImage() 根据x,y绘制tile

/**
* @param {ol.Tile} tile Tile.
* @param {olx.FrameState} frameState Frame state.
* @param {ol.LayerState} layerState Layer state.
* @param {number} x Left of the tile.
* @param {number} y Top of the tile.
* @param {number} w Width of the tile.
* @param {number} h Height of the tile.
* @param {number} gutter Tile gutter.
* @param {boolean} transition Apply an alpha transition.
*/
ol.renderer.canvas.TileLayer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter, transition) {
var image = tile.getImage(this.getLayer());
if (!image) {
return;
}
var uid = ol.getUid(this);
var alpha = transition ? tile.getAlpha(uid, frameState.time) : 1;
if (alpha === 1 && !this.getLayer().getSource().getOpaque(frameState.viewState.projection)) {
this.context.clearRect(x, y, w, h);
}
var alphaChanged = alpha !== this.context.globalAlpha;
if (alphaChanged) {
this.context.save();
this.context.globalAlpha = alpha;
}
this.context.drawImage(image, gutter, gutter,
image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h);
if (alphaChanged) {
this.context.restore();
}
if (alpha !== 1) {
frameState.animate = true;
} else if (transition) {
tile.endTransition(uid);
}
};

五、 ol.renderer.canvas.VectorLayer

实现父类的composeFrame()、prepareFrame()方法

ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) {
var extent = frameState.extent;
var pixelRatio = frameState.pixelRatio;
var skippedFeatureUids = layerState.managed ?
frameState.skippedFeatureUids : {};
var viewState = frameState.viewState;
var projection = viewState.projection;
var rotation = viewState.rotation;
var projectionExtent = projection.getExtent();
var vectorSource = /** @type {ol.source.Vector} */ (this.getLayer().getSource());
var transform = this.getTransform(frameState, 0);
this.preCompose(context, frameState, transform);
// clipped rendering if layer extent is set
var clipExtent = layerState.extent;
var clipped = clipExtent !== undefined;
if (clipped) {
this.clip(context, frameState,
/** @type {ol.Extent} */ (clipExtent));
}
var replayGroup = this.replayGroup_;
if (replayGroup && !replayGroup.isEmpty()) {
if (this.declutterTree_) {
this.declutterTree_.clear();
}
var layer = /** @type {ol.layer.Vector} */ (this.getLayer());
var drawOffsetX = 0;
var drawOffsetY = 0;
var replayContext;
var transparentLayer = layerState.opacity !== 1;
var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER);
if (transparentLayer || hasRenderListeners) {
var drawWidth = context.canvas.width;
var drawHeight = context.canvas.height;
if (rotation) {
var drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight));
drawOffsetX = (drawSize - drawWidth) / 2;
drawOffsetY = (drawSize - drawHeight) / 2;
drawWidth = drawHeight = drawSize;
}
// resize and clear
this.context.canvas.width = drawWidth;
this.context.canvas.height = drawHeight;
replayContext = this.context;
} else {
replayContext = context;
}
var alpha = replayContext.globalAlpha;
if (!transparentLayer) {
// for performance reasons, context.save / context.restore is not used
// to save and restore the transformation matrix and the opacity.
// see http://jsperf.com/context-save-restore-versus-variable
replayContext.globalAlpha = layerState.opacity;
}
if (replayContext != context) {
replayContext.translate(drawOffsetX, drawOffsetY);
}
var width = frameState.size[0] * pixelRatio;
var height = frameState.size[1] * pixelRatio;
ol.render.canvas.rotateAtOffset(replayContext, -rotation,
width / 2, height / 2);
replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids);
if (vectorSource.getWrapX() && projection.canWrapX() &&
!ol.extent.containsExtent(projectionExtent, extent)) {
var startX = extent[0];
var worldWidth = ol.extent.getWidth(projectionExtent);
var world = 0;
var offsetX;
while (startX < projectionExtent[0]) {
--world;
offsetX = worldWidth * world;
transform = this.getTransform(frameState, offsetX);
replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids);
startX += worldWidth;
}
world = 0;
startX = extent[2];
while (startX > projectionExtent[2]) {
++world;
offsetX = worldWidth * world;
transform = this.getTransform(frameState, offsetX);
replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids);
startX -= worldWidth;
}
// restore original transform for render and compose events
transform = this.getTransform(frameState, 0);
}
ol.render.canvas.rotateAtOffset(replayContext, rotation,
width / 2, height / 2);
if (replayContext != context) {
if (hasRenderListeners) {
this.dispatchRenderEvent(replayContext, frameState, transform);
}
if (transparentLayer) {
var mainContextAlpha = context.globalAlpha;
context.globalAlpha = layerState.opacity;
context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY);
context.globalAlpha = mainContextAlpha;
} else {
context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY);
}
replayContext.translate(-drawOffsetX, -drawOffsetY);
}
if (!transparentLayer) {
replayContext.globalAlpha = alpha;
}
}
if (clipped) {
context.restore();
}
this.postCompose(context, frameState, layerState, transform);
};
ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, layerState) {
var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
var vectorSource = vectorLayer.getSource();
this.updateLogos(frameState, vectorSource);
var animating = frameState.viewHints[ol.ViewHint.ANIMATING];
var interacting = frameState.viewHints[ol.ViewHint.INTERACTING];
var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating();
var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting();
if (!this.dirty_ && (!updateWhileAnimating && animating) ||
(!updateWhileInteracting && interacting)) {
return true;
}
var frameStateExtent = frameState.extent;
var viewState = frameState.viewState;
var projection = viewState.projection;
var resolution = viewState.resolution;
var pixelRatio = frameState.pixelRatio;
var vectorLayerRevision = vectorLayer.getRevision();
var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
if (vectorLayerRenderOrder === undefined) {
vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
}
var extent = ol.extent.buffer(frameStateExtent,
vectorLayerRenderBuffer * resolution);
var projectionExtent = viewState.projection.getExtent();
if (vectorSource.getWrapX() && viewState.projection.canWrapX() &&
!ol.extent.containsExtent(projectionExtent, frameState.extent)) {
// For the replay group, we need an extent that intersects the real world
// (-180° to +180°). To support geometries in a coordinate range from -540°
// to +540°, we add at least 1 world width on each side of the projection
// extent. If the viewport is wider than the world, we need to add half of
// the viewport width to make sure we cover the whole viewport.
var worldWidth = ol.extent.getWidth(projectionExtent);
var buffer = Math.max(ol.extent.getWidth(extent) / 2, worldWidth);
extent[0] = projectionExtent[0] - buffer;
extent[2] = projectionExtent[2] + buffer;
}
if (!this.dirty_ &&
this.renderedResolution_ == resolution &&
this.renderedRevision_ == vectorLayerRevision &&
this.renderedRenderOrder_ == vectorLayerRenderOrder &&
ol.extent.containsExtent(this.renderedExtent_, extent)) {
this.replayGroupChanged = false;
return true;
}
this.replayGroup_ = null;
this.dirty_ = false;
var replayGroup = new ol.render.canvas.ReplayGroup(
ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution,
pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, vectorLayer.getRenderBuffer());
vectorSource.loadFeatures(extent, resolution, projection);
/**
* @param {ol.Feature} feature Feature.
* @this {ol.renderer.canvas.VectorLayer}
*/
var renderFeature = function(feature) {
var styles;
var styleFunction = feature.getStyleFunction();
if (styleFunction) {
styles = styleFunction.call(feature, resolution);
} else {
styleFunction = vectorLayer.getStyleFunction();
if (styleFunction) {
styles = styleFunction(feature, resolution);
}
}
if (styles) {
var dirty = this.renderFeature(
feature, resolution, pixelRatio, styles, replayGroup);
this.dirty_ = this.dirty_ || dirty;
}
}.bind(this);
if (vectorLayerRenderOrder) {
/** @type {Array.<ol.Feature>} */
var features = [];
vectorSource.forEachFeatureInExtent(extent,
/**
* @param {ol.Feature} feature Feature.
*/
function(feature) {
features.push(feature);
}, this);
features.sort(vectorLayerRenderOrder);
for (var i = 0, ii = features.length; i < ii; ++i) {
renderFeature(features[i]);
}
} else {
vectorSource.forEachFeatureInExtent(extent, renderFeature, this);
}
replayGroup.finish();
this.renderedResolution_ = resolution;
this.renderedRevision_ = vectorLayerRevision;
this.renderedRenderOrder_ = vectorLayerRenderOrder;
this.renderedExtent_ = extent;
this.replayGroup_ = replayGroup;
this.replayGroupChanged = true;
return true;
};

renderFeature() 渲染要素

/**
* @param {ol.Feature} feature Feature.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
*
styles.
* @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
* @return {boolean} `true` if an image is loading.
*/
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.canvas.VectorTileLayer

VectorTileLayer的渲染方式是TileLayer和VectorLayer两种方式的结合。

/**
* @inheritDoc
*/
ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) {
var layer = this.getLayer();
var layerRevision = layer.getRevision();
if (this.renderedLayerRevision_ != layerRevision) {
this.renderedTiles.length = 0;
var renderMode = layer.getRenderMode();
if (!this.context && renderMode != ol.layer.VectorTileRenderType.VECTOR) {
this.context = ol.dom.createCanvasContext2D();
}
if (this.context && renderMode == ol.layer.VectorTileRenderType.VECTOR) {
this.context = null;
}
}
this.renderedLayerRevision_ = layerRevision;
return ol.renderer.canvas.TileLayer.prototype.prepareFrame.apply(this, arguments);
};
/**
* @param {ol.VectorImageTile} tile Tile.
* @param {olx.FrameState} frameState Frame state.
* @private
*/
ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function(
tile, frameState) {
var layer = this.getLayer();
var pixelRatio = frameState.pixelRatio;
var projection = frameState.viewState.projection;
var revision = layer.getRevision();
var renderOrder = /** @type {ol.RenderOrderFunction} */
(layer.getRenderOrder()) || null;
var replayState = tile.getReplayState(layer);
if (!replayState.dirty && replayState.renderedRevision == revision &&
replayState.renderedRenderOrder == renderOrder) {
return;
}
var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
var sourceTileGrid = source.getTileGrid();
var tileGrid = source.getTileGridForProjection(projection);
var resolution = tileGrid.getResolution(tile.tileCoord[0]);
var tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
var zIndexKeys = {};
for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) {
var sourceTile = tile.getTile(tile.tileKeys[t]);
if (sourceTile.getState() == ol.TileState.ERROR) {
continue;
}
var sourceTileCoord = sourceTile.tileCoord;
var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
var sharedExtent = ol.extent.getIntersection(tileExtent, sourceTileExtent);
var bufferedExtent = ol.extent.equals(sourceTileExtent, sharedExtent) ? null :
ol.extent.buffer(sharedExtent, layer.getRenderBuffer() * resolution);
var tileProjection = sourceTile.getProjection();
var reproject = false;
if (!ol.proj.equivalent(projection, tileProjection)) {
reproject = true;
sourceTile.setProjection(projection);
}
replayState.dirty = false;
var replayGroup = new ol.render.canvas.ReplayGroup(0, sharedExtent, resolution,
pixelRatio, source.getOverlaps(), this.declutterTree_, layer.getRenderBuffer());
var squaredTolerance = ol.renderer.vector.getSquaredTolerance(
resolution, pixelRatio);
/**
* @param {ol.Feature|ol.render.Feature} feature Feature.
* @this {ol.renderer.canvas.VectorTileLayer}
*/
var renderFeature = function(feature) {
var styles;
var styleFunction = feature.getStyleFunction();
if (styleFunction) {
styles = styleFunction.call(/** @type {ol.Feature} */ (feature), resolution);
} else {
styleFunction = layer.getStyleFunction();
if (styleFunction) {
styles = styleFunction(feature, resolution);
}
}
if (styles) {
var dirty = this.renderFeature(feature, squaredTolerance, styles,
replayGroup);
this.dirty_ = this.dirty_ || dirty;
replayState.dirty = replayState.dirty || dirty;
}
};
var features = sourceTile.getFeatures();
if (renderOrder && renderOrder !== replayState.renderedRenderOrder) {
features.sort(renderOrder);
}
var feature;
for (var i = 0, ii = features.length; i < ii; ++i) {
feature = features[i];
if (reproject) {
if (tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS) {
// projected tile extent
tileProjection.setWorldExtent(sourceTileExtent);
// tile extent in tile pixel space
tileProjection.setExtent(sourceTile.getExtent());
}
feature.getGeometry().transform(tileProjection, projection);
}
if (!bufferedExtent || ol.extent.intersects(bufferedExtent, feature.getGeometry().getExtent())) {
renderFeature.call(this, feature);
}
}
replayGroup.finish();
for (var r in replayGroup.getReplays()) {
zIndexKeys[r] = true;
}
sourceTile.setReplayGroup(layer, tile.tileCoord.toString(), replayGroup);
}
replayState.renderedRevision = revision;
replayState.renderedRenderOrder = renderOrder;
};
/**
* @inheritDoc
*/
ol.renderer.canvas.VectorTileLayer.prototype.drawTileImage = function(
tile, frameState, layerState, x, y, w, h, gutter, transition) {
var vectorImageTile = /** @type {ol.VectorImageTile} */ (tile);
this.createReplayGroup_(vectorImageTile, frameState);
if (this.context) {
this.renderTileImage_(vectorImageTile, frameState, layerState);
ol.renderer.canvas.TileLayer.prototype.drawTileImage.apply(this, arguments);
}
};
/**
* @inheritDoc
*/
ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, frameState, layerState) {
var layer = this.getLayer();
var declutterReplays = layer.getDeclutter() ? {} : null;
var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
var renderMode = layer.getRenderMode();
var replayTypes = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode];
var pixelRatio = frameState.pixelRatio;
var rotation = frameState.viewState.rotation;
var size = frameState.size;
var offsetX, offsetY;
if (rotation) {
offsetX = Math.round(pixelRatio * size[0] / 2);
offsetY = Math.round(pixelRatio * size[1] / 2);
ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY);
}
if (declutterReplays) {
this.declutterTree_.clear();
}
var tiles = this.renderedTiles;
var tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
var clips = [];
var zs = [];
for (var i = tiles.length - 1; i >= 0; --i) {
var tile = /** @type {ol.VectorImageTile} */ (tiles[i]);
if (tile.getState() == ol.TileState.ABORT) {
continue;
}
var tileCoord = tile.tileCoord;
var worldOffset = tileGrid.getTileCoordExtent(tileCoord)[0] -
tileGrid.getTileCoordExtent(tile.wrappedTileCoord)[0];
var transform = undefined;
for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) {
var sourceTile = tile.getTile(tile.tileKeys[t]);
if (sourceTile.getState() == ol.TileState.ERROR) {
continue;
}
var replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString());
if (renderMode != ol.layer.VectorTileRenderType.VECTOR && !replayGroup.hasReplays(replayTypes)) {
continue;
}
if (!transform) {
transform = this.getTransform(frameState, worldOffset);
}
var currentZ = sourceTile.tileCoord[0];
var currentClip = replayGroup.getClipCoords(transform);
context.save();
context.globalAlpha = layerState.opacity;
// Create a clip mask for regions in this low resolution tile that are
// already filled by a higher resolution tile
for (var j = 0, jj = clips.length; j < jj; ++j) {
var clip = clips[j];
if (currentZ < zs[j]) {
context.beginPath();
// counter-clockwise (outer ring) for current tile
context.moveTo(currentClip[0], currentClip[1]);
context.lineTo(currentClip[2], currentClip[3]);
context.lineTo(currentClip[4], currentClip[5]);
context.lineTo(currentClip[6], currentClip[7]);
// clockwise (inner ring) for higher resolution tile
context.moveTo(clip[6], clip[7]);
context.lineTo(clip[4], clip[5]);
context.lineTo(clip[2], clip[3]);
context.lineTo(clip[0], clip[1]);
context.clip();
}
}
replayGroup.replay(context, transform, rotation, {}, replayTypes, declutterReplays);
context.restore();
clips.push(currentClip);
zs.push(currentZ);
}
}
if (declutterReplays) {
ol.render.canvas.ReplayGroup.replayDeclutter(declutterReplays, context, rotation);
}
if (rotation) {
ol.render.canvas.rotateAtOffset(context, rotation,
/** @type {number} */ (offsetX), /** @type {number} */ (offsetY));
}
ol.renderer.canvas.TileLayer.prototype.postCompose.apply(this, arguments);
};
/**
* @param {ol.Feature|ol.render.Feature} feature Feature.
* @param {number} squaredTolerance Squared tolerance.
* @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
*
styles.
* @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
* @return {boolean} `true` if an image is loading.
*/
ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, squaredTolerance, 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], squaredTolerance,
this.handleStyleImageChange_, this) || loading;
}
} else {
loading = ol.renderer.vector.renderFeature(
replayGroup, feature, styles, squaredTolerance,
this.handleStyleImageChange_, this);
}
return loading;
};
/**
* @param {ol.VectorImageTile} tile Tile.
* @param {olx.FrameState} frameState Frame state.
* @param {ol.LayerState} layerState Layer state.
* @private
*/
ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function(
tile, frameState, layerState) {
var layer = this.getLayer();
var replayState = tile.getReplayState(layer);
var revision = layer.getRevision();
var replays = ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS[layer.getRenderMode()];
if (replays && replayState.renderedTileRevision !== revision) {
replayState.renderedTileRevision = revision;
var tileCoord = tile.wrappedTileCoord;
var z = tileCoord[0];
var pixelRatio = frameState.pixelRatio;
var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
var tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
var resolution = tileGrid.getResolution(z);
var context = tile.getContext(layer);
var size = source.getTilePixelSize(z, pixelRatio, frameState.viewState.projection);
context.canvas.width = size[0];
context.canvas.height = size[1];
var tileExtent = tileGrid.getTileCoordExtent(tileCoord);
for (var i = 0, ii = tile.tileKeys.length; i < ii; ++i) {
var sourceTile = tile.getTile(tile.tileKeys[i]);
if (sourceTile.getState() == ol.TileState.ERROR) {
continue;
}
var pixelScale = pixelRatio / resolution;
var transform = ol.transform.reset(this.tmpTransform_);
ol.transform.scale(transform, pixelScale, -pixelScale);
ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]);
var replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString());
replayGroup.replay(context, transform, 0, {}, replays);
}
}
};

最后

以上就是听话鼠标为你收集整理的Openlayers源码阅读(七):图层canvas渲染的全部内容,希望文章能够帮你解决Openlayers源码阅读(七):图层canvas渲染所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部