cesium 页面多 viewer 地图加载过缓解决方案

小小吐槽一下

自从开始搞 cesium 相关的东西,各种疑难杂症就开始接踵而至,层出不穷。

更让人“气愤”的是,有些问题,就连 StackOverflow、Google 这种传统的解决问题的大杀招都不好用了,简直要给人气哭了好嘛!😂

不知道是不是因为搞 cesium 的人太少了,还是因为愿意分享的人太少了,不过从 GitHub 的关注度,其实也能看出点端倪。

就拿同是 web 可视化三维框架的 three.jscesium 来作对比吧

image.png
image.png

简直不要太欺负人了,一个关注度六万,一个关注度才6千,差了一个量级。

因此,碰到问题得自己研究也很正常,顿时心里就好受了点。

闲话不多说,开始回到正题吧。

回到正题

至于为什么会碰到这个问题呢?

说起来,又是一言难尽。

感觉一言以蔽之就是:领导需要。

说实话,这其实是一个拍脑袋的需求,本来 3d 对显卡的配置的要求就蛮高,一个页面加多个 viewer,更增加了显卡的负担。

但是没办法,领导拍脑袋,你就得解决问题,领导只会关注你解没解决,而不关心你是怎样解决的。

那么现在的问题是,如果我们碰到了这个需求,我们该如何处之呢?

废话,按照领导的需求做呗,还能有啥更好的解决方案。

image.png

现在假设我们已经写好了我们的测试 demo:https://codepen.io/wuzhiqin/full/wvzvxbJ

多viewer加载1.gif

只要你细心观察,你就会发现,从左到右,每个 viewer 的图层,是依次按照顺序加载出来的。

如果你要是经验丰富,就会发现这就是问题所在了。

依次按顺序加载,我们可以看成是单线程的过程,对于这个场景,单线程的效果有时候是令人无法忍受的。

如果前面的 viewer 加载瓦片地图的时候,一时半会儿获取不到,那么我们后面的 viewer 就一直无法加载瓦片地图,就会处于被阻塞的状态了,表现出来的效果就是一直处于黑屏状态,这时候用户体验就不是很好了。

如何解决这个问题呢?

别急,如何解决这个问题,本就是这篇文章想写的重点。

开始分析

首先,为了找到解决的方案,当然少不了要用调试工具,找到这块代码是写在什么地方的,然后,我们需要尝试不断地去修改调试,以期找到合适的解决方案。

以我们的 chrome devtool 工具为例:


image.png

我们可以在 Network 一栏找到 XHR 子栏目,里面列出了前端页面发送的所有 ajax 请求。

然后我们随便选一条,就能看到这条请求的调用栈。

接下来的工作就很死板了,就是需要一个个方法点进去看,看一看是否是适合我们修改的地方。

我们可以看到最后一个调用的方法在 Resource.js 里面

image.png

我们点进去看看这个方法:


image.png

可以看到,这就是单纯的调用 js 中发送 ajax 请求的 api,执行请求数据的操作。

在往上拉,看看整个方法的全貌:

image.png

可以看到,前面就是 new 了一个 XMLHttpRequest 对象的实例,然后执行了一堆判断,可想而知,这地方并没有我们关心的逻辑。

ajax 本身就是异步的,数据返回的数据,不是前端代码能控制的。

因此,我们只能继续往前找。

现在我们看第二个地方:


image.png

我们点进去,看一眼:


image.png

这一看,我们就能看出来,这里也不是我们要找的地方。

因为这里都只是在处理单个的图片 ajax 加载问题,并不涉及到批量的逻辑。

当然这里为了节省篇幅,后面就不一个个往前分析了,直接定位到我们的目的地去:

image.png

当然,由于篇幅的原因,这里直接展示不了所有代码,我们接着往下拉,定位到,doRequest 方法。


image.png

看到里面有个配置 throttleByServer 默认被设置成了 true ,我们试着改成 false ,然后看看加载的效果

多viewer加载2.gif

我们看上面的图,能够发现,我们改了之后,加载效果明显比之前的更顺畅。

但是我们这里无法直接更改源码,并非说我们不能改。

而是因为,我们使采用别人开源的代码,我们很难搞明白当初为何这地方的代码要这么写,要是我们直接改了,有可能会影响到别的地方代码的正常运行。

那么我们该怎么办呢?

为了使我们的修改,影像最小化,我们可以像下面这样做:

let throttleByServer = false;
      
const {
  ImageryLayer,
  when,
  Request,
  defined,
  TileProviderError,
  RequestState,
  ImageryState,
  RequestType,
} = Cesium;

ImageryLayer.prototype._requestImagery2 =
  ImageryLayer.prototype._requestImagery;
ImageryLayer.prototype._requestImagery = function (imagery) {
  var imageryProvider = this._imageryProvider;

  var that = this;

  function success(image) {
    if (!defined(image)) {
      return failure();
    }

    imagery.image = image;
    imagery.state = ImageryState.RECEIVED;
    imagery.request = undefined;

    TileProviderError.handleSuccess(that._requestImageError);
  }

  function failure(e) {
    if (imagery.request.state === RequestState.CANCELLED) {
      // Cancelled due to low priority - try again later.
      imagery.state = ImageryState.UNLOADED;
      imagery.request = undefined;
      return;
    }

    // Initially assume failure.  handleError may retry, in which case the state will
    // change to TRANSITIONING.
    imagery.state = ImageryState.FAILED;
    imagery.request = undefined;

    var message =
      "Failed to obtain image tile X: " +
      imagery.x +
      " Y: " +
      imagery.y +
      " Level: " +
      imagery.level +
      ".";
    that._requestImageError = TileProviderError.handleError(
      that._requestImageError,
      imageryProvider,
      imageryProvider.errorEvent,
      message,
      imagery.x,
      imagery.y,
      imagery.level,
      doRequest,
      e
    );
  }

  function doRequest() {
    var request = new Request({
      throttle: false,
      throttleByServer,
      type: RequestType.IMAGERY,
    });
    imagery.request = request;
    imagery.state = ImageryState.TRANSITIONING;
    var imagePromise = imageryProvider.requestImage(
      imagery.x,
      imagery.y,
      imagery.level,
      request
    );

    if (!defined(imagePromise)) {
      // Too many parallel requests, so postpone loading tile.
      imagery.state = ImageryState.UNLOADED;
      imagery.request = undefined;
      return;
    }

    if (defined(imageryProvider.getTileCredits)) {
      imagery.credits = imageryProvider.getTileCredits(
        imagery.x,
        imagery.y,
        imagery.level
      );
    }

    when(imagePromise, success, failure);
  }

  doRequest();
};
  1. 将之前原型链上的方法重命名
  2. 将源码这个方法拷贝出来
  3. throttleByServer 变量抽离出来进行控制

这样,我们就做到了按需重载了。

比如,当我们需要在页面加载多 viewer 的时候,我们久可以用 _requestImagery 方法,当我们不需要加载的时候,就换回 _requestImagery2

当然,会改源码,仅仅只是第一步。要想进阶,得需要弄懂整个代码,然后对其进行优化,写出更符合自己需求的代码出来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容