• 沒有找到結果。

The Feel

在文檔中 HTML5 and JavaScript Web Apps (頁 35-45)

Spinning refreshes, choppy page transitions, and periodic delays in tap events are just a few of the headaches you face when attempting to create a mobile web app that behaves like a native one. Developers are trying to get as close to native as they possibly can, but are often derailed by hacks, resets, and rigid frameworks.

Hardware acceleration

Normally, GPUs handle detailed 3D modeling or CAD diagrams, but for mobile web apps, we want our primitive drawings (divs, backgrounds, text with drop shadows, images, etc.) to appear smooth and animate smoothly via the GPU. The unfortunate thing is that most frontend developers are dishing this animation process off to a third-party framework without being concerned about the semantics, but should these core CSS3 features be masked? Consider a few reasons why caring about hardware acceler‐

ation is important:

Memory allocation and computational burden

If you go around compositing every element in the DOM just for the sake of hard‐

ware acceleration, the next person who works on your code may chase you down and beat you severely.

Power consumption and battery life

Obviously, when hardware kicks in, so does the battery. Developers are forced to take the wide array of device constraints into consideration while writing mobile web apps. This will be even more prevalent as browser makers start to enable access to more and more device hardware. Luckily, we will soon have an API for checking the status of the device battery.

Conflicts

You will encounter glitchy behavior when applying hardware acceleration to parts of the page that were already accelerated. So knowing if you have overlapping ac‐

celeration is very important.

To make user interaction smooth and as close to native as possible, you must make the browser work for you. Ideally, you want the mobile device CPU to set up the initial animation, and then have the GPU responsible for only compositing different layers during the animation process. This is what translate3d, scale3d, and translateZ do:

they give the animated elements their own layer, thus allowing the device to render everything together smoothly.

CSS features can come at a cost on low-end devices. When using CSS gradient, box-shadow, borders, and background-repeat , you are using the device GPU to paint your images on the fly. CSS can be very powerful for rendering a nice user interface, but you should avoid doing this type of work in software when it can be prebaked in images.

This means you should use sprites so the device downloads only a single image and embed data URIs in your CSS files for smaller images.

A few animations that don’t require repaints are:

• transition-property

• opacity

• transform

CSS selector performance can cripple older mobile browsers. Using selectors like:

div[style*='foo']

will severely reduce performance on iOS devices up to version 4.3.x.

Interactions and Transitions

Take a look at three of the most common user-interaction approaches when developing a mobile web app: slide, flip, and rotation effects. First, we’ll dissect the slide, flip, and rotation transitions and how they’re accelerated. Notice how each animation requires only three or four lines of CSS and JavaScript. The examples don’t use any additional frameworks, only DOM and vendor prefixed APIs.

You can view this code in action at http://html5e.org/example. The demo is built for a mobile device, so fire up an emulator, use your phone or tablet, or reduce the size of your browser window to 1024px or less.

Sliding

The most common of the three approaches, sliding page transitions, mimics the native feel of mobile applications. The slide transition is invoked to bring a new content area into the view port.

For the slide effect, first you declare your markup:

<div id="home-page"

class="page">

<h1>Home Page</h1>

</div>

<div id="products-page" class="page stage-right">

<h1>Products Page</h1>

</div>

<div id="about-page" class="page stage-left">

<h1>About Page</h1>

</div>

Notice that the pages are staged left and right. You could place them in any direction, but this is most common.

We now add animation plus hardware acceleration with just a few lines of CSS. The actual animation happens when we swap classes on the page div elements.

.page {

position: absolute;

width: 100%;

height: 100%;

/*activate the GPU for compositing each page */

-webkit-transform: translate3d(0, 0, 0);

}

Although translate3d(0,0,0) is known as the silver bullet approach for WebKit, other browser engines like fennec (Mobile Firefox) and Opera Mobile do not support, or are just implementing, translate3d as of this writing. They do support 2D transformations, which cut out the Z-axis, so to support these browsers, you need to change:

transale3d(X,Y,Z); // or

translateX(X), translateY(Y), translateZ(Z);

to:

translate(X,Y);

The one downside to 2D transforms is that, unlike 3D transforms, they are not GPU accelerated.

Hardware acceleration tricks do not provide any speed improvement under Android Froyo 2.2 and beyond. All composition is done within the software.

When the user clicks a navigation element, we execute the following JavaScript to swap the classes. We’re not using any third-party frameworks yet, this is straight up JavaScript!

function slideTo(id) {

//1.) the page we are bringing into focus dictates how // the current page will exit. So let's see what classes // our incoming page is using.

//We know it will have stage[right|left|etc...]

var classes = getElement(id).className.split(' ');

//2.) decide if the incoming page is assigned to right or left // (-1 if no match)

var stageType = classes.indexOf('stage-left');

//3.) on initial page load focusPage is null, so we need // to set the default page which we're currently seeing.

if (FOCUS_PAGE == null) { // use home page

FOCUS_PAGE = getElement('home-page');

}

//4.) decide how this focused page should exit.

if (stageType > 0) {

FOCUS_PAGE.className = 'page transition stage-right';

} else {

FOCUS_PAGE.className = 'page transition stage-left';

}

//5. refresh/set the global variable FOCUS_PAGE = getElement(id);

//6. Bring in the new page.

FOCUS_PAGE.className = 'page transition stage-center';

}

stage-left or stage-right becomes stage-center and forces the page to slide into the center view port. We are completely depending on CSS3 to do the heavy lifting.

.stage-left {

By controlling the animations through swapping the stage classes in JavaScript, we are decoupling the CSS implementations from JavaScript. We could, however, control all the presentation logic within JavaScript by using:

FOCUS_PAGE.style.transform =

"translate(X,Y)";

Each browser vendor may be using a specific vendor prefix for the transform capabilities.

One quick way of checking to see what your target browser supports is to use:

var getTransformProperty =

This slide effect has been tested on Mobile Safari, Android, Mobile Firefox (Figure 3-2), and Opera Mobile (Figure 3-3).You can also see the source code that supports all the aforementioned browsers.

Figure 3-2. Slide transitions running on Mobile Firefox (Fennec)

Figure 3-3. Slide transitions running on Opera Mobile 12

Flipping

On mobile devices, flipping is characterized by actually swiping the page away. Here you can use some simple JavaScript to handle the event on iOS and Android (WebKit-based) devices. Here is an example of flipping in action and the source at github.

When dealing with touch events and transitions, the first thing you’ll want is to get a handle on the current position of the element. Thanks to the CSSMatrix interface, which is implemented by WebKit only at the time of this writing, you can get an instance of WebKitCSSMatrix by passing the current transform’s computed style.

function pageMove(event) { // get position after transform var curTransform =

new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);

var pagePosition = curTransform.m41;

}

Because we are using a CSS3 ease-out transition for the page flip, the usual element.off setLeft will not work.

For more information on WebKitCSSMatrix, go to Apple’s developer page.

Next we want to figure out which direction the user is flipping and set a threshold for an event (page navigation) to take place.

if (pagePosition >= 0) {

//moving current page to the right //so means we're flipping backwards

if ((pagePosition > pageFlipThreshold) ||

(swipeTime < swipeThreshold)) {

//current page is sliding to the left if ((swipeTime < swipeThreshold) ||

(pagePosition < pageFlipThreshold)) {

You’ll also notice that we are measuring the swipeTime in milliseconds as well. This allows the navigation event to fire if the user quickly swipes the screen to turn a page.

To position the page and make the animations look native while a finger is touching the screen, we use CSS3 transitions after each event firing.

function positionPage(end) {

page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)';

if (end) {

page.style.WebkitTransition = 'all .4s ease-out';

//page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)' } else {

page.style.WebkitTransition = 'all .2s ease-out';

}

For this example, ease-out did the trick, but for your own projects, play around with cubic-bezier to give the best native feel to your transitions.

Finally, to make the navigation happen, we must call the previously defined slide To() methods used in the last example.

track.ontouchend =

Next, take a look at the rotate animation being used in this demo. At any time, you can rotate the page you’re currently viewing 180 degrees to reveal the reverse side by tapping on the Contact menu option. Again, this only takes a few lines of CSS and some Java‐

Script to assign a transition class onclick.

The rotate transition isn’t rendered correctly on most versions of An‐

droid, because it lacks 3D CSS transform capabilities. Unfortunately, instead of ignoring the flip, Android makes the page “cartwheel” away by rotating instead of flipping. I recommend using this transition spar‐

ingly until support improves.

Here’s the full source, but here’s the markup (basic concept of front and back):

<div id="front"

class="normal">

...

</div>

<div id="back" class="flipped">

<div id="contact-page" class="page">

<h1>Contact Page</h1>

</div>

</div>

The JavaScript you need is:

function flip(id) {

// get a handle on the flippable region var front = getElement('front');

var back = getElement('back');

// again, just a simple way to see what the state is var classes = front.className.split(' ');

var flipped = classes.indexOf('flipped');

if (flipped >= 0) {

front.className = 'flipped';

back.className = 'normal';

FLIPPED = true;

} }

Finally, here is the relevant CSS:

#back,

在文檔中 HTML5 and JavaScript Web Apps (頁 35-45)

相關文件