访问Openlayers网站(https://jinuss.github.io/Openlayers_map_pages/,网站是基于
Vue3
+Openlayers
,里面有大量的实践和案例。觉得还不错,可以 给个小星星Star,鼓励一波 https://github.com/Jinuss/OpenlayersMap哦~
概述
在 Openlayers 中,IconImage
类主要用于表示一个图标图像对象,它包含了图像的各种信息,如图像本身、尺寸、源 URL、跨域访问设置、颜色、状态等。它继承自 EventTarget
类,意味着 IconImage
实例可以作为事件目标,允许事件监听和触发。
关于EventTarget
类,可以参考这篇文章源码分析之Openlayers的核心EventTarget类的实现
源码分析
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;
}
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
类的核心逻辑,涉及到图像的加载,以及错误处理、状态变更等。