Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 框架

  • core模块

  • dom模块

  • control

  • geometry

  • geo

  • layer

    • marker

    • tile

    • vector

      • Renderer
        • 概述
        • 源码分析
          • 源码实现
          • 源码解析
          • 类结构与继承
          • 构造函数
          • DOM容器生命周期
          • 事件绑定与处理
          • 坐标变换与动画
          • 边界更新与重绘
          • 子类实现关键点
          • 设计思想总结
          • 渲染流程
        • 总结
      • Canvas
      • Path
      • Polyline
      • Polygon
      • Rectangle
      • Render.getRenderer
      • SVG
      • SVG.Util
      • SVG.VML
      • CircleMarker
      • Circle
    • Layer
    • LayerGroup
    • FeatureGroup
    • DivOverlay
    • Popup
    • Tooltip
    • ImageOverlay
    • SVGOverlay
    • VideoOverlay
    • GeoJSON
  • Map

  • 《Leaflet源码》笔记
  • layer
  • vector
东流
2025-04-15
目录

Renderer

# 概述

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();
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

# 源码解析

# 类结构与继承

  • 继承自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):核心变换逻辑:

    1. 计算缩放比例scale:根据当前缩放级别和目标缩放级别,计算出缩放比例scale。
    1. 计算视口半长viewHalf:根据当前地图视口大小和padding选项,计算出视口的一半长度viewHalf。
    1. 计算中心点像素坐标currentCenterPoint:将地图中心点转换为像素坐标currentCenterPoint。
    1. 计算容器偏移量topLeftOffset:
    • 反向偏移:根据缩放比例和视口半长,计算出容器需要反向偏移的距离。
    • 加上当前中心点:加上当前中心点的像素坐标。
    • 减去新的像素原点:减去新的像素原点,以确保容器的位置正确。
    1. 应用变换兼容性处理:
    • 对于支持 3D 变换的浏览器,使用DomUtil.setTransform方法应用变换,包括平移和缩放。
    • 对于不支持 3D 变换的浏览器,使用DomUtil.setPosition方法应用平移。

# 边界更新与重绘

_update:更新渲染器的边界和状态

    1. 计算边界bounds:根据当前地图视口大小和padding选项,计算出渲染器的边界bounds。
    1. 更新中心点和缩放级别:更新渲染器的中心点和缩放级别。

_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)提供通用逻辑,确保矢量图形在地图交互中正确渲染和更新。

编辑 (opens new window)
上次更新: 2025/05/12, 07:40:30
TileLayer.WMS
Canvas

← TileLayer.WMS Canvas→

最近更新
01
GeoJSON
05-08
02
Circle
04-15
03
CircleMarker
04-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式