本篇博客主要是在ArcGIS API 4.12的環境下, 實現捲簾功能
話不多說, 上代碼
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
padding: 0;
margin: 0;
height: 100%;
}
/* 垂直分割線 */
.vertical_line{
position: absolute;
top: 12%;
left: 45%;
bottom: 4.2%;
z-index: 1;
float:left;
width: 10px;
background-color: rgba(50,50,50,0.75);
user-select: none;
}
/* 設置存放地圖div的樣式 */
#esri_view_div {
position: absolute;
top: 12%;
bottom: 4.5%;
left: 2.4%;
right: 2.4%;
}
/* 圓按鈕 */
.circle {
width: 30px;
height: 30px;
background-color: rgba(50,50,50,0.75);
border-radius: 50%;
position: absolute;
top: 40%;
left: 40%;
bottom: 4.2%;
z-index: 2;
margin-left: -10px;
user-select: none;
}
#esri_view_div_swipe {
position: absolute;
top: 12%;
bottom: 4.5%;
left: 2.4%;
right: 2.4%;
z-index: -1;
}
/* 左箭頭 */
.triangle-left {
width: 0;
height: 0;
border-top: 4px solid transparent;
border-right: 7px solid white;
border-bottom: 4px solid transparent;
position: absolute;
top: 40%;
margin-left: -8px;
margin-top: 12px;
z-index: 3;
user-select: none;
}
/* 右箭頭 */
.triangle-right {
width: 0;
height: 0;
border-top: 4px solid transparent;
border-left: 7px solid white;
border-bottom: 4px solid transparent;
position: absolute;
top: 40%;
margin-left: 10px;
margin-top: 12px;
z-index: 3;
user-select: none;
}
</style>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.12/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.12/"></script>
<script>
require([
"esri/Map",
"esri/views/SceneView",
"esri/core/watchUtils",
'dojo/dom-style',
], function(Map, SceneView, watchUtils, EsriDomStyle) {
var esriContainerDiv = 'esri_view_div'
var esriSwipeContainerDiv = 'esri_view_div_swipe'
var map = new Map({
basemap: "satellite"
});
var map2 = new Map({
basemap: "osm"
});
var view1 = new SceneView({
container: "esri_view_div",
map: map
});
var view2 = new SceneView({
container: esriSwipeContainerDiv,
map: map2,
});
view1.ui.remove(['attribution', 'zoom', 'navigation-toggle', 'compass'])
view2.ui.remove(['attribution', 'zoom', 'navigation-toggle', 'compass'])
var isSlitLineDragging = false // 分割線移動狀態
document.getElementById('swipe_split_box').onmousedown = function () {
isSlitLineDragging = true
}
document.getElementById('swipe_split_box').onmouseup = function() {
isSlitLineDragging = false
}
/**
* 分割線移動事件
* @param {Object} e 分割線移動事件對象
*/
function pointMove(e) {
e.stopPropagation()
updateMapSwipeLocation(e.x)
};
/**
* 更新捲簾地圖容器展開位置
* @param {Number} location 當前的位置
* @param {Boolean} isInit 是否是初始化
*/
function updateMapSwipeLocation(location, isInit) {
const swipeMap = document.getElementById(esriSwipeContainerDiv).getBoundingClientRect()
const offsetX = location
if (isSlitLineDragging || isInit) {
EsriDomStyle.set(esriSwipeContainerDiv, 'z-index', '1')
EsriDomStyle.set(esriSwipeContainerDiv, 'clip', 'rect(0px,' + offsetX + 'px, ' + swipeMap.height + 'px,0px)')
EsriDomStyle.set('vertical_line', 'left', (offsetX - 5 + (swipeMap.width * 0.024)) + 'px ')
EsriDomStyle.set('swipe_circle', 'left', (offsetX - 5 + (swipeMap.width * 0.024)) + 'px ')
EsriDomStyle.set('swipe_triangle_left', 'left', (offsetX - 5 + (swipeMap.width * 0.024)) + 'px ')
EsriDomStyle.set('swipe_triangle_right', 'left', (offsetX - 5 + (swipeMap.width * 0.024)) + 'px ')
}
};
/**
* 同步兩個視圖容器
* @param {Object} view 視圖容器
* @param {Object} others 其它的視圖容器
* @return {Object} 監聽事件
*/
function synchronizeView(view, others) {
others = Array.isArray(others) ? others : [others]
let viewpointWatchHandle
let viewStationaryHandle
let otherInteractHandlers
let scheduleId
const clear = function() {
if (otherInteractHandlers) {
otherInteractHandlers.forEach(function(handle) {
handle.remove()
})
}
viewpointWatchHandle && viewpointWatchHandle.remove()
viewStationaryHandle && viewStationaryHandle.remove()
scheduleId && clearTimeout(scheduleId)
otherInteractHandlers = viewpointWatchHandle = viewStationaryHandle = scheduleId = null
}
const interactWatcher = view.watch('interacting, animation', (newValue) => {
if (!newValue) {
return
}
if (viewpointWatchHandle || scheduleId) {
return
}
// start updating the other views at the next frame
scheduleId = setTimeout(function() {
scheduleId = null
viewpointWatchHandle = view.watch('viewpoint', (newValue) => {
others.forEach(function(otherView) {
otherView.viewpoint = newValue
})
})
}, 0)
const that = this
// stop as soon as another view starts interacting, like if the user starts panning
otherInteractHandlers = others.map(function(otherView) {
return watchUtils.watch(otherView, 'interacting,animation',
(value) => {
if (value) {
clear()
}
}
)
})
// or stop when the view is stationary again
viewStationaryHandle = watchUtils.whenTrue(view, 'stationary', clear)
})
return {
remove: function() {
this.remove = function() {}
clear()
interactWatcher.remove()
},
}
};
/**
* 同步兩個視圖容器入口函數
* @param {Object} views 多個視圖容器
* @return {Object} 移除事件
*/
function synchronizeViews(views) {
let handles = views.map(function(view, idx, views) {
const others = views.concat()
others.splice(idx, 1)
return synchronizeView(view, others)
})
return {
remove: function() {
this.remove = function() {}
handles.forEach(function(h) {
h.remove()
})
handles = null
},
}
};
view1.on('pointer-move', (e) => {
pointMove(e)
})
view2.on('pointer-move', (e) => {
pointMove(e)
})
// 設置初始位置
const swipeMap = document.getElementById(esriSwipeContainerDiv).getBoundingClientRect()
updateMapSwipeLocation(swipeMap.width * 0.5, true)
// 同步視圖
synchronizeViews([view1, view2])
})
</script>
</head>
<body>
<div id='esri_view_div'></div>
<div id='esri_view_div_swipe'></div>
<div id="swipe_split_box">
<div id="vertical_line" class="vertical_line"></div>
<div id="swipe_circle" class="circle"></div>
<div id="swipe_triangle_left" class="triangle-left"></div>
<div id="swipe_triangle_right" class="triangle-right"></div>
</div>
</body>
</html>
總結
捲簾實現主要分爲兩個部分
1. 視圖容器Div的Clip
1) 開始捲簾, 即使用最關鍵的功能: css的clip屬性, 將視圖容器的div進行切分以實現捲簾
2) 初始化視圖容器esriSceneView和地圖分割線的位置, 使之出現在中間
3) 在按下分割線的時候, 使之進入拖動狀態, 拖動狀態時計算當前鼠標位置, 以及根據計算到的位置進行設置視圖容器的clip: rect屬性
關於clip: rect的幾個參數說明https://www.zhangxinxu.com/study/201103/css-rect-demo.html
4) 結束拖動, 關閉拖動狀態
2. 兩個視圖容器的聯動
1) 通過兩個view互相監聽interacting, animation兩個屬性,
當着兩個屬性變化的時候獲得該視圖容器的viewpoint, 同步設置另一個視圖容器的viewpoint
注: interacting : boolean, 指示是否與視圖交互(例如平移時)。
animation: ViewAnimation, 表示由goTo()初始化的正在進行的視圖動畫。