美文网首页开源
cesium 页面多 viewer 地图加载过缓解决方案

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

作者: 纯爱枫若情 | 来源:发表于2020-12-28 17:51 被阅读0次

    小小吐槽一下

    自从开始搞 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

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

    相关文章

      网友评论

        本文标题:cesium 页面多 viewer 地图加载过缓解决方案

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