meface/docs/article/gis/openlayers/ol_swipe.md

243 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Openlayers卷帘
date: 2020-12-25
author: ac
sidebar: false
tags:
- Openlayers
categories:
- GIS
---
## Openlayers卷帘
![](./images/swipe.gif)
“卷帘”功能可以分为两部分:
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>
```