访问Openlayers网站(https://jinuss.github.io/Openlayers_map_pages/,网站是基于
Vue3
+Openlayers
,里面有大量的实践和案例。觉得还不错,可以给个小星星Star,鼓励一波 https://github.com/Jinuss/OpenlayersMap哦~
概述
在 Openlayers 中,Icon
类用于创建图标,通常用于在地图上显示标记或图像元素。这个类可以通过设置图标的各种属性(如大小、图像源、锚点等)来控制图标的外观和位置。
Icon
类继承于ImageStyle
类,关于ImageStyle
类,可以参考这篇文章源码分析之Openlayers中样式篇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();
}
}
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
类主要就是管理图标包括其位置、尺寸、颜色、加载状态和缩放等。