# Openlayers卷帘
“卷帘”功能可以分为两部分:
- 控件元素的拖动。
- 地图图层渲染流程的控制,对上层图层进行裁剪。
首先我们需要一个DOM组件作为拖动的“卷帘”,将其放在map
容器内部,避免影响页面其他要素。
<div id="map" class="map">
<div id="swipeContainer">
<div id="swipeDiv">
<div class="handle"></div>
</div>
</div>
</div>
1
2
3
4
5
6
7
2
3
4
5
6
7
在设置样式时,将map元素设为相对定位,则其内部的子元素的绝对定位将参考map元素的位置,即“卷帘”的拖动位置将参考map元素进行。根据“卷帘”组件拖动的位置,利用地图渲染相关的事件,对图层进行处理实现卷帘效果。
# 地图渲染
地图渲染相关的事件:
precompose
:未渲染,layers或layer被渲染前触发postcompose
:layer图层渲染后触发,可以在事件源中获取用于渲染图层的上下文context,也可以得到canvas元素。图层的渲染其实就是将从数据源中请求得到的矢量数据,通过计算转换绘制在content上下文中或将请求得到的图片嵌入到上下文中相应的位置postrender
:map被渲染完成后触发
当初始化map
实例时,会先触发map
的precompose
事件,再依次触发layers
集合中每个图层的precompose
事件和postcompose
事件,在所有layer都渲染完成后,才会触发map
的postrender
事件。
# 实现原理
卷帘实现原理:在客户端监听上层图层的postcompose
事件,获取渲染的上下文content,对其进行裁剪,让下层图层数据可见。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 100%;
position: relative;
}
#swipeContainer {
position: absolute;
opacity: 0.8;
width: 0.625rem;
height: 100%;
/* margin: 0 auto; */
top: 0;
left: 50%;
background-color: rgba(50, 50, 50, 0.75);
cursor: col-resize;
z-index: 2;
}
#swipeContainer:hover {
opacity: 0.5;
}
#swipeDiv {
border: solid 0.5px #ffffff;
height: 100%;
width: 0px;
margin: 0 auto;
}
#swipeDiv .handle {
width: 51px;
height: 24px;
margin-top: -12px;
margin-left: -20px;
top: 50%;
left: 0;
position: absolute;
z-index: 30;
font-family: "CalciteWebCoreIcons";
speak: none;
font-size: 12px;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
text-indent: 0;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: black;
color: white;
opacity: 0.6;
}
*,
*:before,
*:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.handle:before {
margin: 0 18px 0 5px;
content: "\0399\0399\0399";
width: 20px;
height: 24px;
line-height: 2;
}
.handle:after {
content: "\0399\0399\0399";
width: 20px;
height: 24px;
line-height: 2;
}
</style>
<script src="lib/ol.js"></script>
<title>卷帘</title>
</head>
<body>
<h2>卷帘(裁剪图层)</h2>
<div id="map" class="map">
<div id="swipeContainer">
<div id="swipeDiv">
<div class="handle"></div>
</div>
</div>
</div>
<script type="text/javascript">
window.onload = function () {
var map = initMap();
initSwipeDom(map);
swipeLayer(map);
}
function initMap() {
var bingKey = 'AmosL5A0GtVryl4sXNZm6U5EQMD6brAd5E8AJPGJf8AUU1saDYXDkb5CwQFijans';
var roadLayer = new ol.layer.Tile({
id:'road',
source: new ol.source.BingMaps({key: bingKey, imagerySet: 'Road'}),
name: "Bing道路图层"
});
var imageLayer = new ol.layer.Tile({
id:'aerial',
source: new ol.source.BingMaps({key: bingKey, imagerySet: 'Aerial'}),
name: "Bing影像图层"
});
var map = new ol.Map({
view: new ol.View({
center: [12614553, 2648165],
zoom: 12,
minzoom: 6,
maxzoom: 15,
}),
layers: [
roadLayer,
imageLayer
],
target: "map"
});
return map;
}
function initSwipeDom(map) {
var swipe = document.getElementById("swipeContainer");
var obj = {};
swipe.onmousedown = function(event) {
var e = event || window.event; //兼容IE浏览器
// 鼠标点击元素那一刻相对于元素左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
obj.diffX = e.clientX - this.offsetLeft;
document.onmousemove = function(event) {
var e = event || window.event;
var moveX = e.clientX - obj.diffX;
if (moveX < 0) {
moveX = 0
} else if (moveX > window.innerWidth - swipe.offsetWidth) {
moveX = window.innerWidth - swipe.offsetWidth
}
swipe.style.left = moveX + 'px';
//重新渲染图层
map.render();
};
document.onmouseup = function() {
this.onmousemove = null;
this.onmouseup = null;
}
};
}
function swipeLayer(map) {
var layers = map.getLayers();
var topLayer = layers.item(layers.getLength() - 1);
topLayer.on('precompose', function(event) {
var swipe = document.getElementById("swipeContainer");
var ctx = event.context;
//计算图层在canvas画布上需要显示的范围
var mapSize = map.getSize();
var height = event.context.canvas.height;
var width = event.context.canvas.width;
var swipeWidth = swipe.offsetLeft*width/mapSize[0];
var tl = [swipeWidth,0];
var tr = [width,0];
var bl = [swipeWidth,height];
var br = [width,height];
ctx.save();
//绘制裁剪路径
ctx.beginPath();
ctx.moveTo(tl[0], tl[1]);
ctx.lineTo(bl[0], bl[1]);
ctx.lineTo(br[0], br[1]);
ctx.lineTo(tr[0], tr[1]);
ctx.closePath();
//裁剪,裁剪路径以外的部分不会绘制在canvas上
ctx.clip();
});
topLayer.on('postcompose', function(event) {
var ctx = event.context;
ctx.restore();
});
}
</script>
</body>
</html>
1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191