首页 > 编程语言 >源码分析之Openlayers样式篇IconImage类

源码分析之Openlayers样式篇IconImage类

时间:2025-01-23 13:27:41浏览次数:3  
标签:canvas pixelRatio crossOrigin image IconImage imageState 源码 Openlayers 图像

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

标签:canvas,pixelRatio,crossOrigin,image,IconImage,imageState,源码,Openlayers,图像
From: https://blog.csdn.net/m0_46281382/article/details/145308814

相关文章

  • JSP拼车系统1fxmu(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着城市化进程的加快和交通拥堵问题的日益严重,拼车作为一种共享出行的方式,逐渐成为缓解交通压力、降低出行成本的有效途径。拼......
  • JSP暖暖猫窝系统a856q--程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着宠物经济的兴起,宠物猫已成为许多家庭的重要成员。然而,传统的猫窝已无法满足现代宠物猫的生活需求。因此,开发一款智能、舒适......
  • (附源码)基于Java的工艺品展示系统的设计与实现-计算机毕设 37411
    基于Java的工艺品展示系统的设计与实现摘要本论文主要论述了如何使用Java语言开发一个工艺品展示系统,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述工艺品展示系统的当前背景以及系统开发的目的,后续章......
  • JSP农副及衍生产品交易系统3qxs1--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着互联网技术的发展,电子商务已成为推动各行各业数字化转型的重要力量。在农副产品领域,传统的交易模式存在信息不对称、交易效......
  • JSP农业种植管理系统5kb33--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、项目背景与意义随着科技的飞速发展,农业领域正逐步向智能化、信息化转型。农业种植管理作为农业生产的关键环节,其效率与质量直接影响农作物的产......
  • Springcloud在线问诊平台-毕业设计源码24037
    基于springcloud技术的在线问诊平台的设计与实现摘 要在当今社会,医疗资源分配的不均衡和医疗卫生资源的匮乏成为了制约医疗服务质量提升的关键因素。特别是在广大农村地区和偏远地区,优质的医疗资源更是稀缺。因此,打破地域限制,实现医疗资源的优化配置和高效利用,成为了医疗......
  • SSM酒店管理系统-毕业设计源码24361
    摘 要当前,酒店行业面临着客户需求多样化、市场竞争激烈和运营成本上升等多重挑战。为应对这些挑战,酒店需要通过提高运营效率、优化客户体验来增强竞争力。为此,本文提出一种酒店管理系统,本系统是采用SSM框架进行开发设计。SSM(Struts、Spring、MyBatis)是一种广泛使用的J......
  • SSM建材生产智能化系统-毕业设计源码24838
    目 录1绪论1.1研究背景与意义1.2国内外研究现状1.3论文结构与章节安排2 建材生产智能化系统系统分析2.1可行性分析2.1.1技术可行性分析2.1.2 经济可行性分析2.1.3法律可行性分析2.2系统功能分析2.2.1功能性分析2.2.2非功能性分析2.3 ......
  • 计算机毕业设计源码 校园二手交易平台
    标题:校园二手交易平台校园二手交易平台是连接学生之间进行二手商品买卖的在线市场。这种平台通常包括书籍、电子设备、生活用品等多种商品,旨在为学生提供方便、经济的交易方式。以下是校园二手交易平台的基本功能和设计要素:一、基本功能用户注册与登录学生可通过邮箱、手......
  • 计算机毕设源码 乡村振兴视角下农村农民收入动态跟踪系统
    标题:乡村振兴视角下农村农民收入动态跟踪系统设计一个农村农民收入动态跟踪系统,可以帮助政府、研究机构、和相关组织实时监测和分析农民收入变化,以便更好地实施乡村振兴战略。以下是一个综合性的农村农民收入动态跟踪系统的关键功能模块及其详细说明:1.系统用户管理用户角......