访问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
类的核心逻辑,涉及到图像的加载,以及错误处理、状态变更等。