美文网首页
ArcGIS API 4.x实现SceneView的卷帘功能

ArcGIS API 4.x实现SceneView的卷帘功能

作者: A_slow_sparrow | 来源:发表于2021-05-09 12:57 被阅读0次
<!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()初始化的正在进行的视图动画。
      原文链接:https://blog.csdn.net/qq_38737845/article/details/102833766

相关文章

网友评论

      本文标题:ArcGIS API 4.x实现SceneView的卷帘功能

      本文链接:https://www.haomeiwen.com/subject/xpbmdltx.html