243 lines
7.4 KiB
Markdown
243 lines
7.4 KiB
Markdown
|
---
|
|||
|
title: Openlayers卷帘
|
|||
|
date: 2020-12-25
|
|||
|
author: ac
|
|||
|
sidebar: false
|
|||
|
tags:
|
|||
|
- Openlayers
|
|||
|
categories:
|
|||
|
- GIS
|
|||
|
---
|
|||
|
|
|||
|
## Openlayers卷帘
|
|||
|
|
|||
|

|
|||
|
|
|||
|
“卷帘”功能可以分为两部分:
|
|||
|
|
|||
|
1. 控件元素的拖动。
|
|||
|
2. 地图图层渲染流程的控制,对上层图层进行裁剪。
|
|||
|
|
|||
|
首先我们需要一个DOM组件作为拖动的“卷帘”,将其放在`map`容器内部,避免影响页面其他要素。
|
|||
|
|
|||
|
```html
|
|||
|
<div id="map" class="map">
|
|||
|
<div id="swipeContainer">
|
|||
|
<div id="swipeDiv">
|
|||
|
<div class="handle"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
在设置样式时,将map元素设为相对定位,则其内部的子元素的绝对定位将参考map元素的位置,即“卷帘”的拖动位置将参考map元素进行。根据“卷帘”组件拖动的位置,利用地图渲染相关的事件,对图层进行处理实现卷帘效果。
|
|||
|
|
|||
|
### 地图渲染
|
|||
|
|
|||
|
地图渲染相关的事件:
|
|||
|
|
|||
|
- `precompose`:未渲染,layers或layer被渲染前触发
|
|||
|
- `postcompose`:layer图层渲染后触发,可以在事件源中获取用于渲染图层的上下文context,也可以得到canvas元素。图层的渲染其实就是将从数据源中请求得到的矢量数据,通过计算转换绘制在content上下文中或将请求得到的图片嵌入到上下文中相应的位置
|
|||
|
- `postrender`:map被渲染完成后触发
|
|||
|
|
|||
|
当初始化`map`实例时,会先触发`map`的`precompose`事件,再依次触发`layers`集合中每个图层的`precompose`事件和`postcompose`事件,在所有layer都渲染完成后,才会触发`map`的`postrender `事件。
|
|||
|
|
|||
|
### 实现原理
|
|||
|
|
|||
|
卷帘实现原理:在客户端监听上层图层的`postcompose`事件,获取渲染的上下文content,对其进行裁剪,让下层图层数据可见。
|
|||
|
|
|||
|
```html
|
|||
|
<!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>
|
|||
|
```
|
|||
|
|