Thursday, December 22, 2011

HTML5 history support in mobile browsers

For those of you who don`t know theres a new HTML5 API out there for handling the browsers history. (eg. what happens when the user presses back, or reloads the page)

It provides the following functions:

  • change the current history entry (replaceState)
  • add a new history entry onto the stack (pushState)
  • receive an event if the stack has changed(user pressing forward or back)
  • Get the size of the history stack
  • go forward and backwards in history
Together this set of apis provides a nice way to handle the android back button inside a web application. You can actually mimic the behavior of a native app with the back button, as long as android supports the html5 history api.

I was very pleased to see support for this in android 2.2 and android 2.3, but was kind of worried to see that this was not working properly in android 3.x. But I wasn't too worried, because android 3.x was rushed to get out the door and never really had that much devices ( see: http://developer.android.com/resources/dashboard/platform-versions.html)

When I installed android 4.0 last week on my phone I was very surprised to see that mgwt used a fallback mechanism (introduced for the few android 3.x devices) to get the back button working. So I did some digging and was able to find out that that android 4.0 does not support html5 history api.


Why is HTML5 history support important?
If you want to use the back button properly with your android web app, you need to get an event when the user presses that back button. If the browser does not support the html5 history api you can still get an event by using a hashtag mechanism in the url (like GWT has done for quite a long time), but you will not be able to change the current entry on the history stack. So you will get inconsistencies when trying to mimic the behavior of the android activity stack.
The second thing is performance: It seems that the history pop event fires immediately, while the hashtag mechanism needs on my phone at least one second before the gwt app sees the action. This leeds to a serious sluggish feeling when using the back button. I haven't had the time to look into this maybe there is something that can be done about it.

But:
There is a discussion in the gwt contributer group ( https://mail.google.com/mail/#label/gwt-contrib/1345fe21e2ad68ed) that discusses if we can implement a history replace mechanism on top of the hash tag mechanism so there might be a way to get some decent behavior in web apps inside android 4. 

This leaves me wondering what google is doing with android and why it is so hard for them to have a matching browser to apples mobile safari. This is working properly on android 2.2 and 2.3, why is it not working in android 4?

Also there wasn't any issue on the android tracker regarding this. I will be doing some more research and post a bug report eventually, so that we can get this fixed some time in the future... 
Someone else already file a bug (see http://code.google.com/p/android/issues/detail?id=23979)

Also I will try to find a way to get the best possible current solution for mgwt web apps (note: with gwt-phonegap you can get a direct event if the user presses the back button).


Tuesday, December 13, 2011

mgwt 1.0.1 released

A month after the first non beta release, I am very happy to report the next release of mgwt: 1.0.1

This release contains some new features as well as bugfixes. A detailed description can be found here: http://code.google.com/p/mgwt/wiki/Changes

There also have been many improvements to the documentation. I encourage you to check them out here: http://code.google.com/p/mgwt/wiki/


As always the release is also available from central:
<dependency>
      <groupId>com.googlecode.mgwt</groupId>
      <artifactId>mgwt</artifactId>
      <version>1.0.1</version>
</dependency>

Friday, December 2, 2011

Animating in the browser

With this blog post I wanted to give a quick overview about how you can animate inside the browser and give some advice on best practices.


There are several ways to animate inside the browser:
  1. Continuously change screen from javascript
    • using setTimeout / setInterval
    • using requestAnimationFrame
  2. Using the layout engine
    • CSS3 Animations
    • CSS3 Transformations
Using the layout engine for your animations has several benefits. You declare your animations in a declarative fashion and the browser can find an efficient way to display that animation.




Let's take a look at an example. Let's say we want to move a div from position A to position B in a specific time. Something like this:






If we are doing this in Javascript we would be doing something like this: building a function, which moves the div a little and calling that function repeatedly until we reach the end position.
This is what the code would look like:


$(document).ready(function(){
    $("#button").click(function(){
       
        var time = 1000; // animation time in ms
        var start = new Date().getTime();
        var end = start + time;
        var left = 0; //start position
        
        window.setTimeout(animate, 10);
        
        function animate(){
            var now = new Date().getTime();
            left = (now - start) / 10; //update position
             $("#test").offset({top: 0, left: left});
            if(now < end)     
            {
                window.setTimeout(animate, 10);
            }else{
                $("#test").offset({top: 0, left: 100});
            }
        }
        
    });
});




Here is a small jsfiddle with a complete example.


The problem:
This can not be optimized by the browser / layout engine, because it does not know about the whole movement. It only knows about the small fraction of the movement.
Also we need the JavaScript VM running as well as the layout engine, which will consume a lot of CPU cycles. This might be okay for desktop, but it`s bad for mobile.
On mobile we are running on battery with slow CPUs, so we don`t want to do unnecessary things.


So for simple transitions of your UI why do them in JavaScript at all?


The solution:
If we specify the complete transition in a declarative way the browser could take care of optimizing it for us. This is what CSS3 Animations and CSS3 Transitions are about. Instead of updating the position in a "loop" we simply say move from here to there in some time. In CSS3 (for a webkit browser) this looks like this:


.move {
 -webkit-animation-name: moveAnimation; /*name of the animation */
 -webkit-animation-timing-function: ease-in-out; /* timing function */
 -webkit-animation-duration: 1000ms; /* duration for the animation */

}

@-webkit-keyframes moveAnimation {
    from { -webkit-transform: translateX(0); }
    to { -webkit-transform: translateX(100px); }
}

With the keyframes you can control any point in the transition and you can use even more than one css property. Many mobile browser can take something like this and just run it on their GPU, so we don't get any CPU what so ever. This produces very smooth and nice looking animations.


Now you have a basic idea how the two different approaches work. Lets quickly remind ourselves what the pros and cons are:




Animating in JavaScript
  • JavaScript VM needs to run
  • Can not be optimized
  • Good control over every detail
  • Inefficient
Animating with CSS3
  • Only Layout engine needs to run
  • Can be heavily optimized, executed with hardware support
  • Complex updates are hard to write


requestAnimationFrame vs. setTimeout
If you are animating with setTimeout or setInterval you should be considering using requestAnimationFrame. It has two main advantages: It runs synchronized with the display frame rate and it does not run if the webpage is not visible.

If you are using setTimeout for animating your animation loop is even running if the user is currently looking at another tab. This is why browser vendors added requestAnimationFrame so that your code would not be executed if it does not need to. Also you may be calculating things which will never be drawn on screen, because you don`t know when the browser will actually paint the DOM.

Paul Irish has written a nice post about requestAnimationFrame, I encourage you to read it if you want to know more.




Some advice
If you want smooth running animations consider using CSS3 Animations / CSS Transitions. They run much smoother than handwritten animations on many devices, but they can only be used if you are animating DOM Objects.

If you want to animate the content of a canvas element you need to do your updates in javascript using requestAnimationFrame. This is the way canvas is ment to be used and this is fine.

CSS3 Animations and CSS3 Transitions are mostly the same, but the syntax for transitions can be quite clunky if you want to transition over multiple elements.

Depending on your target platform you should be using different approaches. Webkit animations are very good supported with all iOS devices, but with android you should be using CSS3 transition. They work more reliably across android devices.

If you want to see some animations in action take a look at mgwt.