ArcGIS API for JavaScript 4.2學習筆記[7] 鷹眼(縮圖的實現及非同步處理、Promise、回呼函數、監聽的筆記)

來源:互聯網
上載者:User

標籤:屬性   esri   接受   views   放大縮小   art   賦值   mapview   screen   

文前說明:關於style就是頁面的css暫時不做評論,因為官方給的例子的樣式實在太簡單了,照抄閱讀即可。

這篇文章有著大量AJS 4.x版本添加的內容,如監聽watch、Promise對象、回呼函數、非同步處理等內容,原理性的東西我會在文末解釋,各位看官不用擔心看不懂,我盡量用通俗的語言解釋這些。

慣例,如果不習慣從頭看到尾,可以直接跳到後面看總結。

大家應該看過商業地圖的縮圖功能吧?以度娘地圖為例,在使用街景地圖的時候,左下角會出現一個地點一樣的2D小地圖:

這個就是鷹眼功能的應用,在很多案頭軟體中如Erdas、Envi,鷹眼是很常見的。

//如果以下超連結日後更新了4.3或更高版本,請自行尋找4.2的sample配合本文學習~

這次就解讀2D overview map in SceneView這個例子。

原始碼:點我

其實就是一個2D的MapView在3D的SceneView的顯示而已,關鍵就在資料的同步,官方指出了watch()方法是關鍵。

話不多說,先上最終:

結構大概就是,大的DIV裡放SceneView,小的DIV裡放MapView。

小的DIV裡又有一個黑色的地區來標識當前SceneView的地區。小的DIV的widgets被移除。

html代碼為:

<body>  <div id="viewDiv"></div>  <div id="overviewDiv">    <div id="extentDiv"></div>  </div></body>

老樣子,require給出引用(以前都叫第一個字串數組參數,為了省事,以後直接叫引用了)

require(    [      "esri/Map",      "esri/views/SceneView",      "esri/views/MapView",      "esri/core/watchUtils",      "dojo/dom",      "dojo/promise/all",      "dojo/domReady!"    ],    function(Map, SceneView, MapView, watchUtils, dom, all){        //你的代碼    });

重點應該是:

view的watch()方法、watchUtils的when方法、view的toScreen方法、view的extent屬性、view的then方法。

 

既然有兩個view(DIV),那麼肯定要有兩份map(資料)。

所以第二參數(以前的文章叫函數參數,之後都叫第二參數)先將map和view定義如下:

      var mainMap = new Map({        basemap: "hybrid",        ground: "world-elevation"      });      var overviewMap = new Map({        basemap: "osm"      });      var mainView = new SceneView({        container: "viewDiv",        map: mainMap      });      var mapView = new MapView({        container: "overviewDiv",        map: overviewMap      });

mainMap、mainView是3D的,overviewMap、mapView是2D的。

當然,我們看到的2D的小地圖是沒有放大縮小那些控制項的,只需1行代碼,就可以置空那些控制項。

      mapView.ui.components = [];

查閱API,可以知道ui屬性是DefaultUI類,DefaultUI繼承自UI類。components是字串數組,若賦值為空白數組則清空。相應的,DefaultUI類有remove和empty方法可以清除控制項,就不細說了。

為了便於操作,把目前範圍的DIV“extendDiv”的DOM元素擷取為變數:

var extentDiv = dom.byId("extentDiv");

 

以上就完成了準備部分。

接下來,資料載入完成後,就要對2D的地圖和3D的地圖進行“同步”了,需要用到兩個view的then方法。

then()方法是Promise對象的特有方法,而Promise是什麼暫時無需瞭解,只要知道在AJS 4.x中Promise是一個很重要的東西。

而且,MapView和SceneView類都繼承了Promise類。不僅如此,AJS 4.x中很多方法返回的都是Promise對象。

先看看mainView(3D視圖)的then方法看看它做了什麼:

mainView.then(function() {  mainView.goTo({    center: [7, 46],    scale: 200000,    heading: 35,    tilt: 60  },
 { animate: true, duration: 100000 })});

很好,它接受了一個參數,類型是方法。這個匿名方法幹了什麼呢?這不就是上一篇文章裡說的縮放動畫嘛!(goTo)跳過,看mapView(2D視圖)的then方法看它做了什麼:

mapView.then(function() {  mainView.watch("extent", updateOverviewExtent);  mapView.watch("extent", updateOverviewExtent);  watchUtils.when(mainView, "stationary", updateOverview);  function updateOverview() {    mapView.goTo({      center: mainView.center,      scale: mainView.scale * 2 * Math.max(mainView.width /        mapView.width,        mainView.height / mapView.height)    });  }  function updateOverviewExtent() {    var extent = mainView.extent;    var bottomLeft = mapView.toScreen(extent.xmin, extent.ymin);    var topRight = mapView.toScreen(extent.xmax, extent.ymax);    extentDiv.style.top = topRight.y + "px";    extentDiv.style.left = bottomLeft.x + "px";    extentDiv.style.height = (bottomLeft.y - topRight.y) + "px";    extentDiv.style.width = (topRight.x - bottomLeft.x) + "px";  }});
mapView的then方法

很長的樣子。

我慢慢解釋。

仍然是接受一個方法作為參數(為什麼then接受的參數那麼奇怪?文末會解釋的)

//題外話:在javascript裡頭傳函數/方法是很常見的,函數/方法是js的一種變數類型,在C/C++裡頭可以傳遞函數指標,在C#裡頭可以傳遞委託變數。

這個方法裡有兩個方法,命名為 updateOverview 和 updateOverviewExtent,我們根據這兩個方法把這個then方法的代碼拆開看,發現watch和watchUtils.when是跟這兩個方法配對的。

即:

//兩個視圖都與updateOverviewExtent方法綁定mainView.watch("extent", updateOverviewExtent);mapView.watch("extent", updateOverviewExtent);function updateOverviewExtent() {  var extent = mainView.extent;  var bottomLeft = mapView.toScreen(extent.xmin, extent.ymin);  var topRight = mapView.toScreen(extent.xmax, extent.ymax);  extentDiv.style.top = topRight.y + "px";  extentDiv.style.left = bottomLeft.x + "px";  extentDiv.style.height = (bottomLeft.y - topRight.y) + "px";  extentDiv.style.width = (topRight.x - bottomLeft.x) + "px";}

查閱API,得知視圖的父類Accessor就支援watch方法了。值得一提的是,為了實現監聽變化,AJS4.x版本專門提供了watch方法代替了以前的舊方法。

watch的用法是:

對象.watch("該需要監聽的屬性名稱", 屬性變化後需要執行的回呼函數);

即某對象監聽了它的某個屬性後,這個屬性一旦發生改變,就會去執行某些代碼。

在本例中,需要監聽的是兩個view對象的extent(範圍)屬性,一旦extent發生變化,那麼updateOverviewExtent()方法就會被執行。

updateOverviewExtent()方法的大概意思就是:擷取3D視圖的範圍->擷取2D視圖的對角線兩個角點->更改2D視圖上方的地區框的DOM元素的尺寸屬性(top、left、height、width)

光改變地區框是不行的,還要改變2D地圖的範圍。

watchUtils.when(mainView, "stationary", updateOverview);function updateOverview() {  mapView.goTo({    center: mainView.center,    scale: mainView.scale * 2 * Math.max(mainView.width /      mapView.width,      mainView.height / mapView.height)  });}

watchUtils這個對象,是位於esri/core/watchUtils模組下的一個類。

它代表的含義是:監聽某個對象,當這個對象的某個屬性是true時,執行給定的方法。

查閱API得知,這個類提供了when這個靜態方法,when方法的意義是:

所以,在本例中,意思就是:

當mainView這個3D視圖對象的"stationary"屬性是true時,重新整理mapView這個2D視圖對象。

重新整理2D視圖對象主要用的是上一篇中說到的goTo()方法,本例只指定了center和scale這兩個屬性群組成的Object匿名對象。

SceneView類的stationary屬性是布爾類型的,意義是當前視圖是否已經靜止(一般視圖會由滑鼠拖拽或者goTo()方法產生動態效果,一旦停止下來,stationary就會變成true)

 

總結一下。

這個例子大概思路就是:

·先執行個體化兩個map和兩個view,對3D的mainView在建立完成後使用then()方法縮放到指定位置。

·其中,對2D的mapView建立完成後使用then()方法,分別監聽兩個view的extent屬性,還監聽3D視圖的stationary屬性。

·當extent屬性發生變化時,2D視圖上方範圍框先進行變化,然後2D地圖緊隨變化。

·當3D視圖靜止下來後,重新整理2D視圖。

 

監聽還算比較好理解,需要注意的不多,注意到watch和watchUtils.when這兩個方法返回的都是WatchHandle對象。待以後研究多了監聽後,再仔細看看別的監聽方法。

痛點就在於then方法。

 

痛點。

then()方法怎麼來的?這要從ES6(全名ECMAScript 2015)的新規範Promise對象說起。ECMAScript是JavaScript的標準,JS是ES的實現。

Promise是什嗎?這個東西說複雜也很複雜,它是:

為了處理非同步作業多層回呼函數的寫法枯燥、難以閱讀維護而產生的,由CommonJS社區發起的一個新規範的類。

最顯著的特徵是它執行個體化的對象都有then()和catch()方法(PromiseA+規範?好像是)

在AJS中,繼承了Promise的類有:

全部的Layers

MapView、SceneView、LayerView

ViewAnimation

能返回Promise對象的類數不勝數。

所以說,為什麼要用Promise?

這又要從非同步作業說起了。

————

在AJS 4.x中,資料(Map類)和視圖(View類)是分開的,3.x版本繪圖渲染是Map自己完成的。

由於View視圖類被分離開,繪圖邏輯就成了它的主要功能。當然,繪圖不會很快,往往有一個過程,尤其是超大資料量的繪圖的時候會有一個比較長的等待過程。

所以,在JS裡,較長的處理會丟給非同步處理(就是同時進行好幾個操作)

但是但是,我們知道JS是單線程的,它是怎麼處理非同步處理的呢?簡單說說,JS的非同步處理其實是個“偽非同步”,是先完成同步代碼才執行非同步代碼的。

通常,非同步代碼會做一些計算量比較大的事情,而同步代碼則做一些不怎麼耗時間的初始化工作。就是說

同步代碼花少量的時間去初始化一些事情,其間有n個非同步任務丟給非同步隊列。當同步程式碼完成初始化後(時間短),非同步代碼開始按順序執行。

比如:介面的構建交給同步代碼,而其間有n個後台資料交換、處理、計算的任務,就丟給非同步隊列去準備。當介面構造好(時間往往很短,幾乎是秒速),非同步代碼就在後面開始執行。

這先看到的介面會讓體驗好很多,如果非同步代碼(就是耗時比較大的任務)放在同步代碼裡執行,那麼由於同步的性質,必須等待這些耗時大的任務執行完成才能繼續往下走(js的特點,單線程)

【在本例中】

初始化view,我不知道在雲端是怎麼啟動並執行(因為我用的是CDN來運行AJS程式),但是我知道view的執行個體化肯定是用了非同步作業。

先完成網頁的載入(出現3D地球和2D地圖,同步),再進行視圖的渲染(山體拔高等,非同步)。

sometimes,非同步作業當然會有一個結果,比如非同步在後面花好長時間算出個矩陣,但是同步代碼已經結束了,非同步任務丟過去的時候結果還沒出來,怎麼擷取它?

我們可以用一個方法去擷取它。這個方法,古時候叫回呼函數。

在沒有Promise類的時候,通常用回呼函數這種辦法實現(也能用事件、監聽)非同步是很正常的一種。

但是當回呼函數本身也是個非同步作業的時候,就會顯得暈頭轉向。

非同步第一層,有結果要用回呼函數返回給同步代碼->回呼函數是第二層,這個回呼函數裡頭需要用二級回呼函數返回結果給第一層->……

舉個例子:

我是領導,我現在有兩件事:有個事兒要做,和喝茶。

這兩件事不衝突,雖然這個事兒很無聊,耗時大(如文字錄入)。

所以我把這個事兒丟給經理(非同步第一層),我繼續喝茶(同步)

非同步第一層就是經理要做這個事,但是這個事情絕大部分是無聊的,最後的整理比較簡單。

所以經理就把這個無聊的部分丟給職員(非同步第二層),等待職員把這部分做完的同時,也去喝茶(同步)。

於是,職員的結果就是二級回呼函數,職員把結果完成後,“回調”給經理。

經理拿著職員的結果整理好,“回調”給領導。(第二層非同步完成)

此時領導茶已經喝完了(同步完成),而任務也完成了(第一層非同步完成)。

這裡如果用老的寫法將會非常的煩,如果用Promise的then寫法就是

領導要做事兒.then(function(){讓經理去做})

.then(function(){讓職工做});

鏈式寫法,簡單,容易看,也容易維護。

then裡面的function就是回呼函數,告訴非同步任務完成後,要怎麼處理非同步結果的一段代碼。

最後看看then方法的文法:

then(function resolve, function rejected);

我們一般只用前一個參數,即非同步成功要怎麼處理。而後一個參數是非同步任務處理失敗後要做什麼。

甚至AJS官方還給出了處理中要做什麼的第三個參數...這個就不說那麼多了。

——

大概清楚是這麼個過程後,我們知道View對象是Promise對象(繼承),而且有非同步作業的過程。

所以,mainView.then(function(){...});的意義就是

當3D視圖在伺服器端非同步作業成功後,使用goTo()縮放到指定的位置。

 

文末,我還想說說監聽,監聽在AJS 3.x版本裡是通過事件完成的,而AJS 4.x全新使用了watch一派寫法。有關這些可以參考AJS 4.2的Guide文檔。

最後的最後,關於非同步和回呼函數部分我也是學了一天后才給出的模糊定義,希望大家能看懂吧...我也不是很能理解,官方給的多層then()是這樣的:

出處:點我

then裡頭當然是方法,無參的。只有子一層的結果完成的時候,父一層的then才能憑藉子一層的結果的回調完成非同步。

給一些我閱讀中覺得不錯的對非同步、回呼函數講解的文章:

大白話講解Promise

ECMAScript 6入門

Javascript非同步編程的4種方法

百度知道-JS中回呼函數怎麼理解

Javascript非同步編程之—非同步原理

JS中的回呼函數,以及ES6中通過promise處理回調

ArcGIS API for JavaScript 4.2學習筆記[7] 鷹眼(縮圖的實現及非同步處理、Promise、回呼函數、監聽的筆記)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.