Just the weekly tutorials.

Simulating touchscreen inertia with custom JavaScript code

Before we get into this, check out the inertia demo that is discussed in this tutorial.

This year the web will continue to shift toward minified mobile user interfaces.

While many consumers (our clients) compare the mobile devices and the desktop computers with regard to their accessibility (you can't carry a desktop computer in your pocket, yet the phone is not as powerful as the desktop) -- we as web designers and developers -- are more affected by the physical differences between the two. In particular the screen size.

With a smaller screen size comes a new user interface paradigm. The smaller the screen, the less space we have. It's the miniature user controls that are quite different from the ones web designers (and developers) from the 90's and early 2000 are used to.

The importance of touch-screen user controls

The iPhone kicked off the touch-screen revolution. And now the bigger screen of the iPad and similar tablet devices offer touch-screen across a wider screen area. Naturally, this spawned a handful of new ways in which user controls are designed.

Things are now sliding, flipping, switching and swapping. In order to build user interfaces that work we need to understand the space limitations of smaller devices and practical solutions for making things that just work within the context of a smaller screen area.

Moreover...

Evolution of one device influences the evolution of another. The mobile device could eventually influence the desktop computer, too. We've seen this happen with mobile-like UI controls appearing on "regular" full-screen websites designed for the desktop computer.

But will it happen with regard to touch-screen? I don't know for sure, but I believe it will. I will try to explain why I believe it below:

Full size touch-screen monitors are expensive. But because the price of a computer display is tied to its size, the only reason the mobile device was the perfect candidate for the touch-screen experiment is precisely because of its miniature screen.

Even mobile phones themselves, the devices that started the touch-screen revolution are now increasing in size. We have iPods and large tablet devices. If you think about this, a 13" MacBook Pro could already easily feature a touch-screen display.

We also know that prices usually drop as technology and scientific research make progress. In the near future, we might see a full-size desktop monitor making it to our homes. As a web designer, how prepared are you for making these changes?

A web-designer's perspective

Either way, the large majority of computer devices continue shifting toward the touch-screen user interface design. What does it mean to us as web developers, designers and programmers? It means a change is required -- in the way we think about building user interfaces.

There are many distinct techniques for building all kinds of controls. But we must consider one at a time instead of jumping into the fire right away. It's a good rule that... in anything you do, not just programming, that you practice patience.

The subject of this tutorial is touch-screen inertia. It happens when the user slides an object (perhaps a navigation bar) with his finger and expects it to smooth-scroll into the direction of the hand gesture. Moreover, he expects it to scroll at the rate set by his (or of course, her) hand gesture.

This tutorial is not going to focus on any specific user interfaces like navigation bars or buttons. As a creative web designer -- that is your job. But rather, it will focus on the core functionality of the interactive sliding mechanism we are going to build here. It could be applied to a custom design of your own in the future. It's useful in so many situations.

Interactive sliding mechanism

Instead of giving a trivial example I decided to make it more interesting by making the elements look like the air hockey table with a puck. The point is that the puck can be touched and moved around. When you release the puck, it will continue sliding into the direction set by your gesture. After a period of time determined by the strength of gravity (which can be set by you) the puck will come to a complete stop, and can be launched into a new direction again.

Let's first take a look at the core principle that makes the gesture based inertia work. Of course we want to build this as a plugin right away. This way it can be applied to any other <div> element that you wish to inherit the ability to slide/glide by touch.

Limitations

If you ever built a touch-screen slider mechanism you were able to quickly understand that such sliders need to define limitations with regard to how far they can slide. You don't want to slide the puck nor a navigation element completely off the screen.

Boundaries

Therefore, our plugin will be designed in such way that takes parameters that define the boundaries of the sliding element.

Post action

What happens when a boundary is reached? Should we stop movement and put the element to a complete halt? Or should we bounce the element back at what remained of the enertia force after the collision? Maybe the border the element collided with absorbed some of the kinetic energy?

Building the plugin

On page 169 of my tutorial book I explain how, in order to create a jQuery plugin, all we need to do is extend the functionality of the main jQuery object ($ or jQuery) using either the fn or prototype property (which are one and the same) with our own method.

Let's start our plugin by defining the skeleton, naming it "touchable":

$.fn.touchable = function(boundaries, post_action) { /* Plugin code will be written here.. This is the place where magic will happen. */ var selected_elements = this; }

This is the barebone construction method for creating plugins.

Notice that I added two parameters boundaries and post_action to our plugin. These were explained in the two paragraphs just above. We will use them to pass arguments to the plugin which may modify its function. Some plugins take options from the user. This is one of such plugins.

Within the plugin itself the this object will refer to all objects that were selected using the jQuery CSS selector(s), as in:

$("#puck").touchable( [0, 0, 320, 240], "bounce");

Theoretically this will make the element #puck "touchable" (we can drag it and observe the inertia once we release it,) and limited to the imaginary 0,0,230,240 area within the parent element or at least with some regard to the parent element.

That's how this plugin will work in theory. But theory is not enough. Let's write the actual code to make this happen.

The core functionality

I will focus only on the core functionality of the touch-screen code. You can look up the full source code in your browser by following the demo link at the end of this tutorial.

Let's begin...

First. We need to have the ability to calculate the distance between point A and point B. The distance algorithm will help us determine how fast you are moving the touchable object. It will also help us determine the initial inertia force. The faster you move the object the farther away from the original location it will slide.

From algebra we know that the distance between two points can be calculated using the square root equation.

// Calculate the distance between two pixels function distance(x, y, x2, y2) { var nx = (x - x2); var ny = (y - y2); return Math.sqrt(nx*nx + ny*ny); }

The inertia mechanism will always listen for the mousedown event. And the mouseup event. When you move the object with your finger (or mouse.) This is when we determine the distance between location of the object before it's moved, and after it is moved.

We draw the imaginary line between the two points and use information in that line to determine the speed of the puck. The calculated distance is the strength of the inertia. To calculate the distance between two points we need to use the square root equation. In JavaScript the square root function is Math.sqrt(x);

Remember that the coordinate system used by websites extend from the upper left corner into the space below. Where x is the horizontal line going right and y is the vertical going down:

Cartesian coordinate system with x and y axis. The 0 is in the upper left corner. X extends into the right space. Y extends downward.

And direction of the puck can be determined simply by subtracting x-x2 and y-y2. Where x,y is the original location of the puck. And x2,y2 is the position the user "swings" it to (by "drag and drop" action.)

The diagonal line describing the direction of the inertia vector.

Here we are recording motion on both axis x and y at the same time. Of course the same rules apply if we needed to make an element slide across one axis. We would just remove one. However, I wanted to make a slider that worked in all directions. Later on you can simplify it if your user interface or other sliding elements need to slide across a single axis.

Second. Now that we understand the basics of determining inertia force and the direction of our puck we need to create the continuous inertia mechanism for objects that are already sliding. And to turn it off... for objects that are not in motion or stopped sliding.

We will do this by enabling (.on) and disabling (.off) JavaScript event listeners. This will also save computer memory. Of course as you may know .on and .off are jQuery methods that trigger native JavaScript events. But you don't have to worry about that. Just use these methods to add or remove an event listener to an HTML element.

The key here is a timer that runs and continuously calculates new location of the moving object. This is important. This is also where we apply gravity rules to the object and determine whether it went off the screen boundaries specified as the first parameter passed to the plugin.

We can create this timer using JavaScript's setTimeout function. You will see this function used in the source code for this demo.

But when the object stops moving, we shut down the timer. Until the user triggers a new change by touching and sliding the object again.

Let's build this inertia mechanism :-)

Building the Inertia Mechanism

As discussed above, the inertia mechanism consists of two main parts. The initiation of the inertia. And processing the sliding animation. I will explain these below. But first... a small programming lesson.

An HTML element can have something that is called attributes. You already know attributes by their common names. For example href inside the anchor tag A, or src inside the IMG tag. But we can create our own attributes with our own names, for example my_src or my_href. The data will be stored just like in any other attribute. And it can be accessed, too.

Unlike traditional demos, I will store some of the data within the element's attributes in this demo. Not in JavaScript variables. This way they can be accessed from any function. Of course this doesn't mean that we will avoid using variables completely. We will still use variables to work with that data from different functions.

Learning how to write proper code doesn't always mean memorizing the right functions or methods to do something. If you did that, how would you ever know why you are doing it in the first place?

Learning to write code properly means understanding why you are doing what you are doing. And humorously the only way to really learn that is to do things in a way that is not proper. The code will still work, but the lesson here is that you shouldn't do things a certain way -- just because they work.

I do know that the proper way of storing data within an HTML element would be to use jQuery's .data method $("div").data(...) but I am going to avoid using it on purpose. I'll simply use the jQuery's method called .attr to assign custom attributes and value pairs to HTML elements.

$("#puck").attr("inertiax", 10.0); $("#puck").attr("inertiay", 15.0);

This is the equivalent of hand-written:

<div id = "puck" inertiax = "10.0" inertiay = "15.0"/>

Is this the proper way to assign variables to elements? No. But will it work? Yes. ideally you would use the .data method. But we are learning an important lesson here.

By the way, this is not a proper way to assign values to an HTML5 element because:

1. HTML5's specification tells us to prefix all custom attribute names with the word data-

<div id = "puck" data-inertiax = "10.0"/>

and...

2. The jQuery attr method can be used to set or access attributes whose names start with data- but in practice that is not a requirement. "It will still work." So while it is possible, we shouldn't do it mainly because our HTML code will no longer be valid HTML5. If validating your documents is important to you then you must follow the data- rule when writing HTML5 code.

The HTML

The HTML code is very simple. We have a bounding box and the puck:

<div id = "bounding"></div> // The bounding box <div class = "box" id = "div1"> // The puck container <div id = "div3"></div> // The puck background (can be rotated) </div>

Initiating the inertia

Let's write the following code to initiate the inertia on an object.

This is our plugin starting point. Let's call it touchable. Whenever it's applied to an element as in $("div#target").touchable(); the target element acquires the ability to be pushed around.

You will notice that the code and the jQuery methods are extremely simple. What's of more interest here is what they are used for.

$.fn.touchable = function() { /* Warning: All elements that this plugin is applied to must have a unique ID, otherwise the queue will not be able to process them */ /* Warning: All elements must be set to position: absolute;, if not the plugin will force it */ // Apply position:absolute to this element $(this).css("position", "absolute"); // Disable text selection on all divs and body (requires jQuery UI) $("body,div").disableSelection(); // Set default values - 0 inertia on setup $(this).attr("inertiax", 0); $(this).attr("inertiay", 0); $(this).attr("moving", 0); // 0 = "Not moving"; 1 = "Moving" // Track x, y of point A on mousedown $(this).on("mousedown", function(event) { $(this).attr("x", event.pageX); $(this).attr("y", event.pageY); // Display a message on the page $("body").prepend("<b>mousedown</b><br/>"); }); // Track x, y of point B on mouseup, and compare to point A // That's how we calculate inertia values $(this).on("mouseup", function(event) { var newx = event.pageX; // x,y after mouseup var newy = event.pageY; var curx = parseInt($(this).attr("x")); // current x,y var cury = parseInt($(this).attr("y")); // There was a change in inertia if (newx != curx || newy != cury) { var occupied = 0; // Go through the queue to see if this element is already there for (var i = 0; i < queue.length; i++) if ($(this).attr("id") == $(queue[i]).attr("id")) occupied = 1; // Add unless already in the queue if (occupied == 0) queue[queue.length] = this; // Assign "magnitude" of force to this element // I divided it by 4 to decrease the force // Otherwise the puck will just slide like crazy all over the place // It's up to you, your application needs and // your creativity to adjust this value $(this).attr("magnitude", distance(curx, cury, newx, newy) / 4); var inertiax = newx - curx; var inertiay = newy - cury; var ix = inertiax; var iy = inertiay; $(this).attr("forcex", ix/75); // Horizontal force $(this).attr("forcey", iy/75); // Vertical force // Apply new inertia to selected HTML element $(this).attr("inertiax", ix.toFixed(2)); $(this).attr("inertiay", iy.toFixed(2)); // Display a message on the page $("body").prepend("mouseup<br/>"); } }); }

Processing inertia

Now that the values are initiated we need to have some sort of a mechanism that constantly looks for touchable HTML elements to process. If one is in the queue, then we process it (move it toward its destination and apply gravity to it until it stops completely.) If an object is not in the queue, then we do nothing. If an object stops moving, we remove it from the queue.

In order to accomplish this we will write a separate function called processInertia.

This function will be called repeatedly during the lifetime of this web application. We will launch it using JavaScript's setInterval function, to fire every 25 milliseconds:

// Fire processInertia every 25 milliseconds: var timer = setInterval("processInertia()", 25);

Now let's take a look at the function itself.

function processInertia() { // Browse through entire queue for (var i = 0; i < queue.length; i++) { // Is there anything at this index? if (queue[i] != 0) { var ElementMagnitude = parseFloat( $(queue[i]).attr("magnitude") ); var DirectionX = parseFloat($(queue[i]).attr("inertiax")); var DirectionY = parseFloat($(queue[i]).attr("inertiay")); ElementMagnitude = ElementMagnitude -= 0.35; $(queue[i]).attr("magnitude", ElementMagnitude); var Location = $(queue[i]).offset(); var MoveX = DirectionX * ElementMagnitude * 0.005; var MoveY = DirectionY * ElementMagnitude * 0.005; $(queue[i]).offset({ top: Location.top + MoveY, left: Location.left + MoveX }); $("#inx").val(MoveX); $("#iny").val(MoveY); // Stopped, reset inertia so it doesn't stack up if (ElementMagnitude <= 0.005) { $(queue[i]).attr("inertiax", 0); $(queue[i]).attr("inertiay", 0); } // Change direction of the puck if it reaches a bound var bx = 0; var by = 0; var bw = $("#bounding").width() - $("#div1").width(); var bh = $("#bounding").height() - $("#div1").height(); // Test against the bounding box and bounce off the screen edges if (Location.left < bx) { $(queue[i]).offset({left: $(queue[i]).offset().left = bx }); $(queue[i]).attr("inertiax", -DirectionX); rotate_puck(); } if (Location.left > bw) { $(queue[i]).offset({left: $(queue[i]).offset().left = bw }); $(queue[i]).attr("inertiax", -DirectionX); rotate_puck(); } if (Location.top < by) { $(queue[i]).offset({top: $(queue[i]).offset().top = by }); $(queue[i]).attr("inertiay", -DirectionY); rotate_puck(); } if (Location.top > bh) { $(queue[i]).offset({top: $(queue[i]).offset().top = bh }); $(queue[i]).attr("inertiay", -DirectionY); rotate_puck(); } } } }

This tutorial skips the description of the algebra involved. But at the very basic, we simply take point A (mousedown) and point B (mouseup) and calculate the distance between the two on both vertical and horizontal plane. Then we use this information to calculate the direction of the force of that mouse swing. We reduce the force by dividing the numbers, so that the element doesn't slide at an incredibly fast rate, we want a believable animation effect. That's the creative part. You will simply have to tweak the numbers if you want a different feel to your sliding animation.

During processInertia, we choose elements that are active (still sliding.) This is determined by the magnitude calculated in the previous step (distance between A and B.) Then we gradually decrease the magnitude over and over until it is reduced to 0.

We don't use the directional vector calculated between points A and B itself to move the element during the animation. Because we need to move the width and height relative to magnitude. Not relative to themselves (otherwise you'd get a choppy animation where the element is animated on plane X while the other value on plane Y has already reached its destination because it was shorter.) So this is why we multiply both X and Y by the magnitude, and then decrease only the magnitude itself and not the stored X/Y values.

Finally we restrict the animated element to the bounding box area. We simply reverse the momentum values when the element reaches a certain boundary (top,left,right,bottom) and the element appears to bounce off in the opposite direction just as expected

Conclusion

You can find the complete source code and the inertia demo on this page.

The gesture/inertia principle can be applied to so many things. This tutorial demonstrated the custom code that is at the core of a custom inertia mechanism.

I am thinking about writing a tutorial that will explain how to apply it to the real world User Interface controls like scrolling content accompanied by a smooth scrollbar effect. Sliders that respond with inertia to your touch or mouse pointer swing. And a few others.

But rewriting and re-implementing the code from this tutorial to work in your own projects shouldn't be that difficult. That's how custom sliders work at the very core.

Oh yes... and remember to use .data instead of .attr :-) Even though "it will still work."

Just the weekly tutorials.