IT tutorials
 
Mobile
 

Mobile Web Apps : Loading Pages (part 1) - Swapping Pages & Fading with WebKit Animations

1/28/2012 5:45:38 PM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
Our client is unlikely to be impressed with the web-like page reloads that currently exist in our app, so we need a way to hide the request/response cycle from the user. There are three main ways we can do this:
  1. putting everything on one page, and then hiding and displaying sections as required

  2. loading in new pages via Ajax

  3. including only the complete skeleton of the app up front, and then bringing in data as required

1. Swapping Pages

If all our content is loaded in a single HTML page, a “page” from the point of view of our application is no longer a full HTML document; it’s merely a DOM node that we’re using as a container. We need to choose a suitable container and an appropriate way to group our pages, so that our scripts can manipulate them consistently.

We’ll start by creating a container <div> (called <pages>), which contains a number of child <div> elements that are the actual pages. There can only be one page visible at a time, so we’ll give that element a <class> of <current>. This <class> will be passed to whichever page is the active one:

<div id="pages">
<div id="page-spots" class="current">
<!-- Spots Index -->
</div>
<div id="page-spot">
<!-- Spot Detail -->
</div>
<div id="page-sightings">
<!-- Add Sighting Form -->
</div>
<div id="page-stars">
<!-- Stars Index -->
</div>
<div id="page-star">
<!-- Star Detail -->
</div>
</div>

This list of pages will sit below the tab bar—so no need to change the markup of our navigation. We have, however, hooked up the links to point to the various sections by way of their <id> attributes; this will let us use a sneaky trick to show pages in the next step:

<ul id="tab-bar">
<li>
<a href="#spots">Spots</a>
</li>
<li>
<a href="#sightings">Add a sighting</a>
</li>
<li>
<a href="#stars">Stars</a>
</li>
</ul>

After this, we need a couple of styles for hiding and showing pages. In our markup, every page is a first-level child of the main #pages container, so we can rely on that fact and use a child selector (>). First, we’ll hide all the pages; then we’ll unhide the page that has the <current> <class>:

listing 1. stylesheets/transitions.css (excerpt)
#pages > div {
display: none;
}
#pages > div.current {
display: block;
}

To actually select some pages, we need to intercept the navigation menu clicks. We’ll be using the code we wrote earlier to capture the event and prevent the browser from navigating to the link:

listing 2. javascripts/ch4/07-swap.js (excerpt)
$("#tab-bar a").bind('click', function(e) {
e.preventDefault();
// Swap pages!
});

And here’s the trick: the links point to our page elements by using the anchor syntax of a hash symbol (#), followed by a fragment identifier. It coincidently happens that jQuery uses that exact same syntax to select elements by <id>, so we can funnel the hash property of the click event directly into jQuery to select the destination page. Very sneaky:

listing 3. javascripts/ch4/07-swap.js (excerpt)
$("#tab-bar a").bind('click', function(e) {
e.preventDefault();
var nextPage = $(e.target.hash);
$("#pages .current").removeClass("current");
nextPage.addClass("current");

});

With the target page acquired, we can hide the current page by removing the <current> <class> and passing it to the destination page. Swapping between pages now works as expected, but there’s a slight problem: the selected icon in the tab bar fails to change when you navigate to another page. Looking back at our CSS, you’ll remember that the tab bar’s appearance is due to a <class> set on the containing <ul> element; it’s a <class> that’s the same as the current page <div> element’s <id>. So all we need to do is slice out the hash symbol from our string (using slice(1) to remove the first character), and set that as the <ul>’s <class>:

listing 4. javascripts/ch4/07-swap.js (excerpt)
$("#tab-bar a").bind('click', function(e) {
e.preventDefault();
var nextPage = $(e.target.hash);
$("#pages .current").removeClass("current");
nextPage.addClass("current");
$("#tab-bar").attr("className", e.target.hash.slice(1));
});

2. Fading with WebKit Animations

The page swap we just implemented is as straightforward as it gets. This has its advantages—it stays out of our users’ way, for one. That said, well-placed transitions between pages not only make your apps sexier, they can provide a clear visual cue to the user as to where they’re being taken.

After the original iPhone was released, web developers leapt to re-implement the native transition effects in JavaScript, but the results were less than ideal, often containing lags and jumps that were very noticeable and distracting to users. The solution largely was to ditch JavaScript for moving large DOM elements, and instead turn to the new and hardware-accelerated CSS3 transitions and animations.

Before we worry about the transitions, though, we need to lay some groundwork. To fling DOM elements around, we need to be able to show, hide, and position them at will:

listing 5. stylesheets/transitions.css (excerpt)
#pages {
position: relative;
}
#pages > div {
display:none;
position: absolute;
top: 0;
left: 0;
width: 100%;
}

By positioning the elements absolutely, we’ve moved every page up into the top-left corner, giving us a neat stack of invisible cards that we can now shuffle around and animate. They’re not all invisible, though; remember that in our HTML, we gave our default page the <class> of <current>, which sets its display property to block.

The difference this time is that we’re going to apply CSS animations to the pages. The incoming (new) page, and the outgoing (current) page will have equal but opposite forces applied to them to create a smooth-looking effect. There are three steps required to do this:

  1. Set up the CSS animations.

  2. Trigger the animation by setting the appropriate classes on the pages.

  3. Remove the non-required classes when the animation is finished, and return to a non-animating state.

Let’s start on the CSS. There are many approaches you can take with the problem of emulating native page transitions. We’ll adopt a flexible method that’s adapted from the jQTouch library. This is a modular approach, where we control transitions by applying and removing the relevant parts of an animation to each page.

Before we dive into that, though, a quick primer on CSS3 animations. These are currently supported only in WebKit browsers with -webkit- vendor prefixes. A CSS3 animation is made up of a series of keyframes grouped together as a named animation, created using the @-webkit-keyframes rule. Then we apply that animation to an element using the -webkit-animation-name property. We can also control the duration and easing of the animation with the -webkit-animation-duration and -webkit-animation-timing-function properties, respectively. If you’re new to animations, this is probably sounding more than a little confusing to you right now; never mind, once you see it in practice, it’ll be much clearer.

So let’s apply some animations to our elements. First up, we’ll set a timing function and a duration for our animations. These dictate how long a transition will take, and how the pages are eased from the start to the end point:

listing 6. stylesheets/transitions.css (excerpt)
.in, .out {
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-duration: 300ms;
}

We’ve placed these properties in generic classes, so that we can reuse them on any future animations we create.

Next, we need to create our keyframes. To start with, let’s simply fade the new page in:

listing 7. stylesheets/transitions.css (excerpt)
@-webkit-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}

In the above rule, fade-in is the name of the animation, which we’ll refer to whenever we want to animate an element using these keyframes. The from and to keywords allow us to declare the start and end points of the animation, and they can include any number of CSS properties you’d like to animate. If you want more keyframes in between the start and end, you can declare them with percentages, like this:

listing 8. stylesheets/transitions.css (excerpt)
@-webkit-keyframes fade-in-out {
from { opacity: 0; }
50% { opacity: 1; }
to { opacity: 0; }
}

With our keyframes declared, we can combine them with the previous direction classes to create the final effect. For our fade, we’ll use the animation we defined above, and also flip the z-index on the pages to make sure the correct page is in front:

listing 9. stylesheets/transitions.css (excerpt)
.fade.in {
-webkit-animation-name: fade-in;
z-index: 10;
}
.fade.out {
z-index: 0;
}

By declaring -webkit-animation-name, we’re telling the browser that as soon as an element matches this selector, it should begin the named animation.

With this CSS in place, we can move to step two. We’ll start by applying our animation to a single navigation item, then broaden it out later so that it will work for our whole tab bar.

The page we’re fading to (#sightings) will need to have three different classes added to it: <current> to make the page visible, <fade> to add our animation, and <in> to apply our timing function and duration. The page we’re fading from (#spots) is visible, so it will already have the <current> <class>; we only need to add the <fade> and <out> classes:

var fromPage = $("#spots"),
toPage = $("#sightings");

$("#tab-sighting a").click(function(){
toPage.addClass("current fade in");
fromPage.addClass("fade out");
});

This gives us a nice fading effect when we click on the “Add a sighting” tab, but now the pages are stuck—stacked atop one another. This is because those <class> names are still there, so the pages now have <current> and they’re both visible. Time to remove them! We’ll do this by binding to the webkitAnimationEnd event, which fires when the transition is complete. When this event fires, we can remove all three classes from the original page, and the <fade> and <in> classes from the new page. Additionally, we must remember to unbind the webkitAnimationEnd event so that we don’t go adding on extra handlers the next time we fade from the page:

var fromPage = $("#spots"),
toPage = $("#sightings");

$("#tab-sighting a").click(function(){
toPage
.addClass("current fade in")
.bind("webkitAnimationEnd", function(){
// More to do, once the animation is done.
fromPage.removeClass("current fade out");
toPage
.removeClass("fade in")
.unbind("webkitAnimationEnd");
})
;
fromPage.addClass("fade out");
});

There we go. Our page is now fading nicely; however, there are a few problems with our code. The first is structural. It will become quite ugly if we have to replicate this same click handler for each set of pages we want to transition to! To remedy this, we’ll make a function called transition() that will accept a page selector and fade from the current page to the new one provided.

While we’re at it, we can replace our bind() and unbind() calls with jQuery’s one() method. This method will accomplish the same task—it binds an event, and then unbinds it the first time it’s fired—but it looks a lot cleaner:

listing 10. javascripts/ch4/08-fade.js (excerpt)
function transition(toPage) {
var toPage = $(toPage),
fromPage = $("#pages .current");

toPage
.addClass("current fade in")
.one("webkitAnimationEnd", function(){
fromPage.removeClass("current fade out");
toPage.removeClass("fade in")
});
fromPage.addClass("fade out");
}


Warning:

Generalizing Functions

You might spy that we’ve hardcoded the current page selector inside our function. This makes our code smaller, but reduces the reusability of the function. If you are building a larger framework intended for more general use, you’d probably want to accept the fromPage as a parameter, too.


Great. Now we have a reusable function that we can employ to fade between any of the pages in our app. We can pull the link targets out of the tab bar the same way we did earlier, and suddenly every page swap is a beautiful fade:

listing 10. javascripts/ch4/08-fade.js (excerpt)
$("#tab-bar a").click(function(e) {
e.preventDefault();
var nextPage = $(e.target.hash);
transition(nextPage);
$("#tab-bar").attr("className", e.target.hash.slice(1));
});

There’s still a major problem, though, and it’s one you’ll notice if you try to test this code on a browser that lacks support for animations, such as Firefox. Because we’re relying on the webkitAnimationEnd event to remove the <current> <class> from the old page, browsers that don’t support animations—and therefore never fire that event—will never hide the original page.


Tip:

Browser Testing

This bug—which would render the application completely unusable on non-WebKit browsers— highlights the importance of testing your code on as many browsers as possible. While it can be easy to assume that every mobile browser contains an up-to-date version of WebKit (especially if you own an iPhone or Android), the real mobile landscape is far more varied.


This problem is easy enough to solve. At the end of our transition() function, we’ll drop in some feature detection code that will handle the simplified page swap in the absence of animations:

listing 11. javascripts/ch4/08-fade.js (excerpt)
function transition(toPage) {

// For non-animatey browsers
if(!("WebKitTransitionEvent" in window)){
toPage.addClass("current");
fromPage.removeClass("current");
return;
}

}

With this code in place, our app now produces our beautiful fade transition on WebKit browsers, but still swaps pages out effectively on other browsers.

There’s still one slightly buggy behavior, one you might notice if you become a little excited and start clicking like crazy. If you click on the link to the current page—or if you tap quickly to start an animation when the previous one has yet to complete—the <class> attributes we’re using to manage the application’s state will be left in an inconsistent state. Eventually, we’ll end up with no pages with the <current> <class>—at which point we’ll be staring at a blank screen.

It’s relatively easy to protect against these cases. We just need to ensure our toPage is different from our fromPage, and that it doesn’t already have the <current><class> on it. This safeguard goes after the variable declaration, and before any class manipulations:

listing 12. javascripts/ch4/08-fade.js (excerpt)
function transition(toPage) {
var toPage = $(toPage),
fromPage = $("#pages .current");

if(toPage.hasClass("current") || toPage === fromPage) {
return;
};



 
Others
 
- Personalize & Secure Your iPad : Adjusting Sounds on your iPad & Personalize Your Picture Frame
- Personalize & Secure Your iPad : Changing your Lock Screen and Home Screen Wallpapers
- Using Media in XNA Game Studio : Media Enumeration
- Using Media in XNA Game Studio : What Is Media?
- Android Application Development : Layouts (part 2) - AbsoluteLayout & RelativeLayout
- Android Application Development : Layouts (part 1) - inearLayout
- Building an Advanced Java Game on Symbian OS (part 4) - Using the Bluetooth API
- Building an Advanced Java Game on Symbian OS (part 3) - Using the Mobile 3D Graphics API
- Building an Advanced Java Game on Symbian OS (part 2) - Using the Mobile Media API & Using the Scalable 2D Vector Graphics API
- Building an Advanced Java Game on Symbian OS (part 1)
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us