Openlayers用图片填充面
# 概述
我们知道openlayers
通常设置feature
要素的方式主要是通过ol.style.stroke
描边和ol.style.fill
填充,这对比较简单的样式设置十分有效。如果要设置feature
的图标,还可以通过ol.style.Icon
加载外部图片资源,但是如果要设置面的填充,就需要稍微复杂的设置。
# 效果
# 解决方案
# colorlike
openlayers
中定义了colorlike
类型, 通过ol.colorlike
可以加载图片资源,返回的是一个CanvasPattern
,而ol.style.fill
的color
属性可以接收这个值,从而实现图片的填充。
colorlike
提供一个方法asColorLike
,参数可以是一个图片,返回一个 color
,其类型为Color
|ColorLike
|Patttern
# CanvasPattern
但是在开发中,用到比较多的是CanvasPattern
,它是canvas
中的类型,在 MDN 中的描述如下
CanvasPattern
接口表示一个不透明对象,描述了一个基于图像、画布或视频的模板,该模板通过CanvasRenderContext2D.createPattern
方法创建。 它们可用作fillStyle
或strokeStyle
CanvasRenderContext2D
就是通过canvas.getContext("2d")
获取的canvas
2d 的上下文环境
但是如果我们需要动态改变填充图片的透明度,单纯依靠CanvasPattern
是无法实现,因为如上的定义中已经提及它是表示一个不透明对象,如果外部资源图片是一个透明图片呢,那样也只能保证初始是透明,而无法动态修改。这时就需要用到canvas
中的 API 了。
# canvas
的魔力
先看看如下示例
export const getImagePattern = (imgUrl, opacity = 1) => {
return new Promise((resolve, reject) => {
var img = new Image();
img.src = imgUrl;
img.onload = function () {
var cnv = document.createElement("canvas");
var ctx = cnv.getContext("2d");
cnv.width = img.width;
cnv.height = img.height;
ctx.drawImage(img, 0, 0);
var imageData = ctx.getImageData(0, 0, cnv.width, cnv.height);
var data = imageData.data;
for (var i = 3; i < data.length; i += 4) {
data[i] = opacity * 255;
}
ctx.putImageData(imageData, 0, 0);
var pattern = ctx.createPattern(cnv, "repeat");
resolve(pattern);
};
img.onerror = function (error) {
reject(error);
};
});
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
示例中定义一个获取图片Pattern
的方法,返回的是一个Promise
对象,接收两个参数
img
:资源图片opacity
:透明度 (0~1)
在图片加载成功后,创建一个canvas
画布,调用drawImage
方法将图片渲染到画布上,再通过getImageData
方法拿到图片数据,改变每个像素的透明度,再次渲染到画布上,最后调用createPattern
方法创建pattern
# getImageData
方法
getImageData
方法可以实现对图像的像素操作,其语法:ctx.getImageData(x,y,w,h)
参数
x
:被提取图像矩形区域的左上角 x 坐标
y
:被提取图像矩形区域的左上角 x 坐标
w
:被提取图像矩形区域的宽度
h
:被提取图像矩形区域的高度
补充一点:canvas
画布的原点坐标:(0,0)位于画布的左上角
返回值
getImageData
会返回一个ImageData
对象,它包含canvas
给定的也是被提取图像的矩形区域的数据。该对象的height
和width
分别对应参数h
和w
,而示例中用到的imageData.data
则是一个一维数组,包含以RGBA
顺序的数据,大小为 0 —— 255 的整数表示
示例中的 for 循环就是处理RGBA
中A
即alpha
:透明度
所以这会引发一个新的问题,如果图片很大,这种遍历的操作会带来性能的问题
# putImageData
方法
putImageData
顾名思义就是将ImageData
对象绘制到画布中,示例中只写了必传的参数,其语法为
putImageData(imageData, dx, dy);
putImageData(imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2
参数
imageData
:上面提到的ImageData
对象
dx
和dy
就是要绘制到canvas
的起始位置
dirtyX
和dirtyY
对应ImageData
中的左上角的坐标,默认均为 0
dirtyWidth
:要绘制的矩形的宽度,默认为imageData
的宽度
dirtyHeight
:要绘制的矩形的宽度,默认为imageData
的高度
# 调用
调用getImagePattern
方法时,动态改变参数opacity
获取到pattern
赋值就可以实现openlayers
的fetaure
填充图像的透明度改变
const pattern = await getImagePattern(img, opacity / 100);
newStyle = new Style({
stroke: new Stroke({
color: color,
width: width,
}),
fill: new Fill({
color: pattern,
}),
});
fetaure.setStyle(newStyle);
2
3
4
5
6
7
8
9
10
11