ArcGIS API for JavaScript 4.2学习笔记[7] 鹰眼(缩略图的实现及异步处理、Promise、回调函数、监听的笔记)
文前说明:关于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才能凭借子一层的结果的回调完成异步。
给一些我阅读中觉得不错的对异步、回调函数讲解的文章:
ArcGIS API for JavaScript 4.2学习笔记[7] 鹰眼(缩略图的实现及异步处理、Promise、回调函数、监听的笔记)的更多相关文章
- ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录
放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 4.2全线基础学习请点击[直达] 4.3及更高版本的补充学习请关注我的博客. ArcGIS API for JavaScr ...
- ArcGIS API for JavaScript 4.2学习笔记[1] 显示地图
ArcGIS API for JavaScript 4.2直接从官网的Sample中学习,API Reference也是从官网翻译理解过来,鉴于网上截稿前还没有人发布过4.2的学习笔记,我就试试吧. ...
- ArcGIS API for JavaScript 4.2学习笔记[5] 官方API大章节概述与内容转译
内容如上,截图自ESRI官网,连接:ArcGIS API for JavaScript 4.2 [Get Started] 类似于绪论一样的东西,抽取了最需要关注的几个例子.如:加载Map和View, ...
- ArcGIS API for JavaScript 4.2学习笔记[16] 弹窗自定义功能按钮及为要素自定义按钮(第五章完结)
这节对Popups这一章的最后两个例子进行介绍和解析. 第一个[Popup Actions]介绍了弹窗中如何自定义工具按钮(名为actions),以PopupTemplate+FeatureLayer ...
- ArcGIS API for JavaScript 4.2学习笔记[30] 点和线高程查询(第八章完结)
终于到最后一篇了,可喜可贺. 本例先说明了如何进行单点的高程差分析,然后说明了道路的起伏分析.前者很直观地比较了两个年份的高程数据之间的差值,体现山区的高程变化(有啥用啊?)后者,一条路上的起点终点起 ...
- ArcGIS API for JavaScript 4.2学习笔记[27] 网络分析之最短路径分析【RouteTask类】
要说网页端最经典的GIS应用,非网络分析莫属了. 什么?你没用过?百度高德谷歌地图的路线分析就是活生生的例子啊!只不过它们是根据大实际背景优化了结果显示而已. 这个例子使用RouteTask进行网络分 ...
- ArcGIS API for JavaScript 4.2学习笔记[10] 2D添加指北针widget、视图保存、视图padding(第二章完结)
这几个例子是第二章除了入门之外比较简单的几个,就做个合集,把最核心的代码(第二参数)和 引用放上来即可,不作多解释. 2D地图添加指北针widget 2D地图一般修正方向为正北方就需要这个widget ...
- ArcGIS API for JavaScript 4.2学习笔记[14] 弹窗的位置、为弹窗添加元素
这一节我们来看看弹窗的位置和弹窗上能放什么. 先一句话总结: 位置:可以随便(点击时出现或者一直固定在某个位置),也可以指定位置 能放什么:四种,文字.媒体(图片等).表格.附件. [Part I 位 ...
- ArcGIS API for JavaScript 4.2学习笔记[22] 使用【QueryTask类】进行空间查询 / 弹窗样式
上一篇写道,使用Query类进行查询featureLayer图层的要素,也简单介绍了QueryTask类的使用. 这一篇博文继续推进,使用Query类和QueryTask类进行空间查询,查询USA的著 ...
随机推荐
- 《java.util.concurrent 包源码阅读》10 线程池系列之AbstractExecutorService
AbstractExecutorService对ExecutorService的执行任务类型的方法提供了一个默认实现.这些方法包括submit,invokeAny和InvokeAll. 注意的是来自E ...
- JAVA提高十八:Vector&Stack深入分析
前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我们先看一个例子:栈就相当于一个很窄的木桶,我们往木桶里放东西,往外拿东 ...
- 机器学习算法 - 最近邻规则分类KNN
上节介绍了机器学习的决策树算法,它属于分类算法,本节我们介绍机器学习的另外一种分类算法:最近邻规则分类KNN,书名为k-近邻算法. 它的工作原理是:将预测的目标数据分别跟样本进行比较,得到一组距离的数 ...
- Libevent 反应堆的初始化
// 默认情况下new eventbase struct event_base * event_base_new(void) { struct event_base *base = NULL; //初 ...
- 浅谈IM(InstantMessaging) 即时通讯/实时传讯
一.IM简要概述 IM InstantMessaging(即时通讯,实时传讯)的缩写是IM,互动百科大致解释是一种可以让使用者在网络上建立某种私人聊天(chatroom)的实时通讯服务. 大部 ...
- chromium源码阅读--HTTP Cache
最近积累了一些关于HTTP缓存的知识,因此结合Chromium的实现总结一下,主要从如下2个分面: 1.HTTP缓存的基础知识 2.Chromium关于HTTP缓存的实现分析 一.HTTP缓存的基础知 ...
- dij洛谷电车
//Gang #include<iostream> #include<cstring> #include<algorithm> #include<cstdio ...
- for循环,for in和for of的区别
最近在写代码,总在被烦恼着到底遍历的时候到底该使用for循环,还是使用for in或者for of ,今天查了以下 ,写出来. 我们一般用for循环来遍历数组,因它可以按顺序的返回每一个索引的值或者 ...
- SQL-删除重复记录
前几日工作的时候,有个小需求,是要求删除一个表table_A里的重复记录(保留一条),假设以字段COL_PK重复来判断记录重复,那么有几种写法: 在Oracle里,可以利用rowid来删除,这是非常高 ...
- 函数的作用域与this指向 --- 性能篇
紧接上一篇博文:js函数的作用域与this指向 先来说说this的作用于链,this后的属性或者方法在使用时是先从本实例中查找,如果找到就先返回,如果没找到就接着向上从原型链中查找,如果有多重继承关系 ...