CesiumWidget.js的作用
顾名思义,CesiumWidget就是cesium小部件的意思。但是,cesium包含哪些小部件?这些小部件又有哪些意义?这些小部件是不可或缺的吗?
看《Cesium原理篇:1最长的一帧之渲染调度》讲,好像cesium的启动就是由widget来触发的?
CesiumWidget.js
startRenderLoop函数需要传入一个widget参数。这个widget参数是从哪来的??
想来这个widget应该不是一般的控件,而是应该由浏览器窗体传过来的对象?但是,又不能仅仅是浏览器的窗体,它应该还包括一些自定义的属性。
首先,CesiumWidget是一个类,它由哪些参数和方法组成?
1. 构造函数:
function CesiumWidget(container, options) { //>>includeStart('debug', pragmas.debug); if (!defined(container)) { throw new DeveloperError("container is required."); } //>>includeEnd('debug'); container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); //Configure the widget DOM elements var element = document.createElement("div"); element.className = "cesium-widget"; container.appendChild(element); var canvas = document.createElement("canvas"); var supportsImageRenderingPixelated = FeatureDetection.supportsImageRenderingPixelated(); this._supportsImageRenderingPixelated = supportsImageRenderingPixelated; if (supportsImageRenderingPixelated) { canvas.style.imageRendering = FeatureDetection.imageRenderingValue(); } canvas.oncontextmenu = function () { return false; }; canvas.onselectstart = function () { return false; }; // Interacting with a canvas does not automatically blur the previously focused element. // This leads to unexpected interaction if the last element was an input field. // For example, clicking the mouse wheel could lead to the value in the field changing // unexpectedly. The solution is to blur whatever has focus as soon as canvas interaction begins. function blurActiveElement() { if (canvas !== canvas.ownerDocument.activeElement) { canvas.ownerDocument.activeElement.blur(); } } canvas.addEventListener("mousedown", blurActiveElement); canvas.addEventListener("pointerdown", blurActiveElement); element.appendChild(canvas); var innerCreditContainer = document.createElement("div"); innerCreditContainer.className = "cesium-widget-credits"; var creditContainer = defined(options.creditContainer) ? getElement(options.creditContainer) : element; creditContainer.appendChild(innerCreditContainer); var creditViewport = defined(options.creditViewport) ? getElement(options.creditViewport) : element; var showRenderLoopErrors = defaultValue(options.showRenderLoopErrors, true); var useBrowserRecommendedResolution = defaultValue( options.useBrowserRecommendedResolution, true ); this._element = element; this._container = container; this._canvas = canvas; this._canvasClientWidth = 0; this._canvasClientHeight = 0; this._lastDevicePixelRatio = 0; this._creditViewport = creditViewport; this._creditContainer = creditContainer; this._innerCreditContainer = innerCreditContainer; this._canRender = false; this._renderLoopRunning = false; this._showRenderLoopErrors = showRenderLoopErrors; this._resolutionScale = 1.0; this._useBrowserRecommendedResolution = useBrowserRecommendedResolution; this._forceResize = false; this._clock = defined(options.clock) ? options.clock : new Clock(); configureCanvasSize(this); try { var scene = new Scene({ canvas: canvas, contextOptions: options.contextOptions, creditContainer: innerCreditContainer, creditViewport: creditViewport, mapProjection: options.mapProjection, orderIndependentTranslucency: options.orderIndependentTranslucency, scene3DOnly: defaultValue(options.scene3DOnly, false), terrainExaggeration: options.terrainExaggeration, shadows: options.shadows, mapMode2D: options.mapMode2D, requestRenderMode: options.requestRenderMode, maximumRenderTimeChange: options.maximumRenderTimeChange, }); this._scene = scene; scene.camera.constrainedAxis = Cartesian3.UNIT_Z; configurePixelRatio(this); configureCameraFrustum(this); 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 ); } var skyBox = options.skyBox; if (!defined(skyBox)) { skyBox = new SkyBox({ sources: { positiveX: getDefaultSkyBoxUrl("px"), negativeX: getDefaultSkyBoxUrl("mx"), positiveY: getDefaultSkyBoxUrl("py"), negativeY: getDefaultSkyBoxUrl("my"), positiveZ: getDefaultSkyBoxUrl("pz"), negativeZ: getDefaultSkyBoxUrl("mz"), }, }); } if (skyBox !== false) { scene.skyBox = skyBox; scene.sun = new Sun(); scene.moon = new Moon(); } // Blue sky, and the glow around the Earth's limb. var skyAtmosphere = options.skyAtmosphere; if (!defined(skyAtmosphere)) { skyAtmosphere = new SkyAtmosphere(ellipsoid); } if (skyAtmosphere !== false) { scene.skyAtmosphere = skyAtmosphere; } //Set the base imagery layer var imageryProvider = options.globe === false ? false : options.imageryProvider; if (!defined(imageryProvider)) { imageryProvider = createWorldImagery(); } if (imageryProvider !== false) { scene.imageryLayers.addImageryProvider(imageryProvider); } //Set the terrain provider if one is provided. if (defined(options.terrainProvider) && options.globe !== false) { scene.terrainProvider = options.terrainProvider; } this._screenSpaceEventHandler = new ScreenSpaceEventHandler(canvas); if (defined(options.sceneMode)) { if (options.sceneMode === SceneMode.SCENE2D) { this._scene.morphTo2D(0); } if (options.sceneMode === SceneMode.COLUMBUS_VIEW) { this._scene.morphToColumbusView(0); } } this._useDefaultRenderLoop = undefined; this.useDefaultRenderLoop = defaultValue( options.useDefaultRenderLoop, true ); this._targetFrameRate = undefined; this.targetFrameRate = options.targetFrameRate; var that = this; this._onRenderError = function (scene, error) { that._useDefaultRenderLoop = false; that._renderLoopRunning = false; if (that._showRenderLoopErrors) { var title = "An error occurred while rendering. Rendering has stopped."; that.showErrorPanel(title, undefined, error); } }; scene.renderError.addEventListener(this._onRenderError); } catch (error) { if (showRenderLoopErrors) { var title = "Error constructing CesiumWidget."; var message = 'Visit <a href="http://get.webgl.org">http://get.webgl.org</a> to verify that your web browser and hardware support WebGL. Consider trying a different web browser or updating your video drivers. Detailed error information is below:'; this.showErrorPanel(title, message, error); } throw error; } }
构造函数需要传递两个参数container和options
除了构造函数,还有一个Object.defineProperties(CesiumWidget.prototype,{...})
该方法貌似也是用来给类定义属性的?跟在构造函数里使用this.xxx定义的属性有何不同。。。?反正,在这个方法里定义了许多个属性:
Object.defineProperties(CesiumWidget.prototype, {
container: { get: function () { return this._container; }, }, canvas: { get: function () { return this._canvas; }, }, creditContainer: { get: function () { return this._creditContainer; }, }, creditViewport: { get: function () { return this._creditViewport; }, }, scene: { get: function () { return this._scene; }, }, imageryLayers: { get: function () { return this._scene.imageryLayers; }, }, terrainProvider: { get: function () { return this._scene.terrainProvider; }, set: function (terrainProvider) { this._scene.terrainProvider = terrainProvider; }, }, camera: { get: function () { return this._scene.camera; }, }, clock: { get: function () { return this._clock; }, }, screenSpaceEventHandler: { get: function () { return this._screenSpaceEventHandler; }, }, targetFrameRate: { get: function () { return this._targetFrameRate; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (value <= 0) { throw new DeveloperError( "targetFrameRate must be greater than 0, or undefined." ); } //>>includeEnd('debug'); this._targetFrameRate = value; }, }, useDefaultRenderLoop: { get: function () { return this._useDefaultRenderLoop; }, set: function (value) { if (this._useDefaultRenderLoop !== value) { this._useDefaultRenderLoop = value; if (value && !this._renderLoopRunning) { startRenderLoop(this); } } }, }, resolutionScale: { get: function () { return this._resolutionScale; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (value <= 0) { throw new DeveloperError("resolutionScale must be greater than 0."); } //>>includeEnd('debug'); if (this._resolutionScale !== value) { this._resolutionScale = value; this._forceResize = true; } }, }, useBrowserRecommendedResolution: { get: function () { return this._useBrowserRecommendedResolution; }, set: function (value) { if (this._useBrowserRecommendedResolution !== value) { this._useBrowserRecommendedResolution = value; this._forceResize = true; } }, }, });
而其中有一个属性useDefaultRenderLoop,这个属性包含两个方法set和set,其中set(value)方法里调用了startRenderLoop(this)。这里面this传递的widget其实就是CesiumWidget自身。
只是我不解的是,CesiumWidget自身好像并不包含render()方法?那么它的render是由谁来完成呢?
CesiumWidget.prototype.render = function () { if (this._canRender) { this._scene.initializeFrame(); var currentTime = this._clock.tick(); this._scene.render(currentTime); } else { this._clock.tick(); } };
CesiumWidget的render函数属性是定义了的,是通过prototype的方式添加进去的。CesiumWidget的render函数其实就是调用的scene中的render()函数。接下来,就是看一下Scene中的render方法了。
参考:link
参考2:link2