OpenLayers:如何使用渐变色

前两天一位朋友问了我一个问题,他希望在OpenLayers中给扇形的填充色设置为渐变色。于是我便开始研究起了这个问题,发现OpenLayers中使用渐变色还并不简单。

一、使用渐变色的基本思路

首先我认为要确定基本的思路,即使用什么样的技术去实现渐变色。其实无非是有三种可能得方向:

第一个思路 "OpenLayers内置的渐变色样式" 这个好像是走不通的,OpenLayers中似乎并没一个类似于grdient类型之类的东西。

第二个思路"使用CSS中的渐变",这个你还真别说网上还真有文章是按照这个思路去走的,可是它写的这个代码,我尝试了之后发现是无效的,所以下面这种写法是错误的。

第三个思路"使用Canvas中的渐变",这个思路才是正确的。因为OpenLayers的底层就是Canvas,所以OpenLayers中肯定是可以用Canvas的渐变的。这一点在我查阅了官方文档后也得到了验证:

首先我们查看OpenLayers中的Fill类,可以看到它的Color属性是可以接收一个ColorLike的。

再来看ColorLike的描述,里面写的很清楚 :

ColorLike是一种能被 CanvasRenderingContext2D.fillStyle(画布渲染上下文 2D 的填充样式)或 CanvasRenderingContext2D.strokeStyle(画布渲染上下文 2D 的描边样式)所接受的类型。它表示一种颜色、CanvasPattern(画布图案)或者 CanvasGradient(画布渐变)。

也就是说在OpenLayers中我们是可以将图形的填充色设置为 CanvasGradient

二、Canvas中如何实现渐变

1.基本使用流程

既然确定了思路是使用Canvas中的渐变,那就要了解Canvas中的渐变是怎么用的。

使用Canvas渐变的核心就是要获取到上面提到的CanvasGradient对象,因此使用Canvas渐变的过程就是创建并使用CanvasGradient对象的过程,这个过程分为三步:创建、配置和使用。

第一步:创建

首先CanvasGradient对象只能通过createLinearGradient()createConicGradient() createRadialGradient() 方法返回,这三个方法分别用于创建线性渐变、锥形渐变和径向渐变。

第二步:配置

在创建了CanvasGradient对象后就要配置渐变的颜色,这需要使用CanvasGradient.addColorStop() 方法。

第三步:使用

将配置好的CanvasGradient对象赋值给fillStyleStrokeStyle,之后进行绘制就可以使用渐变色了。

2.使用线性渐变

使用createLinearGradient()可以创建线性渐变。线性渐变顾名思义线性渐变就是一种沿直线方向变化的渐变色,因此在使用createLinearGradient()方法创建线性渐变时要设置四个参数以指定直线的位置。注意这四个位置参数都是指的Canvas中的像素坐标。

示例:

JavaScript 复制代码
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

// 创建一个线性渐变
// 渐变起点在 x=20、y=0
// 渐变终点在 x=220、y=0
const gradient = ctx.createLinearGradient(20, 0, 220, 0);

// 添加三个色标
gradient.addColorStop(0, "green");
gradient.addColorStop(0.5, "cyan");
gradient.addColorStop(1, "green");

// 设置填充样式并绘制矩形
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 200, 100);

3.使用径向渐变

径向渐变使用createRadialGradient()方法创建。径向渐变是由一个点(或者圆)向四周扩散的渐变,也就是说它与线性渐变不同,线性渐变是往一个方向渐变,而径向渐变是往四周的所有方向进行渐变。因此径向渐变的定义方式也与线性渐变不同,线性渐变是定义两个点来确定渐变的范围和方向,径向渐变则是定义两个圆来确定渐变的范围和方向。

示例:

JavaScript 复制代码
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

// 创建一个径向渐变
// 内圆位于 x=110、y=90,半径为 30
// 外圆位于 x=100,、y=100,半径为 70
const gradient = ctx.createRadialGradient(110, 90, 30, 100, 100, 70);

// 添加三个色标
gradient.addColorStop(0, "pink");
gradient.addColorStop(0.9, "white");
gradient.addColorStop(1, "green");

// 设置填充样式并绘制矩形
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 160, 160);

4.使用锥形渐变

锥形渐变使用createConicGradient()方法创建。锥形渐变可以理解为沿着弧线方向的渐变,它的定义方式与我想象的还不一样,我本来以为会通过定义两个角度来确定锥形渐变的方向和范围,可实际上创建锥形渐变时只能设置一个起始角度,之后会从这个其实角度开始沿顺时针方向渐变。

示例:

JavaScript 复制代码
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

// 创建一个锥形渐变
// 开始角度为 0
// 中心位置为 100, 100
const gradient = ctx.createConicGradient(0, 100, 100);

// 添加五个色标
gradient.addColorStop(0, "red");
gradient.addColorStop(0.25, "orange");
gradient.addColorStop(0.5, "yellow");
gradient.addColorStop(0.75, "green");
gradient.addColorStop(1, "blue");

// 设置填充样式并绘制矩形
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 200, 200);

三、在OpenLayer中使用Canvas渐变色

在OpenLayers中使用Canvas渐变色据我了解有两种方式:

方式一,只将CanvasGriadient作为图形样式的colorCanvasGriadient算是ColorLike),依旧使用OpenLayers的语法进行绘图。这种方式我下面将称为"插件式方法"。

方式二,完全使用Canvas的语法进行图形绘制(在OpenLayers中Stylerenderer函数或者ImageCanvas数据源都支持直接使用Canvas语法绘图)。这种方式我下面将称为"完全式方法"。

1.插件式方法

插件式方法如何使用?

先创建一个CanvasGriandient

JavaScript 复制代码
  const canvas = document.createElement("canvas");
  const context = canvas.getContext("2d");

  // 创建渐变
  let gradient = context.createLinearGradient(512, 0, 1920, 0);

  gradient.addColorStop(0, "rgba(255, 0, 0, 1)"); // 渐变开始颜色
  gradient.addColorStop(1, "rgba(0, 0, 255, 1)"); // 渐变结束颜色

然后可以将其作为Fill或者Strokecolor属性值。

JavaScript 复制代码
  vectorLayer.setStyle(
    new Style({
      fill: new Fill({
        color: gradient,
      }),
    })
  );

之后正常的进行绘图就可以绘制出带有渐变色的图形了。

插件式方法的缺陷

地图移动时颜色会变化

这种插件式的方法有一个巨大的坑,我们在创建CanvasGradient渐变色的时候使用的Canvas像素坐标,但我们在绘制图形的时候使用的却是地理空间坐标。这种矛盾将会导致一个严重的问题,渐变色是与屏幕上的某个像素区域绑定的,而不是与地理空间区域绑定的。于是就会出现下图的情况当我们移动地图时,图形在屏幕中的位置就会发生变化,此时它的渐变色也会发生变化😂。

这个问题目前也没有找到什么好的解决办法。

2.完全式方法

完全式的方法会稍微复杂一些,我们在设置样式的时候直接使用Style类的renderer函数进行图形的绘制。可以看到在renderer函数中完全采用了Canvas的语法进行绘制。

JavaScript 复制代码
  // 设置渐变样式
  vectorLayer.setStyle(
    new Style({
      renderer: function (coordinates, state) {
        const arr = coordinates[0];
        const ctx = state.context;

        // 创建渐变
        let gradient = ctx.createLinearGradient(
          Math.min(...arr.map(item => item[0])) * DEVICE_PIXEL_RATIO,
          0,
          Math.max(...arr.map(item => item[0])) * DEVICE_PIXEL_RATIO,
          0
        );

        gradient.addColorStop(0, "rgba(255, 0, 0, 1)"); // 渐变开始颜色
        gradient.addColorStop(1, "rgba(0, 0, 255, 1)"); // 渐变结束颜色

        ctx.fillStyle = gradient;

        // 绘制矩形
        ctx.beginPath();

        arr.forEach((point, index) => {
          if (index === 0) {
            ctx.moveTo(point[0], point[1]);
          } else {
            ctx.lineTo(point[0], point[1]);
          }
        });

        ctx.closePath();

        ctx.fill();
      },
    })
  );

这样绘制出来的图形不会存在之前插件式方法的问题,即使移动地图渐变色也不会有改变。

其中要特别注意的是,第一renderer函数有的两个参数,coordinates是图形的像素坐标,state中保存着绘图上下文,可以直接拿它来进行绘图。

第二,renderer函数中需要我们自己把图形给绘制出来,这个千万不要忘记了。

参考资料

  1. OpenLayers v10.4.0 API - Class: Fill
  2. OpenLayers v10.4.0 API - Module: ol/colorlike
  3. CanvasGradient - Web API | MDN
  4. CanvasGradient:addColorStop() 方法 - Web API | MDN
  5. CanvasRenderingContext2D:createLinearGradient() 方法 - Web API | MDN
  6. CanvasRenderingContext2D:createRadialGradient() 方法 - Web API | MDN
  7. CanvasRenderingContext2D:createConicGradient() 方法 - Web API | MDN
  8. openlayers绘制渐变背景多边形圆形_openlayers渐变色-CSDN博客
  9. OpenLayers线性渐变和中心渐变(径向渐变)_openlayers colorlike-CSDN博客
  10. 二十、openlayers官方示例Custom Circle Render解析------自定义圆形渲染、绘制渐变色圆形_openlayers circle-CSDN博客
  11. Custom Circle Render
相关推荐
全宝几秒前
🚀 一文搞定 claude code:国内环境下的安装、配置与体验
前端·ai编程·claude
寻觅~流光5 分钟前
封装---优化try..catch错误处理方式
开发语言·前端·javascript·typescript
csj508 分钟前
前端基础之《Vue(22)—安装MongoDB》
前端·vue
今天也在写bug11 分钟前
输入npm install后发生了什么
前端·npm·node.js
玲小珑36 分钟前
Next.js 教程系列(十六)Next.js 中的状态管理方案
前端·next.js
前端小巷子38 分钟前
web实现文件的断点续传
前端·javascript·面试
小磊哥er38 分钟前
【前端工程化】前端项目怎么做代码管理才好?
前端
jojo是只猫1 小时前
前端vue对接海康摄像头流程
前端·javascript·vue.js
10年前端老司机5 小时前
React无限级菜单:一个项目带你突破技术瓶颈
前端·javascript·react.js
阿芯爱编程9 小时前
2025前端面试题
前端·面试