概述
Renderer
是一个抽象类,作为所有矢量渲染器(如SVG和Canvas)的基类,用于定义渲染器的基本行为。它继承自 Layer
类,提供了一些通用的方法和属性,如处理渲染器的DOM容器管理、坐标变换和地图事件响应。
源码分析
源码实现
Renderer
渲染器的源码实现如下:
export var Renderer = Layer.extend({
options: {
padding: 0.1, //控制渲染区域比地图视口多出的比例,避免边缘图形被裁剪。例如地图视口为100px,地图默认渲染区域为120px=100px *(1+0.1*2)
},
initialize: function (options) {
Util.setOptions(this, options); // 合并选项
Util.stamp(this); // 添加唯一标识符
this._layers = this._layers || {}; // 存储所有子图层
},
onAdd: function () {
if (!this._container) {
this._initContainer(); // 子类实现,创建SVG/Canvas元素
DomUtil.addClass(this._container, "leaflet-zoom-animated");
}
this.getPane().appendChild(this._container);
this._update(); // 初始化边界和变换
this.on("update", this._updatePaths, this);
},
onRemove: function () {
this.off("update", this._updatePaths, this);
},
getEvents: function () {
var events = {
viewreset: this._reset,
zoom: this._onZoom,
moveend: this._update,
zoomend: this._onZoomEnd,
};
if (this._zoomAnimated) {
events.zoomanim = this._onAnimZoom; // 处理动画缩放
}
return events;
},
_onAnimZoom: function (ev) {
this._updateTransform(ev.center, ev.zoom);
},
_onZoom: function () {
this._updateTransform(this._map.getCenter(), this._map.getZoom());
},
_updateTransform: function (center, zoom) {
// 计算缩放比例
var scale = this._map.getZoomScale(zoom, this._zoom),
// 计算视口半长,含(padding)
viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
// 当前中心点在地图坐标系中的像素坐标
currentCenterPoint = this._map.project(this._center, zoom),
// 计算容器偏移量
topLeftOffset = viewHalf
.multiplyBy(-scale) // 反向偏移
.add(currentCenterPoint) // 加上当前中心点
.subtract(this._map._getNewPixelOrigin(center, zoom));
// 应用变换,更新容器的位置和缩放
if (Browser.any3d) {
DomUtil.setTransform(this._container, topLeftOffset, scale);
} else {
DomUtil.setPosition(this._container, topLeftOffset);
}
},
_reset: function () {
this._update(); // 更新边界
this._updateTransform(this._center, this._zoom); // 重新定位
// 重置所有子图层
for (var id in this._layers) {
this._layers[id]._reset();
}
},
_onZoomEnd: function () {
for (var id in this._layers) {
this._layers[id]._project();
}
},
_updatePath: function () {
for (var id in this._layers) {
this._layers[id]._update(); // 重绘路径
}
},
_update: function () {
var p = this.options.padding,
size = this._map.getSize(),
min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
this._center = this._map.getCenter();
this._zoom = this._map.getZoom();
},
});
源码解析
类结构与继承
- 继承自
Layer
Renderer
继承自 Layer
类,这表明它是一个图层类的子类,可被添加到地图中。所有矢量图形(如Polyline
、Polygon
)默认使用渲染器绘制。
- 抽象基类
Renderer
类不能被实例化使用,通常是作为具体渲染器(如SVG和Canvas)的基类,提供了通用的渲染方法和属性,而具体的渲染逻辑由子类实现。
构造函数
initialize
方法用于初始化渲染器,合并传入的选项,并添加唯一标识符。
DOM容器生命周期
1.onAdd()
:添加到地图时
-
容器创建
- 子类实现的
_initContainer()
方法:创建渲染器的DOM容器,如SVG或Canvas元素。
- 子类实现的
-
CSS 类
leaflet-zoom-animated
- 容器添加了
leaflet-zoom-animated
类,用于启用缩放动画效果。
- 容器添加了
-
事件绑定
- 绑定了
update
事件,当渲染器需要更新时触发_updatePaths
方法。
- 绑定了
2.onRemove()
:移除时销毁
事件绑定与处理
1.getEvents()
:监听地图事件
- 关键事件:
viewreset
:地图视图重置时(如地图投影或范围变化)触发_reset
方法,重置渲染器zoom/moveend
:地图缩放或移动结束时触发_update
方法,更新渲染器状态(位置和缩放)zoomanim
:动画缩放时触发_onAnimZoom
方法,处理缩放动画效果
坐标变换与动画
_updateTransform(center,zoom)
:核心变换逻辑:
-
- 计算缩放比例
scale
:根据当前缩放级别和目标缩放级别,计算出缩放比例scale
。
- 计算缩放比例
-
- 计算视口半长
viewHalf
:根据当前地图视口大小和padding
选项,计算出视口的一半长度viewHalf
。
- 计算视口半长
-
- 计算中心点像素坐标
currentCenterPoint
:将地图中心点转换为像素坐标currentCenterPoint
。
- 计算中心点像素坐标
-
- 计算容器偏移量
topLeftOffset
:
- 反向偏移:根据缩放比例和视口半长,计算出容器需要反向偏移的距离。
- 加上当前中心点:加上当前中心点的像素坐标。
- 减去新的像素原点:减去新的像素原点,以确保容器的位置正确。
- 计算容器偏移量
-
- 应用变换兼容性处理:
- 对于支持 3D 变换的浏览器,使用
DomUtil.setTransform
方法应用变换,包括平移和缩放。 - 对于不支持 3D 变换的浏览器,使用
DomUtil.setPosition
方法应用平移。
边界更新与重绘
_update
:更新渲染器的边界和状态
-
- 计算边界
bounds
:根据当前地图视口大小和padding
选项,计算出渲染器的边界bounds
。
- 计算边界
-
- 更新中心点和缩放级别:更新渲染器的中心点和缩放级别。
_reset
:重置渲染器
- 子图层重置:调用每个路径的
_reset()
,例如重新计算投影
_updatePaths()
:更新所有路径
- 触发时机:在
update
事件触发时调用(如地图移动或缩放后),用于重绘所有路径。
子类实现关键点
子类必须实现的方法:
-
_initContainer()
:
创建DOM元素(SVG或Canvas’),设置其样式和尺寸 -
_updatePath(layer)
:
具体如何绘制或更新单个路径
设计思想总结
-
分层抽象
Renderer
处理通用逻辑(事件、变换、容器管理)- 子类专注具体渲染技术(SVG或Canvas)
-
性能优化
- 批量更新:通过
_updatePaths
方法,一次性更新所有路径,减少重绘次数 - CSS 变换:利用硬件加速实现平滑缩放动画
- 离屏渲染:通过
padding
扩展渲染区域,减少频繁重绘
- 批量更新:通过
-
浏览器兼容
- 通过
Browser
模块检测特性,降级处理不支持3D变换的浏览器
- 通过
渲染流程
1. 地图初始化
添加 Renderer
实例到地图,触发 onAdd
,创建容器并绑定事件。
2. 用户拖拽地图
触发 moveend
,调用 _update()
更新 _bounds
和中心点。
3. 缩放动画进行中
触发 zoomanim
,调用 _onAnimZoom
,实时更新容器变换。
4. 缩放结束
触发 zoomend
,调用 _onZoomEnd
,重新投影所有路径。
5. 路径更新
某个 Polyline
修改坐标,触发 _update()
,重绘对应路径
总结
Renderer
是Leaflet 矢量渲染的核心基类,通过统一管理容器变换和地图事件,为不同渲染技术(SVG/Canvas)提供通用逻辑,确保矢量图形在地图交互中正确渲染和更新。