-
Notifications
You must be signed in to change notification settings - Fork 6
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 loading animation to splash #68
Comments
A while ago we discussed adding the PhET animated logo in which the paper airplane flies past the words "interactive simulations" then turns in 3d and flies up through the 'h'. I looked at the actual mp4 for this, and it is about 1.5MB. I tried generating at as a gif, and it was about 7MB. These are both too high to be useful (unless we download the former lazily and store it in an offline cache for future runs--but that seems complicated). I investigated running CSS animations during the startup sequence, but even on Win8/Chrome they stutter and stall when the JS loading code runs. Another idea I wanted to investigate: try adding a hook into a function that is called frequently during startup, such as the Node constructor, inherit, or Property constructor (or link), that updates the animation. Then after the sim is loaded, the hooks can be unhooked. This strategy would only work if rendering changes can be made while JS is still blocking (not sure one way or the other, but it would need to work across all/most of our platforms to be useful). |
By the way, here is the HTML I used to test CSS animations during startup: <!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="initial-scale=1,user-scalable=no,maximum-scale=1,minimal-ui"/>
<title> </title> <!-- Localized title will be filled in by JavaScript. -->
<script type="text/javascript" src="../sherpa/jquery-2.1.0.js"></script>
<script type="text/javascript" src="../sherpa/lodash-2.4.1.js"></script>
<script type="text/javascript" src="../sherpa/has.js"></script>
<script type="text/javascript" src="../sherpa/FileSaver.js"></script>
<script type="text/javascript" src="../sherpa/Tween-r12.js"></script>
<script type="text/javascript" src="../sherpa/numeric-1.2.6.js"></script>
<script type="text/javascript" src="../phetcommon/js/util/query-parameters.js"></script>
<script type="text/javascript" src="../phetcommon/js/util/check-assertions.js"></script>
<script type="text/javascript" src="../phetcommon/js/util/check-accessibility.js"></script>
<script data-main="js/energy-skate-park-basics-config.js" src="../sherpa/require-2.1.11.js"></script>
<style>
div {
width: 100px;
height: 100px;
background: red;
position: absolute;
-webkit-animation: myfirst 10s; /* Chrome, Safari, Opera */
animation: myfirst 10s;
}
/* Chrome, Safari, Opera */
@-webkit-keyframes myfirst {
from {
background: red;
left: 0;
}
to {
background: yellow;
left: 2000px;
}
}
/* Standard syntax */
@keyframes myfirst {
from {
background: red;
left: 0;
}
to {
background: yellow;
left: 2000px;
}
}
</style>
</head>
</head>
<body bgcolor="black">
<p><b>Note:</b> This example does not work in Internet Explorer 9 and earlier versions.</p>
<div></div>
<img id="splash" style="position: absolute;top: 50%;left: 50%;margin-top: -180px;margin-left: -252px;"
src="../brand/images/splash.svg">
</body> |
I doubt we would be able to make any rendering changes while the main JS script tag is being executed. Ideally, we would be able to see an animation during the download of the rest of the HTML file. Did the animation in your example get added to a non-chipper-built version? |
I tried adding this to the scenery.Node constructor: $( '#happyrect' ).css( {left: x + 'px'} );
x=x+0.2; But the rectangle doesn't move during initialization (even though this is called hundreds of time over many seconds), and just updates all at once at the end. I think this supports Jon's hypothesis that:
For this part:
Were you just suggesting to animate during the HTML download and then pause animation once the JS starts running? I haven't experimented in chipper yet, just in requirejs mode. |
I wasn't sure of a better way than having it freeze while the JS executes. Flash content will run in a separate process, so there's a good chance we could recreate the barebones airplane animation in Flash (keep the file-size really small) and have it be somewhat smooth during whatever loading stage there is. It would be more complicated to "wait" for the animation to finish if the sim loads first (detecting if Flash is running adds a major "sim won't run at all" condition if we mess up). |
What about creating some of the screen models and/or views using Web Workers, and passing them back to the main thread as transferable objects with structured cloning? |
If we can have a performance gain by passing binary/string data from a web worker (computed to contain the scene graph, etc.), wouldn't we stand to gain more by just precomputing that and shipping the binary/string data with the sim so no worker is needed? (If possible, string/translation bounds might give issues). |
I think it would be interesting but unlikely to be practical to precompute the simulation state, because it may be much larger and would have to be converted to JS instances/prototypes/etc anyways. (Let me know if I am wrong though!) The main point of constructing it with web workers is not to speed things up but to permit animation while it is running. But perhaps web workers would suffer from the same problem I mentioned above (I'm not familiar with structured copying, etc). |
Web workers would run on another thread (generally) and allow the animation while it is running, but I think it would require major restructuring of how we do things so that everything can be serialized (think axon listeners, etc.) |
Another possibility would be to show a short animation before loading the code, but this would delay sim launch by however long the animation is. |
We noticed that the simulations at http://connexions.github.io/simulations/ have a nice animated splash screen. We should look into how they did it and if we can do something similar. |
The connexions splash has unreliable behavior on the iPad3. Sometimes it animates, sometimes it is blank, sometimes the beaker appears with no bubbles, etc. @jakies pointed out the code that renders it is in CSS and located here: |
Concord consortium also seems to have a loading animation on their HTML5 sims. See for instance: http://mw.concord.org/nextgen/#interactives/chemistry/phase-change/molecular-view-liquid |
I also saw this loading spinner, which seemed immune to blocking JS in the console (at least in Chrome) https://github.com/fgnass/spin.js |
A suggestion floated to me (which I am curious about) is that we could do asynchronous loading and actually ship 2 files when you click the "play" simulation button. My housemate brought up an interesting point -- the single html file for download does not really need a loading screen (in fact you do not really even see it even on a sim like function builder) since once you have it downloaded you are good to go, it opens seamlessly without any appreciable delay (much like sims in the iPad App since they are stored locally). He claimed that with our single HTML file solution getting animation to work while the JS loads is pretty much impossible with the way things currently work. However, he said we might be able to have two different methods. The file for download being the single HTML file we currently have, and a package for running online containing two files, one being the loading animation. This would of course mean a change to our build process, not sure if it is practical/feasible/etc, but I am interested in investigating a bit. Since all loading screens are the same, it seems perhaps we could "tack on" this file to any HTML5 sim being run on the site. Curious to discuss. |
On iPad3, it takes about 11,000 milliseconds for Function Builder to launch after the full HTML file has been downloaded. On Firefox on my MacBook Air, it is taking about 5,000 ms after the full HTML file has been downloaded. Showing a loading animation during code downloading would be helpful for situations that take a long time to download the HTML, which may include: people using a poor LTE or 3G connection, users that are far from our server (such as Europe and Asia), classrooms with poor wifi, etc. But for all of those cases, even after the simulation is downloaded, while the initialization JS code is running, no animation can be shown. For instance, let's say I am in Japan on a flaky LTE connection on my iPad3, then it could take 32 seconds to download function builder, then an additional 11 seconds to run the initialization sequence. The only possible solution I am aware of for showing incremental loading during the initialization sequence is to re-architect our initialization sequence to initialize one screen at a time, and show a progress bar moving forward after each screen. (Say, for a 4 screen simulation, it would initialize in 4 segments). This wouldn't help sims that have only one screen, but maybe they don't take as long to initialize and hence don't need a solution here. I'm not opposed to additionally discussing and showing some progress animation during downloading, but I thought some browsers already had something like that (for instance, the blue line that moves from left to right on iPad). |
@jbphet and @schmitzware are curious to explore this. Current thought, a animation while HTML is downloading and progress bar as joist initiates screens (perhaps granularity of model and view) |
Instead of a 2-file solution, I'm wondering if we can put code in the |
According to http://stackoverflow.com/questions/8249013/why-put-javascript-in-head,
So this may be a suitable place for us to put loading code. |
I was curious about the idea of doing this with a single script, but putting the loading script near the top of the file. So I added this code as the first script in the body: <script>
var span = document.createElement('span');
span.style.color = 'white';
span.innerHTML = 'Loading';
document.body.appendChild(span);
setInterval(function(){
span.innerHTML = span.innerHTML+'.';
},100);
</script> and throttled my connection to simulate 4G, and it looked like this: You can see the Loading........ pause when the code is fully downloaded and the JS starts running. Putting this animation in the same HTML (instead of a separate file) would be immensely simpler as well as for making for a faster download, so I recommend to proceed with the single file. Of course, this approach will have to be tested on different platforms, but it seems promising. |
I should also mention, that for this part of the animation, the sky is the limit for what we display. It could be the airplane animating around, for example. |
I'm also interested in investigating this. |
To understand the issue for showing progress bars while the simulation is loading, I rewrote this code: // Instantiate the screens. Currently this is done eagerly, but this pattern leaves open the door for loading things
// in the background.
screens.forEach( function initializeScreen( screen ) {
screen.backgroundColorProperty.link( sim.updateBackground );
screen.initializeModelAndView();
} ); as var span = document.createElement( 'span' );
span.style.color = 'white';
span.innerHTML = 'Initializing Screens... (0/'+screens.length+')';
document.body.appendChild( span );
var setProgressBar = function( distance, max ) {
span.innerHTML = 'Initializing Screens... (' + distance + '/' + max + ')';
};
var workItems = [];
screens.forEach( function( screen, screenIndex ) {
workItems.push( function() {
screen.backgroundColorProperty.link( sim.updateBackground );
screen.initializeModelAndView();
setProgressBar( screenIndex + 1, screens.length );
} );
} );
workItems.push( function() {
sim.finishInit( screens, options );
sim.start();
} );
var runItem = function( i ) {
setTimeout( function() {
workItems[ i ]();
if ( i + 1 < workItems.length ) {
runItem( i + 1 );
}
}, 0 );
};
runItem( 0 ); To accomplish this, I also had to move out the last half of the constructor to a new function like so: finishInit: function( screens, options ) {
var sim = this;
// ModuleIndex should always be defined. On startup screenIndex=0 to highlight the 1st screen.
// When moving from a screen to the homescreen, the previous screen should be highlighted
if ( this.homeScreen ) {
this.rootNode.addChild( this.homeScreen.view );
}
_.each( screens, function( screen ) {
screen.view.layerSplit = true;
sim.rootNode.addChild( screen.view );
} );
this.rootNode.addChild( this.navigationBar );
if ( this.homeScreen ) {
// Once both the navbar and homescreen have been added, link the PhET button positions together.
// See https://github.com/phetsims/joist/issues/304.
PhetButton.linkPhetButtonTransform( this.homeScreen, this.navigationBar, this.rootNode );
}
this.multilink( [ 'screenIndex', 'showHomeScreen' ], function( screenIndex, showHomeScreen ) {
if ( sim.homeScreen ) {
sim.homeScreen.view.setVisible( showHomeScreen );
}
for ( var i = 0; i < screens.length; i++ ) {
screens[ i ].view.setVisible( !showHomeScreen && screenIndex === i );
}
sim.navigationBar.setVisible( !showHomeScreen );
sim.updateBackground();
} );
// layer for popups, dialogs, and their backgrounds and barriers
this.topLayer = new Node();
this.rootNode.addChild( this.topLayer );
// @private list of nodes that are "modal" and hence block input with the barrierRectangle. Used by modal dialogs
// and the PhetMenu
this.modalNodeStack = new ObservableArray(); // {Node} with node.hide()
// @public (joist-internal) Semi-transparent black barrier used to block input events when a dialog (or other popup)
// is present, and fade out the background.
this.barrierRectangle = new Rectangle( 0, 0, 1, 1, 0, 0, {
fill: 'rgba(0,0,0,0.3)',
pickable: true
} );
this.topLayer.addChild( this.barrierRectangle );
this.modalNodeStack.lengthProperty.link( function( numBarriers ) {
sim.barrierRectangle.visible = numBarriers > 0;
} );
this.barrierRectangle.addInputListener( new ButtonListener( {
fire: function( event ) {
sim.barrierRectangle.trigger0( 'startedCallbacksForFired' );
assert && assert( sim.modalNodeStack.length > 0 );
sim.modalNodeStack.get( sim.modalNodeStack.length - 1 ).hide();
sim.barrierRectangle.trigger0( 'endedCallbacksForFired' );
}
} ) );
options.tandem && options.tandem.createTandem( 'sim.barrierRectangle' ).addInstance( this.barrierRectangle );
// Fit to the window and render the initial scene
$( window ).resize( function() { sim.resizeToWindow(); } );
this.resizeToWindow();
// Kick off checking for updates, if that is enabled
UpdateCheck.check();
// @public (joist-internal) - Keep track of the previous time for computing dt, and initially signify that time
// hasn't been recorded yet.
this.lastTime = -1;
// @public (joist-internal) - Bind the animation loop so it can be called from requestAnimationFrame with the right
// this
this.boundRunAnimationLoop = this.runAnimationLoop.bind( this );
this.trigger0( 'simulationStarted' );
// Signify the end of simulation startup. Used by PhET-iO.
this.trigger0( 'endedSimConstructor' );
} This also meant that sims mains cannot call sim.start() immediately after the constructor finishes, hence I created a "initialized" callback that can be used like this (example from Function Builder): var options = {
credits: {
leadDesign: 'Amanda McGarry',
softwareDevelopment: 'Chris Malley (PixelZoom, Inc.)',
team: 'Amy Hanson, Karina K. R. Hensberry, Ariel Paul, Kathy Perkins,\nSam Reid, Beth Stade, David Webb',
qualityAssurance: 'Steele Dalton, Amanda Davis, Andrea Lin, Ben Roberts'
},
initialized:function(sim){sim.start();}
}; Once these changes were complete, it was possible to update the DOM between each screen initialization: This also suggests that there is a lot of work being done before the first screen initialization starts, probably by SimLauncher initializing downloaded assets, so this could be displayed as a separate "segment" in the loading bar. |
I'd like to help out on this issue when I have time. |
I'm going to split this into 2 separate issues: showing animation while files are downloading and showing js execution progress. |
Let's proceed in the two issues mentioned above. |
Similar to the 'infinte scrolling' progress bar?
The text was updated successfully, but these errors were encountered: