Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wait and progress options to Viewer.switchScene #421

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 112 additions & 18 deletions src/Viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ function Viewer(domElement, opts) {
// The current transition.
this._cancelCurrentTween = null;

// The current scene load monitoring.
this._cancelCurrentMonitor = null;

// The event listener fired when the current scene layers change.
// This is attached to the correct scene whenever the current scene changes.
this._layerChangeHandler = this._updateSceneLayers.bind(this);
Expand Down Expand Up @@ -206,6 +209,10 @@ Viewer.prototype.destroy = function() {
this._cancelCurrentTween();
}

if (this._cancelCurrentMonitor) {
this._cancelCurrentMonitor();
}

clearOwnProperties(this);
};

Expand Down Expand Up @@ -424,6 +431,10 @@ Viewer.prototype.destroyScene = function(scene) {
this._cancelCurrentTween();
this._cancelCurrentTween = null;
}
if (this._cancelCurrentMonitor) {
this._cancelCurrentMonitor();
this._cancelCurrentMonitor = null;
}
this._currentScene = null;
this.emit('sceneChange');
}
Expand Down Expand Up @@ -631,6 +642,12 @@ function defaultTransitionUpdate(val, newScene, oldScene) {
*
* @param {Scene} newScene The scene to switch to.
* @param {Object} opts Transition options.
* @param {boolean} [opts.wait] Wait until all visible tiles are loaded before
* switching to the new scene.
* @param {boolean} [opts.progress] Function to call as the new scene is
* loading. A `progress` parameter is passed as a number in the range of
* [0..1]. If the new scene is already loaded this function is still called
* with a value of 1.
* @param {number} [opts.transitionDuration=1000] Transition duration, in
* milliseconds.
* @param {number} [opts.transitionUpdate=defaultTransitionUpdate]
Expand All @@ -654,6 +671,7 @@ Viewer.prototype.switchScene = function(newScene, opts, done) {

// Do nothing if the target scene is the current one.
if (oldScene === newScene) {
opts.progress && opts.progress(1);
done();
return;
}
Expand All @@ -669,6 +687,13 @@ Viewer.prototype.switchScene = function(newScene, opts, done) {
this._cancelCurrentTween = null;
}

// Cancel an already ongoing monitor. This ensures that the stage doesn't get
// out of sync.
if (this._cancelCurrentMonitor) {
this._cancelCurrentMonitor();
this._cancelCurrentMonitor = null;
}

var oldSceneLayers = oldScene ? oldScene.listLayers() : [];
var newSceneLayers = newScene.listLayers();
var stageLayers = stage.listLayers();
Expand All @@ -687,11 +712,6 @@ Viewer.prototype.switchScene = function(newScene, opts, done) {
var update = opts.transitionUpdate != null ?
opts.transitionUpdate : defaultTransitionUpdate;

// Add new scene layers into the stage before starting the transition.
for (var i = 0; i < newSceneLayers.length; i++) {
this._addLayerToStage(newSceneLayers[i]);
}

// Update function to be called on every frame.
function tweenUpdate(val) {
update(val, newScene, oldScene);
Expand All @@ -715,22 +735,96 @@ Viewer.prototype.switchScene = function(newScene, opts, done) {
done();
}

// Store the cancelable for the transition.
this._cancelCurrentTween = tween(duration, tweenUpdate, tweenDone);
function tweenLayers() {
// Store the cancelable for the transition.
self._cancelCurrentTween = tween(duration, tweenUpdate, tweenDone);

// Update the current and replaced scene.
self._currentScene = newScene;
self._replacedScene = oldScene;

// Update the current and replaced scene.
this._currentScene = newScene;
this._replacedScene = oldScene;
// Emit scene and view change events.
self.emit('sceneChange');
self.emit('viewChange');

// Emit scene and view change events.
this.emit('sceneChange');
this.emit('viewChange');
// Add event listeners to the new scene.
// Note that event listeners can only be removed from the old scene once the
// transition is complete, since layers might get added or removed in the
// interim.
self._addSceneEventListeners(newScene);
}

// Add event listeners to the new scene.
// Note that event listeners can only be removed from the old scene once the
// transition is complete, since layers might get added or removed in the
// interim.
this._addSceneEventListeners(newScene);
// If waiting to transition, report the overall progress and also check the
// progress of each layer and start the transition when they have all finished.
var loadProgress = [];
function layerProgress(layerIndex, value) {
// Stop early if the value is the same as last time
if (loadProgress[layerIndex] === value) {
return;
}
loadProgress[layerIndex] = value;
// Calculate the progress of all layers
var total = 0;
for (var i = 0; i < loadProgress.length; i++) {
total += loadProgress[i];
}
// Report progress and start tweening when they're all finished
if (total === loadProgress.length) {
self._cancelCurrentMonitor = null;
opts.progress && opts.progress(1);
opts.wait && tweenLayers();
} else {
opts.progress && opts.progress(total / loadProgress.length);
}
}

// Monitor the loading progress of a specific layer
function monitorLayerProgress(layerIndex) {
var sceneLayer = newSceneLayers[layerIndex];
var textureStore = sceneLayer.textureStore();
function onRenderComplete() {
var tileList = [];
sceneLayer.visibleTiles(tileList);
var count = 0;
for (var i = 0; i < tileList.length; i++) {
if (textureStore.query(tileList[ i ]).hasTexture) {
count++;
}
}
layerProgress(layerIndex, count / tileList.length);
if (count === tileList.length) {
sceneLayer.removeEventListener('renderComplete', onRenderComplete);
}
}
self._cancelCurrentMonitor = function () {
sceneLayer.removeEventListener('renderComplete', onRenderComplete);
for (var i = 0; i < newSceneLayers.length; i++) {
this._removeLayerFromStage(newSceneLayers[i]);
}
opts.progress && opts.progress(1);
done();
};
sceneLayer.addEventListener('renderComplete', onRenderComplete);
}

// Add new scene layers into the stage before starting the transition.
for (var i = 0; i < newSceneLayers.length; i++) {
this._addLayerToStage(newSceneLayers[i]);
if (opts.wait) {
// Initially hide while loading finishes
newSceneLayers[i].mergeEffects({ opacity: 0 });
loadProgress.push( 0 );
}
}

if (opts.wait || opts.progress) {
for (var i = 0; i < newSceneLayers.length; i++) {
monitorLayerProgress(i)
}
}
if (!opts.wait) {
tweenLayers();
}
};


Expand Down