Openlayers+vue实现地图卷帘功能
地图卷帘功能主要是通过控件元素的拖动 ,对地图图层渲染流程的控制。这里附上两种实现方法供大家参考。
卷帘效果预览
代码实现
这里的卷帘效果加载了OSM和天地图作为底图,若无天地图TK去申请即可,点击申请。
- 方法一(卷帘条拖动)
<template>
<div class="app-container">
<div ref="mapEl" id="map" class="map">
<div ref="sliderEl" id="swipeContainer" :style="{left:left+'px'}" class="slider" @mousedown="onMouseDown">
<div id="swipeDiv" @mousedown="onMouseDown">
<div class="handle"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import OSM from 'ol/source/OSM'
export default {
data() {
return {
map: null,
baseLayers: [],
topLayer: null,
mapWidth: 0,
minClientLeft: 0,
maxClientLeft: 0,
left: 0,
clientleft: 0,
isMove: false
};
},
created() {
this.createdBaseLayer();
},
mounted() {
this.mapInit()
// 拖动事件注册在 doucment 上面,防止拖拽过程中鼠标丢失情况
document.addEventListener('mousemove', this.onMouseMove)
document.addEventListener('mouseup', this.onMouseUp)
},
beforeDestroy() {
document.removeEventListener('mousemove', this.onMouseMove)
document.removeEventListener('mouseup', this.onMouseUp)
},
methods: {
createdBaseLayer() {
const vecLayer = new TileLayer({
source: new XYZ({
url: "http://t2.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=YOUR_TK"
}),
title: '矢量底图1'
});
this.topLayer = new TileLayer({
source: new OSM(),
title: '矢量底图2'
});
this.baseLayers.push(vecLayer)
this.baseLayers.push(this.topLayer)
this.topLayer.on('prerender', (e) => {
const ctx = e.context
const width = ctx.canvas.width * (this.left / this.mapWidth)
ctx.save()
ctx.beginPath()
ctx.rect(width, 0, ctx.canvas.width - width, ctx.canvas.height)
ctx.clip()// 裁剪
})
// 请求完成,渲染
this.topLayer.on('postrender', function (e) {
const ctx = e.context
ctx.restore()
})
},
mapInit() {
// 初始化时,卷帘处在地图中间位置,根据地图宽高计算
this.mapWidth = this.$refs.mapEl.offsetWidth
this.left = this.mapWidth / 2
// 保证卷帘始终在地图内,并两边留一部分
this.minClientLeft = this.$refs.mapEl.offsetLeft + 66
this.maxClientLeft = this.minClientLeft + this.mapWidth - 50
this.map = new Map({
target: 'map',
layers: this.baseLayers,
view: new View({
projection: 'EPSG:4326',
center: [119.923451, 30.385335],
zoom: 10,
maxZoom: 19,
minZoom: 5
})
});
},
onMouseDown(e) {
this.isMove = true
this.clientleft = e.clientX
this.$refs.sliderEl.style.cursor = 'move'
},
onMouseMove(e) {
if (!this.isMove) return
if (e.clientX < this.minClientLeft || e.clientX > this.maxClientLeft) return
const mx = e.clientX - this.clientleft
this.clientleft = e.clientX
this.left = this.left + mx
this.map.render()
},
onMouseUp() {
this.isMove = false
this.$refs.sliderEl.style.cursor = 'default'
},
}
}
;
</script>
<style>
.map {
height: 500px;
width: 100%;
position: relative;
}
#swipeContainer {
position: absolute;
opacity: 0.8;
width: 0.625rem;
height: 100%;
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: 'SimHei';
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;
}
</style>
- 方法二(底部滑轮拖动)
<template>
<div class="app-container">
<div ref="map" id="map" class="map"></div>
<input id="swipe" type="range" ref="swipeInput"/>
</div>
</template>
<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import OSM from 'ol/source/OSM'
import {getRenderPixel} from 'ol/render';
export default {
data() {
return {
currentMap: null,
leftLayer: null,
rightLayer: null,
map: null,
};
},
mounted() {
this.mapInit()
},
methods: {
mapInit() {
this.init("map");
this.mapSwipe();
},
init(mapDiv) {
let leftLayer = new TileLayer({
source: new XYZ({
url: "http://t2.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=YOUR_TK"
}),
title: '矢量底图1'
});
let rightLayer = new TileLayer({
source: new OSM(),
title: '矢量底图2'
});
let map = new Map({
layers: [leftLayer, rightLayer],
target: mapDiv,
view: new View({
center: [0, 0],
zoom: 3
})
});
this.leftLayer = leftLayer;
this.rightLayer = rightLayer;
this.map = map;
},
mapSwipe() {
let swipe = this.$refs["swipeInput"];
this.swipeRender(this.map);
let map = this.map;
swipe.addEventListener('input', function () {
map.render();
}, false);
},
swipeRender(map) {
this.rightLayer.on('prerender', function (event) {
let ctx = event.context;
let mapSize = map.getSize();
let width = mapSize[0] * (swipe.value / 100);
let tl = getRenderPixel(event, [width, 0]);
let tr = getRenderPixel(event, [mapSize[0], 0]);
let bl = getRenderPixel(event, [width, mapSize[1]]);
let br = getRenderPixel(event, mapSize);
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();
ctx.clip();
});
this.rightLayer.on('postrender', function (event) {
let ctx = event.context;
ctx.restore();
});
}
}
};
</script>
<style>
#map {
width: 100%;
height: 500px;
}
#swipe {
width: 100%;
}
</style>
参考链接
- https://blog.csdn.net/weixin_38670190/article/details/111681063
- https://www.cnblogs.com/zhurong/p/16427491.html