本文我们主要讲下如何在 openlayers 中使用 iconfont 字体,看明白了 iconfont ,那么Font Awesome 也就能明白了。虽然网上也能搜到一些讲这个的帖子,但是都讲得不清不楚的,比如牛老师的,我看完以后就是丈二和尚摸不着头脑,所以本文中,我会以 iconfont 为例,从最基础的讲起,力求初学者也能看的懂。另外遇到了哪些坑,我们也来讲一讲。
说到准备工作,那自然是我们要先去 iconfont 官网去下载自己的字体了,看一下我下载好的字体
接下来就是在我们的测试页面中引入 iconfont 的css,然后使用具体的图标了。
注意,iconfont 提供了 3 种在页面中显示的方式,分别是Unicode、Font class和 Symbol ,如果普通的页面元素上使用字体图标,这几种都支持,并且Font class是大家用的最多的方式,,因为既直观又简单。但是在openlayers中,经过我的测试,不支持Font class方式,这一点可以说相当可惜。
注意看,左上角已经显示了 iconfont 的图标了,说明我们的配置没问题。
因为我要实现的是对矢量切片进行样式的配置,所以就新建一个矢量切片的图层。我们的示例图层也使用 GeoServer 中自带的示例图层来进行展示。
var projection = new ol.proj.Projection({
code: 'EPSG:4326',
units: 'degrees',
axisOrientation: 'neu',
global: true
});
var layerName="sf:bugsites";
var pbfLayer4326=new ol.layer.VectorTile({
source: new ol.source.VectorTile({
url: "http://localhost:8080/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER="+layerName+"&STYLE=&TILEMATRIX=EPSG:4326:{z}&TILEMATRIXSET=EPSG:4326&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}",
format: new ol.format.MVT({}),
projection: projection,
tileGrid: new ol.tilegrid.WMTS({
tileSize: [256,256],
origin: [-180.0, 90.0],
resolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 6.866455078125E-4, 3.4332275390625E-4, 1.71661376953125E-4, 8.58306884765625E-5, 4.291534423828125E-5, 2.1457672119140625E-5, 1.0728836059570312E-5, 5.364418029785156E-6, 2.682209014892578E-6, 1.341104507446289E-6, 6.705522537231445E-7, 3.3527612686157227E-7],
matrixIds: ['EPSG:4326:0', 'EPSG:4326:1', 'EPSG:4326:2', 'EPSG:4326:3', 'EPSG:4326:4', 'EPSG:4326:5', 'EPSG:4326:6', 'EPSG:4326:7', 'EPSG:4326:8', 'EPSG:4326:9', 'EPSG:4326:10', 'EPSG:4326:11', 'EPSG:4326:12', 'EPSG:4326:13', 'EPSG:4326:14', 'EPSG:4326:15', 'EPSG:4326:16', 'EPSG:4326:17', 'EPSG:4326:18', 'EPSG:4326:19', 'EPSG:4326:20', 'EPSG:4326:21']
}),
wrapX: true
}),
style:function(feature,resolutions){
// console.log(feature.getType(),feature.get("layer"),resolutions);
if(feature.getType()=="Point"){
return new ol.style.Style({
text: new ol.style.Text({
font: 'normal 12px "iconfont"' ,
text: "\ue61b",
fill: new ol.style.Fill({ color: '#00f' }),
textBaseline: 'bottom'
})
});
}else if(feature.getType()=="Polygon" || feature.getType()=="MulitiPolygon"){
//...
}else if(feature.getType()=="LineString" || feature.getType()=="MulitiLineString"){
//...
}
}
});
var map = new ol.Map({
layers: [pbfLayer4326],
target: 'map',
view: new ol.View({
zoom:13,
center:[-103.78,44.40],
projection: projection
})
});
请注意这段关键代码中 font 和 text 的属性的写法,font 属性中的 iconfont 用双引号包了起来,而 text 属性则使用的是 Unicode。
if(feature.getType()=="Point"){
return new ol.style.Style({
text: new ol.style.Text({
font: 'normal 12px "iconfont"' ,
text: "\ue61b",
fill: new ol.style.Fill({ color: '#00f' }),
textBaseline: 'bottom'
})
});
}
那么 “\ue61b” 对应的是哪个 Unicode 图标,这还得回到我们下载 iconfont 字体里去看一下
可以看出,对应的公园的图标,但是这里显示的是 “”,为什么在 openlayers 里要改成 “\ue61b” 呢?因为 “” 是十六进制写法,“\u” 则是对应的 Unicode 写法。
那么如果我们将 openlayers 里的写法换成是 Font class写法,会发生什么事呢?我们来尝试一下,修改一下代码
if(feature.getType()=="Point"){
return new ol.style.Style({
text: new ol.style.Text({
font: 'normal 12px "iconfont"' ,
text: "icon-gongyuan",
fill: new ol.style.Fill({ color: '#00f' }),
textBaseline: 'bottom'
})
});
}
可以发现,字体图标并没有起作用,本文中用到的 openlayers 版本为4.6.5,并没有使用更高的版本进行测试,不排除是版本问题,也可能后续版本进行了修复,但是3.x和4.x中想使用字体图标,那就必须是本文中的写法,才能起作用。
我还曾遇到过一个很奇怪的问题,地图首次加载时字体图标死活出不来,显示的是一个方框,后来刷新一下页面就出来了。我不知道为什么会存在延迟加载的问题,目前我的解决方式是,先在页面给个普通元素 span 加载一下相应的图标,之后才在 openlayers 里去使用这个字体图标,这样就不会存在使用上的延迟问题。
经过调试,我发现造成这一现象的主要原因是,iconfott.woff2 的请求顺序决定的。
可以看到 iconfott.woff2 是由 iconfont.css 请求的,如果 iconfott.woff2 是在矢量切片之前被请求到,则显示就是正确的,反之显示的就是一个方框。
那么有没有更好的解决方式呢?答案是:有,link 标签可以预加载
<link rel="preload" as="font" href="./iconfont/iconfont.woff2?t=1665994611202" type="font/ttf" crossorigin="anonymous">
href 中需要传递一个 t 值,我们可以在 iconfont.css 中的找到它
那么如果我们想使用一个特殊的图标,应该如何制作呢?比如高德中的这种:
首先这个图标是由两层构成的,下面的一层是圆圈,上面的一层才是图标,那么怎么在 openlayers 中画下面的这个圆呢?答案是使用 ol.style.RegularShape 这个类,这个类是用来画五角星的,参数里可以给定点的数量,如果只给五个,就是五角星,那要是给360个呢?那么不就是圆了么?
但是360个太多了,影响性能,36个就能满足我们的需求。
var tubiao="\u0119";
var color="#ff5b4d";
var offsetX=-0.5;
var scale=1;
return [
new ol.style.Style({
text:new ol.style.Text({
font: 'normal 15px "iconfont"' ,
text: tubiao,
fill: new ol.style.Fill({ color: "#fff"}),
textBaseline: 'bottom',
scale:scale,
offsetY:6,//正值向下,负值向上
offsetX:offsetX,//正值向右,负值向左
}),
image:new ol.style.RegularShape({
points:36,
radius:8,
fill:new ol.style.Fill({ color: color }),
})
}),
new ol.style.Style({
text:new ol.style.Text({
font: 'bold 12px arial' ,
text: "故宫博物院",
overflow:true,
stroke: new ol.style.Stroke({
color: '#fff',
width: 1
}),
fill: new ol.style.Fill({ color: color }),
textBaseline: 'bottom',
offsetY:24,
scale:1,
}),
}),
];
本文以 iconfont 为例,讲解了如何在 openlayers 中使用字体图标,并讲解了其中遇到的问题,以及对应的解决方案。值得注意的是,本文中是以 wmts 的方式加载的矢量切片,网上搜到的资料,大部分人使用的 tms 方式来加载矢量切片,因 tms 并不是 ogc 的标准,所以本文并未采用。当然,不论以何种形式来加载矢量切片,并不会对样式有影响,还是这样去设置的。
所以在一定程度上可以说,本文是一石二鸟,在一个示例里,既讲了矢量切片的 wmts 加载,又讲了 openlayers 中字体图标的使用,一举两得。好了,本文就讲到这里了,回见~