2016-08-15 42 views
2

我正在创建需要通过按钮和滑块支持大量交互性的MapboxGL地图。使用RxJS在另一个序列之后运行序列

MapboxGL地图实例是状态并有气质生命周期由于大量的砖和来源的异步装载。这意味着大部分工作都是在事件处理程序内完成的。这让我认为RxJS是管理所有这些交互性的可靠选择。

例如,如果要将要素图层添加到地图,则the mapbox example建议在map.on('load', handler)中执行所有操作。这对于静态地图来说很好,但对于具有20多个切换/滑块/控件的地图来说,协调命令式事件处理程序是一件麻烦事。

我想要做的是为每个单独的操作创建序列。其中最基本的是加载地图,然后在地图加载后添加基础要素图层。

所以这里是我得到的,我一直在使用RxJS约5小时总计,所以也许有一些我只是不知道我不知道。

段:

var map = new mapboxgl.Map({ 
     container: 'map', 
     style: 'mapbox://styles/mapbox/dark-v9', 
     center: [-122.78, 38.43], 
     zoom: 10 
    }); 

    // Check every 100ms if the map is loaded, if so emit 
    var loaded$ = Rx.Observable 
    .interval(100) 
    .map(() => map.loaded()) 
    .filter(x => x) 
    .first(); 

    // We must wait until the map is loaded before attaching the feature layer. 
    var addLayer = loaded$.subscribe(
    baseMapLoaded, errorHandler, addFeatureLayerAndSource 
); 

    // Attaching the feature layer sets the map.loaded() to false 
    // while it fetches the tiles. 
    // 
    // When the feature layer and source are loaded, 
    // log 'all tiles loaded' then 'completed' to the console 
    // 
    // Why can I declare `addLayer` and `completed` like this? 
    // Shouldn't these both fire after the loaded$ emits the first time, 
    // causing an error? 
    var completed = loaded$.subscribe(tilesLoaded, errorHandler, completeHandler); 

    /* Misc Handlers */ 
    function addFeatureLayerAndSource() { 
    console.log(2, 'adding feature layer and source'); 
    map.addSource('parcel_data', { 
     'type': 'vector', 
     'tiles': ['http://localhost:5000/api/v1/tiles/{z}/{x}/{y}'] 
    }); 

    map.addLayer({ 
     "id": "parcel_data", 
     "type": "fill", 
     "source": "parcel_data", 
     "source-layer": "parcel_data", 
    }); 
    } 
    function baseMapLoaded() { console.log(1, 'base map loaded'); } 
    function tilesLoaded() { console.log(3, 'all tiles loaded'); } 
    function errorHandler(err) { console.log('error handling', err); } 
    function completeHandler() { console.log('completed'); } 

编辑:更新的代码段,以反映@ CalvinBeldin的帮助。目前试图找出为什么这实际上起作用。我console.logs的顺序是正确的:

1 "base map loaded" 
2 "adding feature layer and source" 
3 "all tiles loaded" 
completed 

回答

2

可观察到的由renderObservable.last()返回永远不会火,因为renderObservable的完整的事件永远不会发生(renderObservable只会继续监听render事件,直到它的配置,所以在这个意义上它永远不会“完成”)。

您需要创建一个新的Observable,它在最后一次渲染时发出。根据MapboxGL API的限制,轮询解决方案可能是唯一的选择:

// Emits the value of map.loaded() every 100ms; you can update this 
// time interval to better suit your needs, or get super fancy 
// and implement a different polling strategy. 
const loaded$ = Rx.Observable.interval(100).map(() => map.loaded()); 

// Will emit once map.loaded() is true. 
const isLoaded$ = loaded$.filter(x => x).first(); 
+0

感谢您的帮助!根据MapboxGL问题跟踪器,知道最后一次呈现事件何时开始的唯一方法是检查'render'事件处理程序中是否存在'map.loaded()=== true'。这就是为什么我在谓词函数中使用'.last()'的原因。由于这种狡猾,我希望能够建立一个简单的方法来订阅正在触发的最后一个渲染事件,以便我可以在多个地方重复使用它。 –

+0

您可以设置一个Observable,它每隔一段时间轮询一次地图,如果加载是真的,然后发射,则进行过滤。将尽快用代码更新答案。 –

+0

谢谢,我想看看。 –

0

我可能不完全明白是什么问题。 要点:

  • 保持负载事件负载事件已经happned后认购的观察员((行为|重播)除)
  • 返回渲染事件后,才使事件的发生(flatMap)

loadSource$ = Observable.fromEvent(map, 'load').first(); 

// You need load$ to be a BehaviorSubject or replay last value on 
// In order to keep state of wheter laod event happend or not 
// For observers which subscribed after load event already happened 
load$ = loadSource.replay(1); 
render$ = Observable.fromEvent(map, 'render'); 
renderAfterLoad$ = load$.flatMap(() => render$); 

load$.subscribe(() => { 
    map.addSource('source_data', { 
    'type': 'vector', 
    'tiles': ['http://localhost:5000/api/v1/tiles/{z}/{x}/{y}'] 
    }); 

    map.addLayer({ 
    "id": "layer_data", 
    "type": "fill", 
    "source": "source_data", 
    "source-layer": "source_layer_id", 
    }); 
}); 

render$.subscribe(() => { 
    console.log('all tiles loaded'); 
}); 
相关问题