自定义事件及应用
通过API文档,我们可以看到OpenLayers 3的相关类,都有一些事件,但这些事件大多是和现有引擎相关的,并不能满足我们大多数的业务需求。如果能为OpenLayers 3的类增加自定义事件,那么必然能更好地实现业务需求。接下来就尝试为ol.Feature
添加一个mouseover
的事件,通过这个事件,就可以实现在鼠标移到Feature
上时,改变它的样式。
要添加自定义事件,需要知道这样一个事实:ol.Feature
继承于ol.Object
,而ol.Object
具有派发事件(dispatchEvent)和监听事件(on)的功能。 关于这两个功能的详细信息可以参见API文档。这样,我们要自定义事件就非常容易了,如果注意观察,会发现OpenLayers 3中的类都继承于ol.Object
,也就是说,如果自定义事件方法在ol.Feature
上有效,那么在其他的OpenLayers 3的类上也是同样有效的。
下面就先展示一下通过自定义的mouseover
事件来改变Feature
的样式:
为了对比,在地图上添加了两个圆,一个黄色的,一个红色的。鼠标移动到红色的圆上,它会变成蓝色的圆。但是鼠标移到黄色的圆上,不会有任何改变。这个功能在官网的例子中也有,参见select-features。那么用自定义事件的方式来做,又该怎样来实现呢?对应的源码如下:
<!Doctype html>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
<meta content=always name=referrer>
<title>OpenLayers 3地图示例</title>
<link href="../src/ol3.13.1/ol.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../src/ol3.13.1/ol.js" charset="utf-8"></script>
</head>
<body>
<div id="map" style="width: 100%, height: 400px"></div>
<script>
// 在原点处创建一个feature
var feature1 = new ol.Feature({
geometry: new ol.geom.Point([0, 0])
});
// 并设置为半径为100像素的圆,用红色填充
feature1.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 100,
fill: new ol.style.Fill({
color: 'red'
})
})
}));
// 在坐标[5000000, 5000000]处创建另一个feature
var feature2 = new ol.Feature({
geometry: new ol.geom.Point([5000000, 5000000])
});
// 并设置为半径为100像素的圆,用黄色填充
feature2.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 100,
fill: new ol.style.Fill({
color: 'yellow'
})
})
}));
// 创建地图
var map = new ol.Map({
// 设置地图图层
layers: [
// 创建一个使用Open Street Map地图源的瓦片图层
new ol.layer.Tile({source: new ol.source.OSM()}),
// 把之前创建的feature1和feature2放在另一个层里
new ol.layer.Vector({source: new ol.source.Vector({
features: [feature1, feature2]
})})
],
// 设置显示地图的视图
view: new ol.View({
center: [0, 0], // 定义地图显示中心于经度0度,纬度0度处
zoom: 2 // 并且定义地图显示层级为2
}),
// 让id为map的div作为地图的容器
target: 'map'
});
// 为地图注册鼠标移动事件的监听
map.on('pointermove', function(event){
map.forEachFeatureAtPixel(event.pixel, function(feature){
// 为移动到的feature发送自定义的mousemove消息
feature.dispatchEvent({type: 'mousemove', event: event});
});
});
// 为feature1注册自定义事件mousemove的监听
feature1.on('mousemove', function(event){
// 修改feature的样式为半径100像素的园,用蓝色填充
this.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 100,
fill: new ol.style.Fill({
color: 'blue'
})
})
}));
});
</script>
</body>
</html>
代码中有详细的注释,再辅以API文档,相信应该看懂绝大部分的代码,当然最关键的代码在于最后的两个事件监听,在这两段代码中,可能下面这句不是很明白:
feature.dispatchEvent({type: 'mousemove', event: event});
dispatchEvent
的参数具有type
和event
属性,必须这样构造吗?在回答这个问题之前,需要先看一下API文档,发现参数类型为goog.events.EventLike
,说明它其实用的是google的closure库来实现的,通过closure库的源码我们知道,派发的事件如果是一个对象,那么必须包含type
属性,用于表示事件类型。其他的属性可以自由定义,比如此处定义了event
属性,并设置对应的值,为的是让鼠标事件传递给feature1
的监听函数。
dispatchEvent
的参数会被原封不动的传递给事件响应函数,对应代码
feature1.on('mousemove', function(event){
里function
的参数event
,可以通过调试窗口看到此处的event
和dispatchEvent
的参数是一样的。注意事件名称是可以自定义的,只要派发和监听使用的事件名称是一致的就可以。
除了可以通过dispatchEvent({type: 'mousemove', event: event})
这种形式派发一个事件之外,还可以通过dispatchEvent('mousemove')
这中形式直接发送mousemove
事件。有兴趣的同学可以自行验证。
到此,我们就完成了自定义事件的三件事:定义事件类型,派发事件,监听事件。
使用自定义事件会带来下面几个好处:
- 灵活控制,对比一下官网例子和这个例子,就能看出一二。
- 能更好地满足业务需要,适当地进行扩展。
- 采用更统一的事件处理框架。
有很多同学为了处理多个Feature
的鼠标事件,可能会为Map
注册很多次pointermove
事件监听,从而会导致性能降低,这种做法是不可取的。如果采用上面这种自定义事件的处理方式,就只用注册一次,让真正有需要的Feature
通过注册事件响应来处理业务,从而还能简化处理逻辑。
上面这个例子鼠标移到红色圆后变蓝色,但是移出圆后,还是蓝色,能不能再移出圆的时候再变成红色呢? 试试用自定义事件的方式再改进一下,实现mousein
和mouseout
两种事件。