Les nouveautés et Tutoriels de Votre Codeur | SEO | Création de site web | Création de logiciel

seo Gmail for Mobile HTML5 Series: CSS Transforms and Floaty Bars 2013

Seo Master present to you: On April 7th, Google launched a new version of Gmail for mobile for iPhone and Android-powered devices. We shared the behind-the-scenes story through this blog and decided to share more of what we've learned in a brief series of follow-up blog posts. This week, I'll talk about different ways to animate the floaty bar.

Even from the earliest brainstorming days for our new version of Gmail for iPhone and Android-powered devices, we knew we wanted to try something novel with menu actions: a context-sensitive, always-accessible UI element that follows conveniently as a user scrolls. Thus, the "floaty bar" was born! It took us a surprisingly long time, experimenting with different techniques and interactions, to converge on the design you see today. Let's look under the covers to see how the animation is achieved. You may be surprised to find that the logic is actually quite simple!


Screenshots of the floaty bar in action

In CSS:
.CSS_FLOATY_BAR {
...
top: -50px; /* start off the screen, so it slides in nicely */
-webkit-transition: top 0.2s ease-out;
...
}
In JavaScript:
// Constructor for the floaty bar
gmail.FloatyBar = function() {
this.menuDiv = document.createElement('div');
this.menuDiv.className = CSS_FLOATY_BAR;
...
};

// Called when it's time for the floaty bar to move
gmail.FloatyBar.prototype.setTop = function() {
this.menuDiv.style.top = window.scrollY + 'px';
};

// Called when the floaty bar menu is dismissed
gmail.FloatyBar.prototype.hideOffScreen = function() {
this.menuDiv.style.top = '-50px';
};

gmail.floatyBar = new gmail.FloatyBar();

// Listen for scroll events on the top level window
window.onscroll = function() {
...
gmail.floatyBar.setTop();
...
};
The essence here is that when the viewport scrolls, the floaty bar 'top' is set to the new viewport offset. The -webkit-transition rule specifies the animation parameters. (The 'top' property is to be animated, over 0.2s, using the ease-out timing function.) This is the animation behavior we had at launch, and it works just fine on Android and mobile Safari browsers.

However, there's actually a better way to achieve the same effect, and the improvement is particularly evident on mobile Safari. The trick is to use "CSS transforms". CSS transforms is a mechanism for applying different types of affine transformations to page elements, specified via CSS. We're going to use a simple one which is translateY. Here's the same logic, updated to use CSS transforms.

In CSS:
.CSS_FLOATY_BAR {
...
top: -50px; /* start off the screen, so it slides in nicely */
-webkit-transition: -webkit-transform 0.2s ease-out;
...
}
In JavaScript:
// Called when it's time for the floaty bar to move
gmail.FloatyBar.prototype.setTop = function() {
var translate = window.scrollY - (-50);
this.menuDiv.style['-webkit-transform'] = 'translateY(' + translate + 'px)';
};

// Called when the floaty bar menu is dismissed
gmail.FloatyBar.prototype.hideOffScreen = function() {
this.menuDiv.style['-webkit-transform'] = 'translateY(0px)';
};
Upon every scroll event, the floaty bar is translated vertically to the new viewport offset (modulo the offscreen offset which is important to the floaty bar's initial appearance). And, why exactly is this such an improvement? Even though the logic is equivalent, iPhone OS's implementation of CSS transforms is "performance enhanced", whilst our first iteration (animating the 'top' property) is performed by the OS in software. That's why the experience was unfortunately somewhat chunky at times, depending on the speed of the iPhone hardware.

You'll see smoother looking floaty bars coming very soon to an iPhone near you. This is just the first in a series of improvements we're planning for the mobile Gmail floaty bar. Watch for them in our iterative webapp, rolling out over the next couple of weeks and months!



Previous posts from Gmail for Mobile HTML5 Series:
HTML5 and Webkit pave the way for mobile web applications
Using AppCache to launch offline - Part 1
Using AppCache to launch offline - Part 2
Using AppCache to launch offline - Part 3
A Common API for Web Storage
Suggestions for better performance
Cache pattern for offline HTML5 web application
Using timers effectively
Autogrowing Textareas
Reducing Startup Latency
2013, By: Seo Master

seo CSS3 Transitions and Transforms in Gmail for the iPad 2013

Seo Master present to you: The Mobile Gmail team recently announced a new stacked cards interface for the iPad. In this interface we make use of CSS3 transitions and transforms to provide a more intuitive interface that has a look and feel that is similar to native applications. In this post we will describe CSS3 transitions and transforms and how they were used to develop this interface. All of the CSS and JavaScript examples currently work only in WebKit-based browsers, such as Safari on the iPad. However, Mozilla-based browsers have their own versions of WebKit-based extensions that use the ‘-moz’ prefix, and that should behave similarly.

CSS3 Transitions

CSS3 transitions allow the browser to animate the change of a CSS property from an initial value to a final value. A transition is configured by setting four CSS properties on an HTML element:
  • -webkit-transition-property
  • -webkit-transition-duration
  • -webkit-transition-timing-function
  • -webkit-transition-delay
The -webkit-transition-property property identifies the CSS properties where changes to the property will trigger a transition between the old value of the property and the new value. The -webkit-transition-duration property specifies, in milliseconds, the length of time over which the transition should take place. The -webkit-transition-timing-function property describes the speed at which the transition progresses over the duration of the transition. For example, -webkit-transition-timing-function: ease-in-out describes a transition that will proceed slowly at the beginning and the end of the transition, but that will proceed quickly during the middle of the transition. You can also provide a custom, cubic-bezier function for a higher degree of control over the timing. The -webkit-transition-delay property specifies a delay, measured in milliseconds, before the transition begins.

The transition properties can also be set simultaneously using the -webkit-transition property, by simply specifying them in the above order. Once the transitions properties are set and up to the point where the value of -webkit-transition-property is changed, all modifications of the specified CSS properties will trigger transitions.

CSS3 Transforms

CSS3 transforms allow the rendering of an HTML element to be modified using 2D and 3D transformations such as rotation, scaling, and translations. Transforms are applied by setting the -webkit-transform CSS property with the desired list of transforms. Each transform takes the form of a transformation function, such as translate3d or rotate, and a list of parameters enclosed in brackets. For example, to move an object to the right by 100 pixels and rotate it by 45 degrees you can use the -webkit-transform property:

-webkit-transform: translate(100px, 0) rotate(45deg);

Using -webkit-transform as the transition property when moving an element is advantageous relative to using the standard top and left properties because transitions using -webkit-transform are hardware-accelerated in Safari. An exception here is that it seems that 2D translations are not hardware-accelerated. But, since any 2D translation is equivalent to a corresponding 3D translation with the same translations in the x and y and no translation in the z axis, it is easy to use a hardware accelerated translate3d(x, y, 0) transform instead of a non-hardware accelerated translate(x, y) transform.

Terminology

There are a few terms here that begin with ‘trans,’ and they can easily be confused if you are not familiar with them. Here they are again:
  • Transition: An implicit animation of CSS properties between an initial and a final value.
  • Transform: A modification to the appearance of an HTML element by manipulating it in a 2D or 3D space.
  • Translation: A particular type of transformation that moves the HTML element in 2D or 3D space.
Stacked Cards Interface

In the stacked cards interface, cards representing selected conversations transition onto the screen when their corresponding conversation is selected, and transition off of the screen when it is deselected.

When cards are selected, they are transitioned out from underneath the conversation list on the left side of the application into the selected conversation area on the right side of the application. To move the card onto the screen, we set an initial transform, configure the transition, and finally apply the desired final transform to the element.

To simplify the layout, the un-transformed position of each card is its normal position in the selected conversation area. This allows the card to have no translation applied when in this position, although it will need a translation to animate the movement. Initially the card has a transform that translates it to the left. The z-index property is used to ensure that the card will render underneath the conversation list. The rotation of the card is also initially applied, since we chose not to have the card rotate as it transitions onto the screen.

card.style.WebkitTransform =
‘translate3d(-700px, 0, 0) rotate(5deg)’;

Since the particular translation and rotation can vary, we chose to apply this property using JavaScript rather than including it in the CSS class applied to the card. It is important that the CSS3 transition is not yet applied because we do not want this transform to be a transition. Moreover, it is important that the card is rendered at its initial transform before the transition is configured and the destination transform is applied. This is easily achieved by wrapping these next steps in a call to window.setTimeout with a timeout of 0 ms.

window.setTimeout(function() {
card.style.WebkitTransition =
‘-webkit-transform 300ms ease-in-out’;
card.style.WebkitTransform =
‘translate3d(0, 0, 0) rotate(5deg)’;
}, 0);

Completion of the Transition

It is useful to know when the transition is complete. In the stacked cards interface, we use this to improve performance by setting display:none on obscured cards so that they do not need to be rendered. Adding an event listener allows the application to be notified when the transition has completed.

element.addEventListener(‘webkitTransitionEnd’, listener, false);

Interrupting a Transition

In some cases, you may want to change a transition while it is in progress. For example, if the user unselected a conversation while the corresponding card was still animating onto the screen, we might apply a new transition to send the card back off of the screen again. When you apply a new CSS value while a transition is already in progress, a new transition will occur between the current value of the property in the transition and the new value that you apply. For example, suppose a card is halfway through it’s transition onto the screen, and we apply this CSS transform:

card.style.WebkitTransform = 
‘translate3d(-700px, 0, 0) rotate(5deg)’;

Since the transition properties are still configured, a new transition will occur. The initial value for the transition will be the halfway point - approximately translated3d(-350px, 0, 0) rotate(5deg). The final value will be translate3d(-700px, 0, 0) rotate(5deg). The full duration of the transition will still apply, so the card will move about half as quickly as it usually does. It is possible to determine the current transform applied to an HTML element using the WebKitCSSMatrix and to use this to recalculate more appropriate transition parameters, but this is outside the scope of this post.

Conclusion
I hope that this introduction to CSS3 transitions and transforms has been useful, and that the insight into the implementation of Mobile Gmail on the iPad has been interesting. Based on positive feedback, the Mobile Gmail team is looking forward to making more use of transitions and transforms in the future.

2013, By: Seo Master

seo Gmail for Mobile HTML5 Series: Autogrowing Textareas 2013

Seo Master present to you: On April 7th, Google launched a new version of Gmail for mobile for iPhone and Android-powered devices. We shared the behind-the-scenes story through this blog and decided to share more of what we've learned in a brief series of follow-up blog posts. This week I'll talk about autogrowing textareas for entering large amounts of text.

When composing a long message in a web app, regardless of whether it's on a desktop or a mobile device, you really want to see as much of your draft as possible and make use of all the available screen space.
One of my biggest gripes are fixed-size textareas that restrict me to only a couple lines of visible text when my screen is actually many times larger than the size of the textarea.

In today's blog post, I'll share a JavaScript solution for textareas that automatically grow (vertically) to the size of the content. They make composing long messages much easier and, for all those iPhone users out there, takes away the need to scroll with the dreaded magnifying glass! We're working on getting this into Gmail for mobile but here it is now as a teaser of things to come.





Measuring the height of the content

The first step is to detect when the content has changed. Some solutions on the net recommend using a timer (see our previous post to find out more about timers) to check if content has changed. However, that approach is not ideal on a mobile device, where both battery life and processor power are limited.

Instead, we will listen for key-up events from the browser. This guarantees that we only measure the textarea when the content has actually changed.

<textarea id="growingTextarea" onkeyup="grow();"></textarea>

The second step is to actually measure the height of the content. There are solutions on the net that recommend keeping a copy of the content in a div and measuring the div to get the height; however, due to memory and processor limitations on a mobile device, those solutions don't scale well when the content gets large (and it's also hard to replicate textarea line wrapping behavior exactly in a div).

Therefore we will make use of the scrollHeight and clientHeight properties. For our purposes, scrollHeight is the height of all the content while clientHeight is the height of the content that's visible in the textarea (for more precise definitions, see scrollHeight and clientHeight)
function grow() {
var textarea = document.getElementById('growingTextarea');
var newHeight = textarea.scrollHeight;
var currentHeight = textarea.clientHeight;

}
One limitation of using scrollHeight and clientHeight is that we aren't able to shrink the textarea when content is deleted. When all the content of a textarea is visible, the scrollHeight is equal to the clientHeight. Therefore we aren't able to detect that our textarea is actually larger than the minimum size required to fit all the content (please do leave a comment if you think of a solution that doesn't require re-rendering the page).

Growing the textarea

To grow the text area, we modify the height CSS property:
if (newHeight > currentHeight) {
textarea.style.height = newHeight + 5 * TEXTAREA_LINE_HEIGHT + 'px';
}
Notice how we only change the height if newHeight > currentHeight. Depending on the browser, changing the height (even if it's to the same value) will cause the page to re-render. On a mobile device, we want to try our best to minimize the number of operations.

Also, we grow the textarea by five lines every time we grow. From a UI perspective, this reduces the amount of jitter when composing a message but, from a performance perspective, this reduces the number of times we need to re-render the page.

(Quick note for developers implementing this for a browser with scrollbars: you might want to modify the CSS overflow property to preventing the scrollbar from appearing and disappearing as you grow your textarea)

The complete solution

Growing textareas are easy to implement and they make composing long messages infinitely more usable. So go out there add it to all your web apps!
<script>
// Value of the line-height CSS property for the textarea.
var TEXTAREA_LINE_HEIGHT = 13;

function grow() {
var textarea = document.getElementById('growingTextarea');
var newHeight = textarea.scrollHeight;
var currentHeight = textarea.clientHeight;

if (newHeight > currentHeight) {
textarea.style.height = newHeight + 5 * TEXTAREA_LINE_HEIGHT + 'px';
}
}
</script>
<textarea id="growingTextarea"
onkeyup="grow();">
</textarea>

Previous posts from Gmail for Mobile HTML5 Series
Using timers effectively

2013, By: Seo Master

seo Gmail for Mobile HTML5 Series: Suggestions for Better Performance 2013

Seo Master present to you: On April 7th, Google launched a new version of Gmail for mobile for iPhone and Android-powered devices. We shared the behind-the-scenes story through this blog and decided to share more of our learnings in a brief series of follow-up blog posts. This week, I'll talk about a few small things you can do to improve performance of your HTML5-based applications. Our focus here will be on performance bottlenecks related to the database and AppCache.

Optimizing Database Performance

There are hundreds of books written about optimizing SQL and database performance, so I won't bother to get into these details, but instead focus on things which are of particular interest for mobile HTML5 apps.

Problem: Creating and deleting tables is slow! It can take upwards of 200 ms to create or delete a table. This means a simple database schema with 10 tables can easily take 2-4 seconds (or more!) just to delete and recreate the tables. Since this often needs to be done at startup time, this really hurts your launch time.

Solution: Smart versioning and backwards compatible schema changes (whenever possible). A simple way of doing this is to have a VERSION table with a single row that includes the version number (e.g., 1.0). For backwards-compatible version changes, just update the number after the decimal (e.g., 1.1) and apply any updates to the schema. For changes that aren't backwards compatible, update the number before the decimal (e.g., 2.0) at which point you can drop all the tables and recreate them all. With a reasonable schema design to begin with, it should be very rare that a schema change is not backwards compatible and even if this happens every month or so, users should get to use your application 20, 30 even 100 times before they hit this startup delay again. If your schema changes very infrequently, a simple 1, 2, 3 versioning scheme will probably work fine; just make sure to only recreate the database when the version changes!

Problem: Queries are slow! Queries are faster than creates and updates, but they can still take 100ms-150ms to execute. It's not uncommon for traditional applications to execute dozens or even hundreds of queries at startup – on mobile this is not an option.

Solution: Defer and/or combine queries. Any queries that can be deferred from startup (or at any other significant point in the application) should be deferred until the data is absolutely needed. Adding 2-3 more queries on a user-driven operation can turn an action from appearing instantaneous to feeling unresponsive. Any queries that are performed at startup should be optimized to require as few hits to the database as possible. For example, if you're storing data about books and magazines, you could use the following two queries to get all the authors along with the number of books and magazine articles they've writen:

SELECT Author, COUNT(*) as NumArticles
FROM Magazines
GROUP BY Author
ORDER BY NumArticles;

SELECT Author, COUNT(*) as NumBooks
FROM Books
GROUP BY Author
ORDER BY NumBooks;


This will work fine, but the additional query will generally cost you about 100-200 ms over a different (albeit less pretty) query like:

SELECT Author, NumPublications, PubType
FROM (
SELECT Author, COUNT(*) as NumPublications, 'Magazine' as PubType, 0 as SortIndex
FROM Magazines
GROUP BY Author
UNION
SELECT Author, COUNT(*) as NumPublications, 'Book' as PubType, 1 as SortIndex
FROM Books
GROUP BY Author
)
ORDER BY SortIndex, NumPublications;

This will return all the entries we want, with the magazine entries first in increasing order of number of articles, followed by the book entries, in increasing order of the number of books. This is a toy example and there are clearly other ways of improving this, such as merging the Magazines and Books tables, but this type of scenario shows up all the time. There's always a trade-off between simplicity and speed when dealing with databases, but in the case of HTML5 on mobile, this trade-off is even more important.

Problem: Multiple updates is slow!

Solution: Use Triggers whenever possible. When the result of a database update requires updating other rows in the database, try to do it via SQL triggers. For example, let's say you have a table called Books listing all the books you own and another called Authors storing the names of all the authors of books you own. If you give a book away, you'll want to remove it from the Books table. However, if this was the only book you owned by that author, you would also want to remove the author from the Authors table. This can be done with two UPDATE statements, but a "better" way is to write a trigger that automatically deletes the author from the Authors table when the last book by this author is removed. This will execute faster and because triggers happen asynchronously in the background, it will have less of an impact on the UI than executing two statements. Here's an example of a simple trigger for this case:

CREATE TRIGGER IF NOT EXISTS RemoveAuthor
AFTER DELETE ON Books
BEGIN
DELETE FROM Authors
WHERE Author NOT IN
(SELECT Author
FROM Books);
END;
We'll get into more detail on triggers and how to use them in another performance post to come.

Optimizing AppCache Performance

Problem: Logging in is slow!

Solution: Avoid redirects to the login page. App-Cache is great because it can launch the application without needing to hit the network, which makes it much faster and allows you to launch offline. One problem you might encounter though, is that the application will launch and then you'll need to hit the network to get some data for the current user. At this point you'll have to check that the user is authenticated and it might turn out that they're not (e.g., their cookies might have expired or have been deleted). One option is to redirect the user to a login page somewhere, allow him to authenticate and then redirect him back to the application. Regardless of whether or not the login page is listed in the manifest, when it redirects back to your application, the entire application will reload. A nicer approach is for the application itself to display an authentication interface which sends the credentials and does the authentication seamlessly in the background. This will avoid any additional reloads of the application and makes everything feel faster and better integrated.

Problem: AppCache reloading causes my app to be slow!

Solution: List as few URLs in the manifest as possible. In a series of posts on code.google.com, we talked about the HTML5 AppCache manifest file. An important aspect of the manifest file is that when the version gets updated, all the URLs listed in the file are fetched again. This happens in the background while the user is using the application, but opening all these network connections and transferring all that data can cause the application to slow down considerably during this process. Try to setup your application so that all the resources can be fetched from as few URLs as possible to speed up the manifest download and minimize this effect. Of course you could also just never update your manifest version, but what's the point of having rapid development if you never make any changes?


That's a brief intro to some performance considerations when developing HTML5 applications. These are all issues that we ran into ourselves and have either fixed or are in the process of fixing in our application. I hope this helps you to avoid some of the issues we ran into and makes your application blazing fast!

We plan to write several more performance related posts in the future, but for now stay tuned for next post where we'll discuss the cache pattern for building offline capable web applications.



Previous posts from Gmail for Mobile HTML5 Series
HTML5 and Webkit pave the way for mobile web applications
Using AppCache to launch offline - Part 1
Using AppCache to launch offline - Part 2
Using AppCache to launch offline - Part 3
A Common API for Web Storage
2013, By: Seo Master
Powered by Blogger.