Matt DuVall

Learning Android: Transitioning

Introduction

Recently I have started learning Android development since I got a Nexus 5 and it seemed like a fun thing to do. It's been an interesting experience picking up something completely new - I really haven't touched Java since college and hadn't used an Android phone before. My first app hit the Google Play stores this morning and it's been a fun week of hacking away, check out Shiba. It's a great way to handle your Stripe payments.

So let's get to it! While furiously Googling around and searching Stack Overflow I ran into a few problems that weren't well documented on the internet (or at least that I could find), so this is the beginning of a series on some things I picked up along the way. We'll get started with transition animations between activities.

Transitioning Activities

When first starting your Android application you will have a single activity, and then as your application grows you will indubitably add another activity. The typical way of starting an activity is to package up an intent and call startActivity. It looks something like this:

Intent i = new Intent(this, <ActivityClass>.class);
startActivity(i);

This will create an intent that will be used to start an activity, the intent is packaged with the activity's class. The first thing to notice is that the default animation applied between activities is not the slide in and scale out animation that has been popularized by Twitter and Vine's Android applications (and by now, used by a good majority of Android applications). By default it is a small translation with a fade out and fade in of the started activity. Let's talk about overriding this animation to have the same animations as the Twitter application.

Overriding Transitions

Android provides a hook to override the activity animations (or any pending animation), it is called overridePendingTransition. It takes in two parameters: an enter animation for the started activity and an exit animation for the initial activity. We'll want one animation to scale and the other animation to slide. For the following code examples let's discuss a new activity being started: the enter animation will slide in from the right and the exit animation will scale down on the Z-axis. The animation for scaling down will go in the res/anim folder and will consist of two animation components: a scaling animation and a fade animation. It will look like this...

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale android:fromXScale="100%p"
           android:toXScale="80%p"
           android:fromYScale="100%p"
           android:toYScale="80%p"
           android:pivotX="50%p"
           android:pivotY="50%p"
           android:duration="@android:integer/config_mediumAnimTime" />
    <alpha android:fromAlpha="1"
           android:toAlpha="0.0"
           android:duration="@android:integer/config_mediumAnimTime" />
</set>

The animation to slide in from the right is available in android.R.anim but let's re-create it. It's a simple translation from the right with the same fade we used in the scaling animation.

<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
    <translate android:duration="300" android:fromXDelta="100%" android:toXDelta="0%" />
    <alpha android:duration="300" android:fromAlpha="0.0" android:toAlpha="1.0" />
</set>

Transition Hooks

With these two animations we can animate the started activity by overriding the transition right after startActivity:

startActivity(intent);
overridePendingTransition(R.anim.slide_in_right, R.anim.scale_down);

Annoyingly (hopefully I can be corrected here), the transitions must be called from every single entry point of a started activity, it's easy enough to wrap the two together but confusing that no methods on Activity allow a hook for this. When going back the transition must be overridden for both the action bar up button and the phone's back button. Override onOptionsItemSelected and onBackPressed.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        finish();
        overridePendingTransition(R.anim.scale_up, R.anim.slide_out_right);
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void onBackPressed() {
    super.onBackPressed();
    overridePendingTransition(R.anim.scale_up, R.anim.slide_out_right);
}

Hopefully that was enough to get your app's activities animating nicely! Stay tuned for more Android goodness in the coming weeks and feel free to reach out to me on Twitter @mduvall_ with any corrections/feedback!

July 21, 2014

Fun with Selects

For most frontend developers, the phrase "custom form elements" is something that belongs in nightmares. No element that is put in a form is spared it's out-of-the-box look. One of the abused form elements, <select>, is typically replaced in HTML forms with completely customized dropdown markup that is tied together with Javascript. This isn't optimal for accessibility and is a pain for maintainability. Here we'll discuss the ways we can create customized <select> elements.

Refresher

Let's first discuss quickly what this <select> element should be doing in our markup, W3 has a succinct definition:

The <select> element represents a control for selecting amongst a set of options.

Simple enough. Why then do we have the need to extend what the browser gives us for free? It boils down to overriding the existing styling, but in the interim the implementation becomes muddled. The need to redesign is usually one of two things: attempting to standardize the design cross-browser (as the form control looks different on each browser) or adding more than plain text to the set of options that are available (embedding images in an option). Both are valid concerns from a UX perspective. Let's jump into the ways we can style the element and make developers and designers happy.

Styling

Here we'll enumerate the possible ways to style the <select> element. Take note that this becomes a balancing act, we will have to trade obstrusive markup for consistency in the design.

Use only supported CSS properties

One way to style <select>s is to style only the properties that are supported on the element. This is the way many CSS kits such as Bootstrap implement a customized select menu. Some of the supported properties are background-color, border-radius, and border. That's what most of us would want and it doesn't take much. The upside is that there is no additional markup for developers to rely on and it will just work. The rule could look something like this:

select {
    border: 1px solid #ddd;
    background-color: #fff;
    border-radius: 5px;
}

The downside with this for many people is that there is no gradient support for the background and the arrow looks different on every single browser, for many designers this inconsistent experience is unacceptable:

Use pointer-events for the arrow

One of the ways we can cover the arrow is using the pointer-events CSS property. What exactly is that?

The CSS property pointer-events allows authors to control under what circumstances (if any) a particular graphic element can become the target of mouse events.

This means that if we apply the none value to an arrow we absolutely position, the user will still be able to click on the select element while it's being obscured by a custom arrow. How does this look in the CSS? We simply need to add a psuedo-element after the <select> that is positioned on the right side of the select with the pointer elements off.

select:after {
    pointer-events: none;
    content: "V";
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
}

The downsides to this approach are that pointer-events is not supported on IE and we still can't style beyond the supported properties.

Add an overlay element to the select

The above two options are great, they get us a custom styled event and an arrow that is the same across all the browsers! But what if we want a gradient on the background or full control over every pixel on the form element? Now we get into adding additional markup that will add an overlay that looks like a select and pass it's events to the form element instead of the overlay.

We will have three main components: the outer container, inner container and the select element itself.

  • Outer container: will be setting the position relative so that absolute positioning works within the child container
  • Inner container: will be containing the select element and cutting off the overflow by being slightly thinner than the select (to hide the Firefox arrow that won't go away with appearance:none)
  • Overlay element: will provide the styling for the element and position it over the select box (the arrow will be a pseudo-element on the overlay)

It's a bit more CSS than would fit inline in this post, here's a link to a CodePen here demonstrating this technique.

Additional optimizations

After taking a wide survey of the <select> elements used across major websites, they have all fall into the general buckets above. One of the nice optimizations I found along the way was used on Square Dashboard, where instead of offsetting the width of the Firefox arrow, they use the text-overflow to trigger an overflow that will push the arrow out of the container. The additional property-value pairs look like this:

select {
    text-overflow: "";
    text-indent: 0.01px;
}

Closing Thoughts

Hopefully this was a conclusive post on styling <select> elements, the one thing to remember is that HTML forms should always be using the browser's built in elements, anything that is thrown together with Javascript will be a suboptimal user experience for accessibility and yet another thing that needs to be maintained.

Let me know if there is anything else to add, reach out to me @mduvall_.

July 01, 2014

Dissecting Delight: Stripe Checkout III

Last week we continued a teardown of the wonderful animations in Stripe Checkout and took a deep dive into the modal shake animation used for form validation. This week we will take a look at one of the ways that animations are harnessed for form submission.

Reasoning

Before diving into the CSS that comprises this animation, let's look at why this is a good use of CSS animations. Primarily this is an optimal user experience when there is an asynchronous action happening that may take longer than the user expects (in this case sending a text notification could be on the order of seconds). This animation is not tied to the actual progress, but instead is a placeholder for what would otherwise be a loading state that would last for more than a second. Jakob Nielsen digs in here and reasons that the typical user's flow of thought is around 1 second. This is significant for a flow such as Stripe Checkout where the user is trying to purchase something, and the use of animation here does a good job at keeping the user engaged.

Implementation

This is a relatively straightforward effect to implement given the animation rules we have learned in the past couple weeks. The animation here makes use of two animation rules: translation and transition. The content is set up to be all in the same block, there is a pre-transition state where the content is below the button and a post-transition state where the content is above the button.

We will manipulate the Y axis of the content during the transition and have it fade in and out of view using the opacity of the element. The above picture displays the two transitions we will have, there will be one element that will fade in and slide up (1) and another element in view that will fade out and slide up (2).

The two pieces of content can be placed into the button.

<button class="submit">
  <span class="hello-container">
    <p class="hello-text">Hello!</p>
  </span>
  <span class="goodbye-container">
    <p class="goodbye-text">Goodbye!</p>
  </span>
</button>

And we can have the CSS set the initial state of hidden elements to be below the fold of the button.

.goodbye-container {
  -webkit-transform: translateY(10px);
  opacity: 0;
}

The transition will ease out and have two rules to transition: a transform and opacity. These give us the ability to fade as well as translate:

.slide-out {
  transition: all 400ms ease-out;
  -webkit-transform: translateY(-10px);
  opacity: 0;
}

.slide-in {
  transition: all 400ms ease-out;
  -webkit-transform: translateY(0px);
  opacity: 1;
}

After applying this, it is simple enough to bind an event listener that applies the sliding out to the current content and sliding in to the next content in line to get the effect:

This is a part of the Dissecting Delight series, we initially will be reviewing the Stripe Checkout animations and hopefully many more great experiences. The code is available on Github here, please feel free to make poke me on Twitter if there's anything I can help with or correct!

May 04, 2014

Dissecting Delight: Stripe Checkout II

Last week we began dissecting the animations in Stripe Checkout and tackled the flap effect used to display content from radio buttons. This week we will look at the animation used during form validation:

This animation does an excellent job at letting the user know that the form submission did not pass validation and has form fields to correct. Typically for modal windows the standard user experience is to highlight the fields that have errors and have the user correct them. For small forms this works great, it can often become a goose chase in larger forms to determine whether or not there is an error or if the submission is in progress, and then locate the erroneous field. The shake animation does a great job at notifying the user it definitely did not go through and as Michaël Villar put it: “the form is basically shaking its head at you.”

position/perspective

The Stripe Checkout is a typical modal window overlay implemented in an iframe, we can have a fixed container as the background and the modal window sit in the center of the page. The container will need to apply the perspective property from last week. The perspective will push back the Z-plane and the animation will be more subtle. The actual modal will be put in the center of the page and adjusted with margin to have a symmetrically skewed animation.

.easing-box-container {
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  -webkit-perspective: 500;
  background: rgba(0,0,0,0.6);
}

.easing-box {
  position: absolute;
  width: 300px;
  height: 500px;
  top: 50%;
  left: 50%;
  margin-top: -300px;
  margin-left: -150px;
}

static modal

animation/keyframes

Now that the modal window is positioned in the middle of the page, we want to apply the animation to it. Imagine shaking your head for a moment, the acceleration at the beginning and end are less than middle - this gives us a nice built-in easing function that we can already use: ease-in-out, the easing is in the beginning and the end. Shake your head a few times and end at the original position, notice that each shake the beginning and end positions were a few degrees closer to the original position. For this normal CSS transitions cannot be applied, we want to be able to control each state throughout the animation. In come CSS animation keyframes, this gives us a powerful syntax that will allow the expression of animation state at arbitrary points in the animation. Let's use these keyframes in the context of the ease-in-out easing function.

@-webkit-keyframes shakeAnimation {
    0% { /* initial */ }
    12.5% { /* ease in */ }
    37.5% { /* full-rate animation */ }
    62.5% { /* full-rate animation */ }
    87.5% { /* ease-out animation */ }
    100% { /* end */ }
}

Now that there is an animation defined, it can be referenced from a CSS rule with the animation-name property.

.shake {
  -webkit-animation-duration: 400ms;
  -webkit-animation-timing-function: ease-in-out;
  -webkit-animation-name: shakeAnimation;
}

translateX/rotateY

All that is left is filling in the animation states at each keyframe. The six states we want to use throughout the animation are defined and we know the initial and end states already. The states in between will represent one end of the shake. The Y axis will be rotated and the X axis will be translated a little more to give the element the feel that it is shaking as it is rotating.

The Y rotations can be defined as going from max to min to max-1 to min-1 until the done state is reached. Starting from -5, this produces the rotation sequence (0, -5, 4, -3, 2, -1, 0). This is one too many states, we can discard -3 and put in -2 during the peak of the animation. The X translations will be 1px more than the degree and produces the sequence (0, -6, 5, -3, 2, 0). Let's put these two transform sequences into the keyframes defined above:

@-webkit-keyframes shakeAnimation {
  0% {
    -webkit-transform: translateX(0);
  }

  12.5% {
    -webkit-transform: translateX(-6px) rotateY(-5deg);
  }

  37.5% {
    -webkit-transform: translateX(5px) rotateY(4deg);
  }

  62.5% {
    -webkit-transform: translateX(-3px) rotateY(-2deg);
  }

  87.5% {
    -webkit-transform: translateX(2px) rotateY(1deg);
  }

  100% {
    -webkit-transform: translateX(0);
  }
}

Applying the Animation

The animation is triggered whenever the class .shake is added to an element. This will need to be attached to the button in the form and reapplied when the button is clicked.

var shakeBtnEl = document.getElementById("btn-shake");
shakeBtnEl.addEventListener("click", function(e) {
  var boxEl = document.getElementsByClassName("easing-box")[0];
  boxEl.classList.remove("shake");
  boxEl.classList.add("shake");
});

There is a snag that when the CSS animations are applied and the class list is modified to re-add the .shake class, the animation will not be reapplied. The animation can be manually triggered by having the element reflow itself, this can be done by reassigning the offset width on the element.

boxEl.classList.remove("shake");
boxEl.offsetWidth = boxEl.offsetWidth;
boxEl.classList.add("shake");

Putting this all together, here's the animation in action:

This is a part of the Dissecting Delight series, we initially will be reviewing the Stripe Checkout animations and hopefully many more great experiences. The code is available on Github here, please feel free to make poke me on Twitter if there's anything I can help with or correct!

April 27, 2014

Dissecting Delight: Stripe Checkout I

Stripe Checkout is an example of a delightful application that tastefully uses CSS animations as an essential part of it's user experience. Michaël Villar, a User Interface Designer at Stripe, enumerates the reasons why animations were integrated into the design here. In this article we will dig into the first animation, pictured below.

Unable to find a really pertinent term for this animation, I dubbed it the "flap effect" as it feels like the flap of content is swinging towards the user. The animation is subtly pleasing and adds a level of depth that isn't typically seen in forms around the web.

CSS

The majority of this animation lies in the CSS, it makes use of the CSS rules: transform, transform-origin, perspective, and transition. Let's go through each rule to describe how it contributes to the overall effect.

transform/transform-origin

The transform is responsible applying the rotation along the X-axis, the XY axis here is the plane of the monitor. The content will start at an angle away from the user and swing towards the user, going from -60 degrees to 0 degrees. Here's a diagram of what the rotation looks like to the user.

Since CSS rotation transforms have an origin along the axis of rotation. We will want to change the origin of the Y offset to be along the top of the content, this will make the animation origin the top of the container.

/* Initial */
transform: rotateX(-60deg);

/* End */
transform: rotateX(0deg);

transform-origin: 50% 0;

The initial rotation state looks like this:

The end state:

perspective

If you look at the Stripe animation above, it is clear that the initial state has a perspective applied to the element so it's not a flat transform. The z-axis needs to have a stronger vanishing point and for that we will use the perspective property. Let's apply a large perspective value to the content.

perspective: 300;

With the perspective applied to a containing element of the content, the initial rotation state comes out looking much better:

transition

Now that the start and end states are defined, the glue for the animation needs to be applied. Let's pull out the Swiss Army knife of CSS transitions: transition. To make the content feel like it's coming into focus the opacity property will be transitioned from 0 to 1 as the rotation is changed. When applying the transition there is a plethora of easing functions to use, this site here does a great job at visualizing many of the choices. Let's go with easeOutQuint for the flap.

transition: all 400ms cubic-bezier(0.23, 1, 0.32, 1);

.hidden {
  opacity: 0;
  transform: rotateX(-60deg);
}

.expanded {
  opacity: 1;
  transform: rotateX(0deg);
}

All that's needed now is a sprinkle of JS for toggling the CSS classes when the input is clicked:

var showInputEl = document.getElementById("show-form");

showInputEl.addEventListener("click", function(e) {
  var description = document.getElementsByClassName("description")[0];
  description.className = e.target.checked ? "description expanded" : "description hidden";
});

Here's the final product:

Not too bad!

This is a part of the Dissecting Delight series, we initially will be reviewing the Stripe Checkout animations and hopefully many more great experiences. The code is available on Github here, please feel free to make poke me on Twitter if there's anything I can help with or correct!

Edits

  1. Michaël awesomely replied with a correction that the transition should have a slight bounce, this means changing one of the points in the Bezier to be > 1.0 so that it pops out and goes back into the page. He also suggested gifrocket, which I highly recommend for converting .mov files to .gif.
April 20, 2014