A TEXT POST

Tactile UX - rapid prototyping of touch interactions

Today I will follow up on my session at UXcamp Europe 2011 in Berlin, where I showed you how to quickly prototype touch interactions. As of now, there is no tool available for UX designers to design touch interactions beyond a simple tap, so we have a choice of either sticking with pen & paper or going along a slightly more technical path to test the ideas.

To follow this short tutorial, you need some really basic knowledge of HTML, CSS and JavaScript.

It’s easiest to learn by example, so please download demo sources from GitHub: https://github.com/NitroLab/Tactile-UX and open index.html using your favourite text editor or IDE (I find Aptana Studio 3 quite suitable for this task). To view the demo on your iPad, you can put the files on any web server or access http://thenitrolab.com/tactile-ux directly. For user testing, I recommend enabling full-screen by adding the app to your home screen (instructions here).

As you probably remember, we discussed prototyping a clone of the classic game Pong. You can follow the comments in the source code, but I’ll explain more interesting elements here, step by step. Please have your editor open and line numbering enabled. Ready? Let’s go!

First, let’s have a look at HTML structure:

1. Lines 25-38 define the elements, which we will manipulate. For simplicity we use 3 DIVs, each of them individually styled in CSS file (css/style.css). CSS classes describe shapes, colour, positions and we also use them to add touch handling to the elements (movable, scalable, rotatable). At this point, we have a static webpage with two bars (paddles) at the edges, and a ball in the middle of the screen.

<div id="main">
	<!-- Top bar -->
	<div id="bar1" class="bar movable only-x">
	</div>
	<!-- Ball -->
	<div id="ball" class="ball rotatable">
	</div>
	<!-- Bottom bar -->
	<div id="bar2" class="bar movable only-x">
	</div>
	<!-- Debug window. Uncomment if you'd like to use it.
	<div id="debug">
	</div> -->
</div><!-- /#main -->

Now, let’s add some JavaScript magic to enable touch interactions. For its simplicity, I have chosen XUI JS library, but you can also use e.g. jqTouch.

2. Lines 41-43 prevent the web page from being scrolled with touching. This is necessary to handle touch positions correctly.

document.addEventListener('touchmove', function(e) {
	e.preventDefault();
});

3. In line 46 we initialize the variable needed to calculate touch offsets (when dragging).

var touchdiff = new Object();

4. Lines 49-62 contain the first interesting bit:

Line 49 is written using xui’s syntax (docs here), and it registers a gesturechange event handler for elements having the scalable class. e is the event being caught by xui.

x$('.scalable').gesturechange( function(e) {

e will contain different properties, depending on the type of event being caught. For gesture events, you will have 3 different properties:

  • scale
  • rotate
  • position

We will use the first two for the sake of this tutorial.

Lines 53-55 contain the syntax needed to scale the element. We use scale3d CSS transform, as the normal version is not hardware accelerated and therefore it doesn’t work smoothly.

var scaleTransform = {
	"-webkit-transform": "scale3d(" + scale + ", " + scale +", 0)"
};

Line 58 adds the CSS transform to the element being touched and that’s enough to handle scaling.

x$(this).css(scaleTransform);

5. See lines 65-78 for rotation gesture handling, it’s almost identical to handling scaling.

x$('.rotatable').gesturechange( function(e) {
	var rotation = e.rotation;

	//construct the CSS transform based on event. Use '3d' transforms for best performance
	var rotationTransform = {
		"-webkit-transform": "rotate3d(0,0,1, " + rotation +"deg)"
	};

	//apply CSS property to touched element
	x$(this).css(rotationTransform);

	//debug logging
	x$('#debug').html("Rotation: " + rotation + "<br>");
});

6. Lines 80-112 contain code responsible for handling dragging.

//handle drag event - record initial touch position
x$('.movable').touchstart( function(e) {
	var touch = e.changedTouches[0];
	touchdiff.startX = touch.pageX;
	touchdiff.startY = touch.pageY;
});

//handle drag event - calculate offset and move the element
x$('.movable').touchmove( function(e) {
	var touch = e.changedTouches[0];
	touchdiff.endX = touch.pageX;
	touchdiff.endY = touch.pageY;

	//construct the CSS transform based on event. Use '3d' transforms for best performance
	var transform = {
		"-webkit-transform": "translate3d(" + (touchdiff.endX - touchdiff.startX) + "px, " + (touchdiff.endY - touchdiff.startY) + "px, 0)"
	};

	//apply CSS property to touched element
	x$(this).css(transform);
});

//handle drag event - overrides previous handler to lock movement only to x-axis
x$('.only-x').touchmove( function(e) {
	var touch = e.changedTouches[0];
	touchdiff.endX = touch.pageX;

	//construct the CSS transform based on event. Use '3d' transforms for best performance
	var transform = {
		"-webkit-transform": "translate3d(" + (touchdiff.endX - touchdiff.startX) + "px, 0px, 0)"
	};
	x$(this).css(transform);
});

Basically, the logic is very similar to above handlers, except for the fact that we’re catching different events (touchstart and touchmove), and we use simple substraction to calculate the distance needed to move the element. Dragging is also based on CSS transform: translate3d. The last event handler for “.only-x” elements is used to lock the movement to X axis only. Please note that the touch position is stored in:

  • e.changedTouches[0].pageX (for X axis)
  • e.changedTouches[0].pageY (for Y axis)

7. You can have some fun with changing classes to introduce different functionality. In the example, the ball only has the rotatable class - you can change it to movable or scalable, save the file and reload the page in iPad’s Safari - it just works! You can also remove the only-x class from bars to make them draggable along the whole screen.

Feel free to experiment with the code, extend it to include more functions (like limiting draggable movement within some boundaries). To do it, just fork the project on GitHub and please let me know how you’re doing. I’d be happy if someone incorporated the ideas presented here in UX design software - there’s still a market gap.

I’d like to thank all who have attended my session at UXcamp Berlin. It’s been a great pleasure meeting you, learning your insights, and I’m glad that you find the solution interesting enough to apply in your projects. Good luck with JavaScripting and let me know how you’re doing!

blog comments powered by Disqus
  1. nitrolab posted this