记录一下查看openlayers矢量绘图原理的查看过程,
用这个例子切入,https://openlayers.org/en/latest/examples/feature-animation.html
环境:windows chrome nodejs webstorm
1.下载openlayer 源码,版本5.1.3,webstrom中打开
2.查看源码
feature-animation.js
function animate(event) {
const vectorContext = event.vectorContext;
const frameState = event.frameState;
const flashGeom = feature.getGeometry().clone();
const elapsed = frameState.time - start;
const elapsedRatio = elapsed / duration;
// radius will be 5 at start and 30 at end.
const radius = easeOut(elapsedRatio) * 25 + 5;
const opacity = easeOut(1 - elapsedRatio);
const style = new Style({
image: new CircleStyle({
radius: radius,
snapToPixel: false,
stroke: new Stroke({
color: 'rgba(255, 0, 0, ' + opacity + ')',
width: 0.25 + opacity
})
})
});
vectorContext.setStyle(style);
vectorContext.drawGeometry(flashGeom);//跟踪代码
if (elapsed > duration) {
unByKey(listenerKey);
return;
}
// tell OpenLayers to continue postcompose animation
map.render();
}
Immediate.js
drawGeometry(geometry) {
const type = geometry.getType();
switch (type) {
case GeometryType.POINT:
this.drawPoint(/** @type {module:ol/geom/Point} */ (geometry));
break;
case GeometryType.LINE_STRING:
this.drawLineString(/** @type {module:ol/geom/LineString} */ (geometry));
break;
case GeometryType.POLYGON:
this.drawPolygon(/** @type {module:ol/geom/Polygon} */ (geometry));
break;
case GeometryType.MULTI_POINT:
this.drawMultiPoint(/** @type {module:ol/geom/MultiPoint} */ (geometry));
break;
case GeometryType.MULTI_LINE_STRING:
this.drawMultiLineString(/** @type {module:ol/geom/MultiLineString} */ (geometry));
break;
case GeometryType.MULTI_POLYGON:
this.drawMultiPolygon(/** @type {module:ol/geom/MultiPolygon} */ (geometry));
break;
case GeometryType.GEOMETRY_COLLECTION:
this.drawGeometryCollection(/** @type {module:ol/geom/GeometryCollection} */ (geometry));
break;
case GeometryType.CIRCLE:
this.drawCircle(/** @type {module:ol/geom/Circle} */ (geometry));
break;
default:
}
}
任意挑选一种要素类型,这里选择线
drawLineString(geometry) {
if (!intersects(this.extent_, geometry.getExtent())) {
return;
}
if (this.strokeState_) {
this.setContextStrokeState_(this.strokeState_);
const context = this.context_;
const flatCoordinates = geometry.getFlatCoordinates();
context.beginPath();
this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,
geometry.getStride(), false);
context.stroke();
}
if (this.text_ !== '') {
const flatMidpoint = geometry.getFlatMidpoint();
this.drawText_(flatMidpoint, 0, 2, 2);
}
}
这里重点来了,this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,geometry.getStride(), false);
moveToLineTo_(flatCoordinates, offset, end, stride, close) {
const context = this.context_;
const pixelCoordinates = transform2D(
flatCoordinates, offset, end, stride, this.transform_,
this.pixelCoordinates_);
context.moveTo(pixelCoordinates[0], pixelCoordinates[1]);
let length = pixelCoordinates.length;
if (close) {
length -= 2;
}
for (let i = 2; i < length; i += 2) {
context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]);
}
if (close) {
context.closePath();
}
return end;
}
重点
transform2D(flatCoordinates, offset, end, stride, this.transform_, this.pixelCoordinates_);
其中的重点this.transform_,变换矩阵,这个变换矩阵是CanvasImmediateRenderer的构造参数,图层渲染器layerrender,canvaslayerrender,都会给它传入这个参数,默认的变换
export function create() { return [1, 0, 0, 1, 0, 0]; }
在完成绘图准备,分发事件的时候,会重新计算一个transform,getTransform,
dispatchComposeEvent_(type, context, frameState, opt_transform) {
const layer = this.getLayer();
if (layer.hasListener(type)) {
const width = frameState.size[0] * frameState.pixelRatio;
const height = frameState.size[1] * frameState.pixelRatio;
const rotation = frameState.viewState.rotation;
rotateAtOffset(context, -rotation, width / 2, height / 2);
const transform = opt_transform !== undefined ?
opt_transform : this.getTransform(frameState, 0);
const render = new CanvasImmediateRenderer(
context, frameState.pixelRatio, frameState.extent, transform,
frameState.viewState.rotation);
const composeEvent = new RenderEvent(type, render, frameState,
context, null);
layer.dispatchEvent(composeEvent);
rotateAtOffset(context, rotation, width / 2, height / 2);
}
}
getTransform
getTransform(frameState, offsetX) {
const viewState = frameState.viewState;
const pixelRatio = frameState.pixelRatio;
const dx1 = pixelRatio * frameState.size[0] / 2;
const dy1 = pixelRatio * frameState.size[1] / 2;
const sx = pixelRatio / viewState.resolution;
const sy = -sx;
const angle = -viewState.rotation;
const dx2 = -viewState.center[0] + offsetX;
const dy2 = -viewState.center[1];
return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2);
}
可以看到这个矩阵的构成
export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
const sin = Math.sin(angle);
const cos = Math.cos(angle);
transform[0] = sx * cos;
transform[1] = sy * sin;
transform[2] = -sx * sin;
transform[3] = sy * cos;
transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;
transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;
return transform;
}
经过这个矩阵变换,才会完成feature的平面坐标-》css坐标?-》屏幕坐标的变换,最终绘制到屏幕上
resolution的设置、交互过程中ol如何确定resolution也很重要,另说。
还有一些关节没打通,还需学习