2016-08-15 33 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'); 
}); 
相關問題