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)
  • 核心基类

  • Control控件篇

  • Geom几何图形篇

  • Layer图层篇

  • Renderer篇

  • Feature篇

  • style样式篇

    • Style类
    • Fill类
    • Stroke类
    • Text类
    • ImageStyle类
    • Icon类
      • 概述
      • 源码分析
        • Icon类源码实现
        • Icon类的构造函数
        • Icon类的主要方法
      • 总结
    • RegularShape类
    • Circle类
    • IconImage类
    • IconImageCache类
  • 《Openlayers源码》笔记
  • style样式篇
东流
2025-01-20
目录

Icon类

# 概述

在 Openlayers 中,Icon类用于创建图标,通常用于在地图上显示标记或图像元素。这个类可以通过设置图标的各种属性(如大小、图像源、锚点等)来控制图标的外观和位置。

Icon类继承于ImageStyle类,关于ImageStyle类,可以参考这篇文章。

# 源码分析

# Icon类源码实现

Icon类源码实现如下:

class Icon extends ImageStyle {
  constructor(options) {
    options = options || {};
    // 透明度
    const opacity = options.opacity !== undefined ? options.opacity : 1;
    // 旋转角度
    const rotation = options.rotation !== undefined ? options.rotation : 0;
    // 缩放大小
    const scale = options.scale !== undefined ? options.scale : 1;
    //是否跟随地图旋转
    const rotateWithView =
      options.rotateWithView !== undefined ? options.rotateWithView : false;

    super({
      opacity: opacity,
      rotation: rotation,
      scale: scale,
      displacement:
        options.displacement !== undefined ? options.displacement : [0, 0],
      rotateWithView: rotateWithView,
      declutterMode: options.declutterMode,
    });

    //锚点相关
    //图标相对锚点的位置
    this.anchor_ = options.anchor !== undefined ? options.anchor : [0.5, 0.5];
    this.normalizedAnchor_ = null;
    //锚点的原点
    this.anchorOrigin_ =
      options.anchorOrigin !== undefined ? options.anchorOrigin : "top-left";
    // 锚点水平方向的单位
    this.anchorXUnits_ =
      options.anchorXUnits !== undefined ? options.anchorXUnits : "fraction";
    // 锚点垂直方向的位置
    this.anchorYUnits_ =
      options.anchorYUnits !== undefined ? options.anchorYUnits : "fraction";
    // 用于设置跨域请求的策略,默认为null
    this.crossOrigin_ =
      options.crossOrigin !== undefined ? options.crossOrigin : null;
    // 图标的图像
    const image = options.img !== undefined ? options.img : null;
    // 图像的唯一标识
    let cacheKey = options.src;

    assert(
      !(cacheKey !== undefined && image),
      "`image` and `src` cannot be provided at the same time"
    );

    if ((cacheKey === undefined || cacheKey.length === 0) && image) {
      cacheKey = /** @type {HTMLImageElement} */ (image).src || getUid(image);
    }
    assert(
      cacheKey !== undefined && cacheKey.length > 0,
      "A defined and non-empty `src` or `image` must be provided"
    );

    assert(
      !(
        (options.width !== undefined || options.height !== undefined) &&
        options.scale !== undefined
      ),
      "`width` or `height` cannot be provided together with `scale`"
    );

    let imageState; //图像状态
    if (options.src !== undefined) {
      imageState = ImageState.IDLE;
    } else if (image !== undefined) {
      if ("complete" in image) {
        if (image.complete) {
          imageState = image.src ? ImageState.LOADED : ImageState.IDLE;
        } else {
          imageState = ImageState.LOADING;
        }
      } else {
        imageState = ImageState.LOADED;
      }
    }

    // 图标的颜色
    this.color_ = options.color !== undefined ? asArray(options.color) : null;

    this.iconImage_ = getIconImage(
      image,
      cacheKey,
      this.crossOrigin_,
      imageState,
      this.color_
    );
    // 图标的偏移量
    this.offset_ = options.offset !== undefined ? options.offset : [0, 0];
    // 偏移量的原点
    this.offsetOrigin_ =
      options.offsetOrigin !== undefined ? options.offsetOrigin : "top-left";
    this.origin_ = null;
    // 图标的尺寸
    this.size_ = options.size !== undefined ? options.size : null;
    this.initialOptions_;

    // 如果用户同时提供了 width 或 height,则会根据图标的原始尺寸和目标尺寸进行缩放计算
    if (options.width !== undefined || options.height !== undefined) {
      let width, height;
      if (options.size) {
        [width, height] = options.size;
      } else {
        const image = this.getImage(1);
        if (image.width && image.height) {
          width = image.width;
          height = image.height;
        } else if (image instanceof HTMLImageElement) {
          this.initialOptions_ = options;
          const onload = () => {
            this.unlistenImageChange(onload);
            if (!this.initialOptions_) {
              return;
            }
            const imageSize = this.iconImage_.getSize();
            this.setScale(
              calculateScale(
                imageSize[0],
                imageSize[1],
                options.width,
                options.height
              )
            );
          };
          this.listenImageChange(onload);
          return;
        }
      }
      if (width !== undefined) {
        this.setScale(
          calculateScale(width, height, options.width, options.height)
        );
      }
    }
  }

  clone() {
    let scale, width, height;
    if (this.initialOptions_) {
      width = this.initialOptions_.width;
      height = this.initialOptions_.height;
    } else {
      scale = this.getScale();
      scale = Array.isArray(scale) ? scale.slice() : scale;
    }
    return new Icon({
      anchor: this.anchor_.slice(),
      anchorOrigin: this.anchorOrigin_,
      anchorXUnits: this.anchorXUnits_,
      anchorYUnits: this.anchorYUnits_,
      color:
        this.color_ && this.color_.slice
          ? this.color_.slice()
          : this.color_ || undefined,
      crossOrigin: this.crossOrigin_,
      offset: this.offset_.slice(),
      offsetOrigin: this.offsetOrigin_,
      opacity: this.getOpacity(),
      rotateWithView: this.getRotateWithView(),
      rotation: this.getRotation(),
      scale,
      width,
      height,
      size: this.size_ !== null ? this.size_.slice() : undefined,
      src: this.getSrc(),
      displacement: this.getDisplacement().slice(),
      declutterMode: this.getDeclutterMode(),
    });
  }

  getAnchor() {
    let anchor = this.normalizedAnchor_;
    if (!anchor) {
      anchor = this.anchor_;
      const size = this.getSize();
      if (
        this.anchorXUnits_ == "fraction" ||
        this.anchorYUnits_ == "fraction"
      ) {
        if (!size) {
          return null;
        }
        anchor = this.anchor_.slice();
        if (this.anchorXUnits_ == "fraction") {
          anchor[0] *= size[0];
        }
        if (this.anchorYUnits_ == "fraction") {
          anchor[1] *= size[1];
        }
      }

      if (this.anchorOrigin_ != "top-left") {
        if (!size) {
          return null;
        }
        if (anchor === this.anchor_) {
          anchor = this.anchor_.slice();
        }
        if (
          this.anchorOrigin_ == "top-right" ||
          this.anchorOrigin_ == "bottom-right"
        ) {
          anchor[0] = -anchor[0] + size[0];
        }
        if (
          this.anchorOrigin_ == "bottom-left" ||
          this.anchorOrigin_ == "bottom-right"
        ) {
          anchor[1] = -anchor[1] + size[1];
        }
      }
      this.normalizedAnchor_ = anchor;
    }
    const displacement = this.getDisplacement();
    const scale = this.getScaleArray();
    return [
      anchor[0] - displacement[0] / scale[0],
      anchor[1] + displacement[1] / scale[1],
    ];
  }
  setAnchor(anchor) {
    this.anchor_ = anchor;
    this.normalizedAnchor_ = null;
  }
  getColor() {
    return this.color_;
  }
  getImage(pixelRatio) {
    return this.iconImage_.getImage(pixelRatio);
  }
  getPixelRatio(pixelRatio) {
    return this.iconImage_.getPixelRatio(pixelRatio);
  }
  getImageSize() {
    return this.iconImage_.getSize();
  }
  getImageState() {
    return this.iconImage_.getImageState();
  }
  getHitDetectionImage() {
    return this.iconImage_.getHitDetectionImage();
  }
  getOrigin() {
    if (this.origin_) {
      return this.origin_;
    }
    let offset = this.offset_;

    if (this.offsetOrigin_ != "top-left") {
      const size = this.getSize();
      const iconImageSize = this.iconImage_.getSize();
      if (!size || !iconImageSize) {
        return null;
      }
      offset = offset.slice();
      if (
        this.offsetOrigin_ == "top-right" ||
        this.offsetOrigin_ == "bottom-right"
      ) {
        offset[0] = iconImageSize[0] - size[0] - offset[0];
      }
      if (
        this.offsetOrigin_ == "bottom-left" ||
        this.offsetOrigin_ == "bottom-right"
      ) {
        offset[1] = iconImageSize[1] - size[1] - offset[1];
      }
    }
    this.origin_ = offset;
    return this.origin_;
  }
  getSrc() {
    return this.iconImage_.getSrc();
  }
  getSize() {
    return !this.size_ ? this.iconImage_.getSize() : this.size_;
  }

  getWidth() {
    const scale = this.getScaleArray();
    if (this.size_) {
      return this.size_[0] * scale[0];
    }
    if (this.iconImage_.getImageState() == ImageState.LOADED) {
      return this.iconImage_.getSize()[0] * scale[0];
    }
    return undefined;
  }

  getHeight() {
    const scale = this.getScaleArray();
    if (this.size_) {
      return this.size_[1] * scale[1];
    }
    if (this.iconImage_.getImageState() == ImageState.LOADED) {
      return this.iconImage_.getSize()[1] * scale[1];
    }
    return undefined;
  }

  setScale(scale) {
    delete this.initialOptions_;
    super.setScale(scale);
  }

  listenImageChange(listener) {
    this.iconImage_.addEventListener(EventType.CHANGE, listener);
  }

  load() {
    this.iconImage_.load();
  }

  unlistenImageChange(listener) {
    this.iconImage_.removeEventListener(EventType.CHANGE, listener);
  }
  ready() {
    return this.iconImage_.ready();
  }
}
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

# Icon类的构造函数

Icon类构造函数的核心作用是初始化 Icon 实例,并根据传入的 options 配置来设置图标的各种属性,如透明度、旋转角度、缩放比例、图像来源等。它还会处理图标图像的加载、偏移、锚点等相关设置。

# Icon类的主要方法

  • clone()方法:复制一个Icon对象,内部就是实例化一个Icon类,并返回实例对象。

  • getAnchor()方法:计算并返回图标的锚点位置,getAnchor方法首先会检查normalizedAnchor_属性;若已经计算郭锚点位置,则直接返回该值;否则计算基于尺寸的锚点;计算的依据就是锚点的X或者Y单位以及锚点的位置。计算后会保存锚点,以便下次直接使用,避免重复计算;最后会计算位移以及缩放,返回最终的锚点。

  • setAnchor(anchor)方法:接受一个数组anchor,并将它赋值给this.anchor,然后将this.normalizedAnchor置空null

  • getColor()方法:获取this.color_属性

  • getImage(pixelRatio)方法:返回与图标相关的图像对象,pixelRatio参数是像素比率

  • getPixelRatio(pixelRatio)方法:返回图标图像的像素比率

  • getImageSize()方法:返回图标图像的尺寸(宽度和高度),形如[宽度,高度]

  • getImageState()方法:这个方法返回图像的当前加载状态。(ImageState.IDLE:图像处于空闲状态,尚未加载、ImageState.LOADING:图像正在加载中、ImageState.LOADED:图像加载完成、ImageState.ERROR:图像加载失败)

  • getHitDetectionImage()方法:用于点击检测的图像

  • getOrigin()方法:根据设置的偏移量、原点位置和图标大小来计算并返回图标或标注的“原点”位置。

  • getSrc()方法:返回图标图片的源 URL

  • getSize()方法:获取图标的尺寸

  • getWidth()方法:获取图标的宽度

  • getHeight()方法:获取图标的高度

  • setScale(scale)方法: 设置图标的缩放比例

  • listenImageChange(listener)方法:监听图标图像的变化事件

  • load()方法:加载图标图像

  • unlistenImageChange(listener)方法:移除监听图标图像变化的事件处理函数

  • ready()方法:检查图标图像是否已准备好

# 总结

Icon类主要就是管理图标包括其位置、尺寸、颜色、加载状态和缩放等。

编辑 (opens new window)
上次更新: 2025/01/22, 08:37:39
ImageStyle类
RegularShape类

← ImageStyle类 RegularShape类→

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