IconImage类
# 概述
在 Openlayers 中,IconImage
类主要用于表示一个图标图像对象,它包含了图像的各种信息,如图像本身、尺寸、源 URL、跨域访问设置、颜色、状态等。它继承自 EventTarget
类,意味着 IconImage
实例可以作为事件目标,允许事件监听和触发。
# 源码分析
# IconImage
类的源码实现
IconImage
类的源码实现如下:
class IconImage extends EventTarget {
constructor(image, src, crossOrigin, imageState, color) {
super();
this.hitDetectionImage_ = null;
this.image_ = image;
this.crossOrigin_ = crossOrigin;
this.canvas_ = {};
this.color_ = color;
this.imageState_ = imageState === undefined ? ImageState.IDLE : imageState;
this.size_ =
image && image.width && image.height ? [image.width, image.height] : null;
this.src_ = src;
this.tainted_;
this.ready_ = null;
}
initializeImage_() {
this.image_ = new Image();
if (this.crossOrigin_ !== null) {
this.image_.crossOrigin = this.crossOrigin_;
}
}
isTainted_() {
if (this.tainted_ === undefined && this.imageState_ === ImageState.LOADED) {
if (!taintedTestContext) {
taintedTestContext = createCanvasContext2D(1, 1, undefined, {
willReadFrequently: true,
});
}
taintedTestContext.drawImage(this.image_, 0, 0);
try {
taintedTestContext.getImageData(0, 0, 1, 1);
this.tainted_ = false;
} catch (e) {
taintedTestContext = null;
this.tainted_ = true;
}
}
return this.tainted_ === true;
}
dispatchChangeEvent_() {
this.dispatchEvent(EventType.CHANGE);
}
handleImageError_() {
this.imageState_ = ImageState.ERROR;
this.dispatchChangeEvent_();
}
handleImageLoad_() {
this.imageState_ = ImageState.LOADED;
this.size_ = [this.image_.width, this.image_.height];
this.dispatchChangeEvent_();
}
getImage(pixelRatio) {
if (!this.image_) {
this.initializeImage_();
}
this.replaceColor_(pixelRatio);
return this.canvas_[pixelRatio] ? this.canvas_[pixelRatio] : this.image_;
}
getPixelRatio(pixelRatio) {
this.replaceColor_(pixelRatio);
return this.canvas_[pixelRatio] ? pixelRatio : 1;
}
getImageState() {
return this.imageState_;
}
getHitDetectionImage() {
if (!this.image_) {
this.initializeImage_();
}
if (!this.hitDetectionImage_) {
if (this.isTainted_()) {
const width = this.size_[0];
const height = this.size_[1];
const context = createCanvasContext2D(width, height);
context.fillRect(0, 0, width, height);
this.hitDetectionImage_ = context.canvas;
} else {
this.hitDetectionImage_ = this.image_;
}
}
return this.hitDetectionImage_;
}
getSize() {
return this.size_;
}
getSrc() {
return this.src_;
}
load() {
if (this.imageState_ !== ImageState.IDLE) {
return;
}
if (!this.image_) {
this.initializeImage_();
}
this.imageState_ = ImageState.LOADING;
try {
if (this.src_ !== undefined) {
this.image_.src = this.src_;
}
} catch (e) {
this.handleImageError_();
}
if (this.image_ instanceof HTMLImageElement) {
decodeFallback(this.image_, this.src_)
.then((image) => {
this.image_ = image;
this.handleImageLoad_();
})
.catch(this.handleImageError_.bind(this));
}
}
replaceColor_(pixelRatio) {
if (
!this.color_ ||
this.canvas_[pixelRatio] ||
this.imageState_ !== ImageState.LOADED
) {
return;
}
const image = this.image_;
const ctx = createCanvasContext2D(
Math.ceil(image.width * pixelRatio),
Math.ceil(image.height * pixelRatio)
);
const canvas = ctx.canvas;
ctx.scale(pixelRatio, pixelRatio);
ctx.drawImage(image, 0, 0);
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = asString(this.color_);
ctx.fillRect(0, 0, canvas.width / pixelRatio, canvas.height / pixelRatio);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(image, 0, 0);
this.canvas_[pixelRatio] = canvas;
}
ready() {
if (!this.ready_) {
this.ready_ = new Promise((resolve) => {
if (
this.imageState_ === ImageState.LOADED ||
this.imageState_ === ImageState.ERROR
) {
resolve();
} else {
const onChange = () => {
if (
this.imageState_ === ImageState.LOADED ||
this.imageState_ === ImageState.ERROR
) {
this.removeEventListener(EventType.CHANGE, onChange);
resolve();
}
};
this.addEventListener(EventType.CHANGE, onChange);
}
});
}
return this.ready_;
}
}
export function get(image, cacheKey, crossOrigin, imageState, color, pattern) {
let iconImage =
cacheKey === undefined
? undefined
: iconImageCache.get(cacheKey, crossOrigin, color);
if (!iconImage) {
iconImage = new IconImage(
image,
image && "src" in image ? image.src || undefined : cacheKey,
crossOrigin,
imageState,
color
);
iconImageCache.set(cacheKey, crossOrigin, color, iconImage, pattern);
}
if (
pattern &&
iconImage &&
!iconImageCache.getPattern(cacheKey, crossOrigin, color)
) {
iconImageCache.set(cacheKey, crossOrigin, color, iconImage, pattern);
}
return iconImage;
}
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
# IconImage
类的构造函数
IconImage
类的构造函数内部就是初始化了一些属性,如下:
this.hitDetectionImage_
:用于在进行图像点击检测时使用备用图像,默认为null
this.image_
:设置image_
为传入的图像对象,用于存储图标的实际图像this.crossOrigin_
:设置crossOrigin_
为传入的跨域访问设置,用于处理图像加载时的跨域问题this.canvas
:初始化一个空的canvas_
对象,可能用于后续图像的绘制或缓存this.color
:设置图标的颜色属性,通常用于为图标添加颜色或修改现有颜色this.imageState_
:设置imageState_
为图像的当前状态,若没有提供imageState
,则默认为ImageState.IDLE
,表示图像处于待机状态。this.size_
:如果传入的image
对象具有width
和height
属性,则size_
被设置为图像的宽高数组[width, height]
,否则设置为null
,表示图像没有明确的尺寸信息。this.src_
:设置 src_ 为传入的图像源 URL,用于指向图标图像的位置。this.tainted_
:初始化tainted_
属性,但未赋值。这个属性可能用于标记图像是否“污染”,通常与跨域图像加载有关,当图像的跨域访问被限制时,可能会被标记为污染。this.ready
:初始化ready_
为null
,该属性可能用于标识图像是否准备好,可以用于图像加载状态的判断.
# IconImage
类的主要方法
initializeImage_()
方法:初始化图像,就是实例化一个Image
对象,然后判断,若this.crossOrigin
不为null
,则将它设置成Image
对象实例也就是属性this.image_
的crossOrigin
的属性值。isTainted_()
方法:用于检测图像是否由于跨域问题或安全策略而被污染。如果图像污染了,开发者就不能读取它的像素数据,这通常会影响一些图像处理或绘制操作。检测原理就是将其绘制到画布上,然后通过getImageData
方法获取元素,若可以获取,则说明没有被污染;最后返回一个布尔值。dispatchChangeEvent_()
方法:内部就是调用父类的this.dispatchEvent
方法handleImageError_()
方法:处理图片加载错误会调用该方法,会将this.imageState_
标识为错误状态,然后调用this.dispatchChangeEvent_
方法handleImageLoad_()
方法:图片加载完成会被调用,会将this.imageState_
标识为已加载状态以及设置this.size
图片的大小,然后调用this.dispatchChangeEvent_
方法getImage(pixelRatio)
方法:根据像素比获取图片,先判断,若this.image_
不存在,则调用this.initializeImage_
进行初始化,然后调用this.replaceColor_
方法,进行创建该像素比下的图像canvas
,若该图像存在则返回,否则返回this.image_
。getPixelRatio(pixelRatio)
方法:类似getImage(pixelRatio)
方法,返回的是像素比,若this.canvas
中不存在该像素比的图片,就返回1
。getImageState()
方法:获取图像的状态getHitDetectionImage()
方法:如果图像未被污染,则返回原始图像;如果图像被污染,则返回一个替代的命中检测图像,这个替代图像的主要作用是用来进行交互检测。getSize()
方法:获取图像的大小,即this.size_
getSrc()
方法:获取图像源load()
方法:负责加载图像,设置图像源,并处理加载过程中的成功或失败。该方法负责加载图像。它检查图像的当前状态,并在图像尚未加载时开始加载过程。经过一系列检查后,使用decodeFallback
方法尝试解码图像。如果成功,调用handleImageLoad_()
处理图像加载成功的逻辑。如果失败,调用handleImageError_()
处理错误replaceColor_(pixelRatio)
方法:在图像上应用颜色,并处理高分辨率屏幕上的显示ready()
方法:返回一个Promise
,该Promise
在图像加载完成后解决(或者在加载失败时解决)。它用于确保图像加载完成后执行某些操作
# get
函数
get
函数通过使用一个图标图像缓存(iconImageCache
),高效地管理图标图像的创建和获取。
如果图标图像已经缓存,则直接返回缓存中的图标图像。
如果图标图像未缓存,则创建新的
IconImage
对象并将其存入缓存。该函数还会根据提供的参数(如颜色和图案)对图标图像进行处理,并确保图案的正确缓存。
通过这种方式,图标图像能够避免重复创建,提高性能,同时还支持跨域、颜色替换、图案应用等特性
# 总结
本文主要介绍了IconImage
类的核心逻辑,涉及到图像的加载,以及错误处理、状态变更等。