前两天一位朋友问了我一个问题,他希望在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
对象赋值给fillStyle
或StrokeStyle
,之后进行绘制就可以使用渐变色了。
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
作为图形样式的color
(CanvasGriadient
算是ColorLike
),依旧使用OpenLayers的语法进行绘图。这种方式我下面将称为"插件式方法"。
方式二,完全使用Canvas的语法进行图形绘制(在OpenLayers中Style
的renderer
函数或者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
或者Stroke
的color
属性值。
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
函数中需要我们自己把图形给绘制出来,这个千万不要忘记了。
参考资料
- OpenLayers v10.4.0 API - Class: Fill
- OpenLayers v10.4.0 API - Module: ol/colorlike
- CanvasGradient - Web API | MDN
- CanvasGradient:addColorStop() 方法 - Web API | MDN
- CanvasRenderingContext2D:createLinearGradient() 方法 - Web API | MDN
- CanvasRenderingContext2D:createRadialGradient() 方法 - Web API | MDN
- CanvasRenderingContext2D:createConicGradient() 方法 - Web API | MDN
- openlayers绘制渐变背景多边形圆形_openlayers渐变色-CSDN博客
- OpenLayers线性渐变和中心渐变(径向渐变)_openlayers colorlike-CSDN博客
- 二十、openlayers官方示例Custom Circle Render解析------自定义圆形渲染、绘制渐变色圆形_openlayers circle-CSDN博客
- Custom Circle Render