四葉小天使!
上承
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 @秋意正寒
https://www.cnblogs.com/onsummer/p/12571972.html

