Cesium 源碼筆記[2] CesiumWidget模塊的實例化過程 ver1.67

四葉小天使!

上承

CesiumWidget實際上和Viewer差不多。以下兩句代碼用于初始化,效果是差不多的。

const widget = new Cesium.CesiumWidget('id選擇器')
const viewer = new Cesium.Viewer('id選擇器')

實例化Viewer必定會實例化一個CesiumWidget。CesiumWidget實際上代表的是三維數據可視區域,而Viewer除了包括可視區域,還包括各種控件(時間軸、右上角各種按鈕、搜索框、時間撥盤等),更像是一個總體承載容器。Viewer能通過extend()方法擴充自定義的控件。

真正使用WebGL繪圖的,還不是CesiumWidget模塊,而是在CesiumWidget中實例化的Scene模塊。

不過,CesiumWidget起了一個橋梁的作用,它將構造時傳遞的DOM元素(或ID選擇器)再內嵌了一個canvas元素,再將此canvas元素傳遞給Scene,讓Scene接著繪圖。

比較Viewer和CesiumWidget和Scene分別作用的DOM元素,用一張圖表示:

構造原理

DOM構造

// function CesiumWidget(container, options)構造函數內,第180~207行
if (!defined(container)) {
    throw new DeveloperError('container is required.');
}
container = getElement(container);
options = defaultValue(options, defaultValue.EMPTY_OBJECT);

var element = document.createElement('div');
element.className = 'cesium-widget';
container.appendChild(element);
// ...
var canvas = document.createElement('canvas');
// ...
element.appendChild(canvas);

我忽略了一些內容。這不到30行代碼,完成了上級Viewer的DOM元素判斷,完成了本級DOM元素創建,并完成了下一級DOM元素——canvas的創建,判斷了傳遞進來的options參數是否為空。

關于下一級DOM元素canvas,還配置了一些HTML相關的事件、配置等,不詳細展開了。

這樣,DOM的層級關系就制造完畢了。

接下來還有一些其他的小部件(例如商標版權等)以及分辨率等設置,位于209~219行。

221~238行,將CesiumWidget的私有變量賦值完畢,并調用configureCanvasSize()來調整canvas的尺寸。

場景及有關對象構造

在240~349行,是一個大大的try/catch塊,CesiumWidget模塊的構造函數這部分代碼,完成了Scene、Globe、SkyBox、SkyAtmosphere模塊的實例化。

而最終暴露到CesiumWidget的API中的,有camera、scene、imageryLayers、terrainProvider、screenSpaceEventHandler、clock這幾個主要的對象。

這讓我十分郁悶的事情來了,某個模塊的屬性(例如CesiumWidget的屬性——camera)并不是它原型上的,而是這個模塊的別的屬性的原型上的(camera屬性其實是屬于Scene模塊的)。不理解Js原型的同學可以理解為Java的類,原型是Js(ES5)實現面向對象的一個重要設計模式。

從以下代碼可以看到:

// CesiumWidget.js模塊
Object.defineProperties(CesiumWidget.prototype, {
    // ...
    camera : { // 449行
        get : function() {
            return this._scene.camera;
        }
    },
    // ...
}

這種設計在Cesium的API中非常常見,原理歸原理,API歸API。想要弄清楚誰是誰的崽兒(Scene.camera),而不是被誰撫養的(CesiumWidget.camera),只能通過源碼來知曉。

回歸正題。

// CesiumWidget.js,241~255行
var scene = new Scene({
    canvas : canvas,
    contextOptions : options.contextOptions,
    creditContainer : innerCreditContainer,
    creditViewport: creditViewport,
    mapProjection : options.mapProjection,
    // 太長了不貼了
    // ...
});
this._scene = scene;

實例化Scene對象,傳遞主要的構造參數,大部分來自CesiumWidget的構造參數options中。

// CesiumWidget.js,257~260行
scene.camera.constrainedAxis = Cartesian3.UNIT_Z;

configurePixelRatio(this);
configureCameraFrustum(this);

指定攝像機的約束軸為Z軸,觸發私有函數調整像素比例和攝像機視錐體。

// CesiumWidget.js,262~271行
var ellipsoid = defaultValue(scene.mapProjection.ellipsoid, Ellipsoid.WGS84);

var globe = options.globe;
if (!defined(globe)) {
    globe = new Globe(ellipsoid);
}
if (globe !== false) {
    scene.globe = globe;
    scene.globe.shadows = defaultValue(options.terrainShadows, ShadowMode.RECEIVE_ONLY);
}

創建ellipsoid和globe,并傳遞給scene.

273~299行創建環境因素,主要是天空盒和太陽、月亮、大氣環境。

302~314行創建影像數據源(若無,則調用createWorldImagery模塊創建世界影像,和CesiumION的token有關)和地形數據源,并傳遞給scene。影像數據源和地形數據源均可以從options中獲取,若options沒有,則使用Cesium官方給的,需要注意token問題。

318~325行確定scene對象的視圖模式是二維的、三維的還是哥倫布的(2.5D)。

316,333~341行給scene綁定了渲染錯誤事件處理函數。

327~331行,確定了是否使用默認的循環渲染機制(useDefaultRenderLoop屬性),這個屬性若為false,則需要手動調用CesiumWidget.render()渲染。還確定了在默認循環渲染機制時,目標幀速率(targetFrameRate屬性)。

原型上的屬性定義

// CesiumWidget.js,352~581行
Object.defineProperties(CesiumWidget.prototype, {
    container : {
        get : function() {
            return this._container;
        }
    },
    // ...
    // 太長不貼了
}

API文檔中能看到的CesiumWidget的屬性,均在此定義了,使用的是Object.defineProperties()方法。

原型上的方法定義

CesiumWidget.js中593~708行,是CesiumWidget的API中所有方法的定義。

  • CesiumWidget.prototype.showErrorPanel = function(title, message, error) {...}
  • CesiumWidget.prototype.isDestroyed = function() {...}
  • CesiumWidget.prototype.destroy = function() {...}
  • CesiumWidget.prototype.resize = function() {...}
  • CesiumWidget.prototype.render = function() {...}

當渲染錯誤時,調用showErrorPanel方法,彈出個對話框。

如果CesiumWidget被銷毀了,調用isDestroyed方法返回的是true。

destroy()方法用于需要銷毀整個視圖時。

當窗口大小發生變化時,調用resize方法調整canvas和camera。

render方法是自動調用的,通常不需要開發者關心,除非設置CesiumWidget.useDefaultRenderLoop為false。這個方法是用來渲染場景的。

導出模塊

最后在709行,導出此模塊。

// CesiumWidget.js,709行
export default CesiumWidget;

版權所有。轉載請聯系我,B站/知乎/小專欄/博客園/CSDN @秋意正寒
http://www.jsfhjj.com/onsummer/p/12571972.html

posted @ 2020-04-01 01:53  秋意正寒  閱讀(...)  評論(...編輯  收藏
最新chease0ldman老人