Appcelerator Titanium: Creating Animations, Transformations, and Understanding Drag-and-drop

Exclusive offer: get 50% off this eBook here
Appcelerator Titanium Smartphone App Development Cookbook

Appcelerator Titanium Smartphone App Development Cookbook — Save 50%

Over 80 recipes for creating native mobile applications specifically for iPhone and Android smartphones – no Objective-C or Java required with this book and ebook

$26.99    $13.50
by Boydlee Pollentine | December 2011 | Cookbooks

Almost any control or element in Titanium can have an animation or transform applied to it. This allows you to enhance your applications by adding a level of interactivity and "bling" that your apps would otherwise perhaps not have.

In this article by Boydlee Pollentine, author of of Appcelerator Titanium Smartphone App Development Cookbook, we will cover:

  • Animating a View using the "animate" method
  • Animating a View using 2D matrix and 3D matrix transforms
  • Dragging an ImageView using touch events
  • Scaling an ImageView using a Slider control
  • Saving our "Funny Face" image using the toImage() method

(For more resources related to this subject, see here.)

Animating a View using the "animate" method

Any Window, View, or Component in Titanium can be animated using the animate method. This allows you to quickly and confidently create animated objects that can give your applications the "wow" factor. Additionally, you can use animations as a way of holding information or elements off screen until they are actually required. A good example of this would be if you had three different TableViews but only wanted one of those views visible at any one time. Using animations, you could slide those tables in and out of the screen space whenever it suited you, without the complication of creating additional Windows.

In the following recipe, we will create the basic structure of our application by laying out a number of different components and then get down to animating four different ImageViews. These will each contain a different image to use as our "Funny Face" character.

Complete source code for this recipe can be found in the /Chapter 7/Recipe 1 folder.

Getting ready

To prepare for this recipe, open up Titanium Studio and log in if you have not already done so. If you need to register a new account, you can do so for free directly from within the application. Once you are logged in, click on New Project, and the details window for creating a new project will appear. Enter in FunnyFaces as the name of the app, and fill in the rest of the details with your own information.

Pay attention to the app identifier, which is written normally in reverse domain notation (that is, com.packtpub.funnyfaces). This identifier cannot be easily changed after the project is created and you will need to match it exactly when creating provisioning profiles for distributing your apps later on.

The first thing to do is copy all of the required images into an images folder under your project's Resources folder. Then, open the app.js file in your IDE and replace its contents with the following code. This code will form the basis of our FunnyFaces application layout.

// this sets the background color of the master UIView Titanium.
UI.setBackgroundColor('#fff');

//
//create root window
//
var win1 = Titanium.UI.createWindow({
title:'Funny Faces',
backgroundColor:'#fff'
});

//this will determine whether we load the 4 funny face
//images or whether one is selected already
var imageSelected = false;

//the 4 image face objects, yet to be instantiated
var image1;
var image2;
var image3;
var image4;

var imageViewMe = Titanium.UI.createImageView({
image: 'images/me.png',
width: 320,
height: 480,
zIndex: 0
left: 0,
top: 0,
zIndex: 0,
visible: false
});
win1.add(imageViewMe);

var imageViewFace = Titanium.UI.createImageView({
image: 'images/choose.png',
width: 320,
height: 480,
zIndex: 1
});
imageViewFace.addEventListener('click', function(e){
if(imageSelected == false){
//transform our 4 image views onto screen so
//the user can choose one!
}
});
win1.add(imageViewFace);

//this footer will hold our save button and zoom slider objects
var footer = Titanium.UI.createView({
height: 40,
backgroundColor: '#000',
bottom: 0,
left: 0,
zIndex: 2
});
var btnSave = Titanium.UI.createButton({
title: 'Save Photo',
width: 100,
left: 10,
height: 34,
top: 3
});
footer.add(btnSave);

var zoomSlider = Titanium.UI.createSlider({
left: 125,
top: 8,
height: 30,
width: 180
});
footer.add(zoomSlider);

win1.add(footer);

//open root window
win1.open();

Build and run your application in the emulator for the first time, and you should end up with a screen that looks just similar to the following example:

How to do it…

Now, back in the app.js file, we are going to animate the four ImageViews which will each provide an option for our funny face image. Inside the declaration of the imageViewFace object's event handler, type in the following code:

imageViewFace.addEventListener('click', function(e){
if(imageSelected == false){
//transform our 4 image views onto screen so
//the user can choose one!
image1 = Titanium.UI.createImageView({
backgroundImage: 'images/clown.png',
left: -160,
top: -140,
width: 160,
height: 220,
zIndex: 2
});
image1.addEventListener('click', setChosenImage);
win1.add(image1);

image2 = Titanium.UI.createImageView({
backgroundImage: 'images/policewoman.png',
left: 321,
top: -140,
width: 160,
height: 220,
zIndex: 2
});
image2.addEventListener('click', setChosenImage);
win1.add(image2);

image3 = Titanium.UI.createImageView({
backgroundImage: 'images/vampire.png',
left: -160,
bottom: -220,
width: 160,
height: 220,
zIndex: 2
});
image3.addEventListener('click', setChosenImage);
win1.add(image3);

image4 = Titanium.UI.createImageView({
backgroundImage: 'images/monk.png',
left: 321,
bottom: -220,
width: 160,
height: 220,
zIndex: 2
});
image4.addEventListener('click', setChosenImage);
win1.add(image4);

image1.animate({
left: 0,
top: 0,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN
});

image2.animate({
left: 160,
top: 0,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_OUT
});

image3.animate({
left: 0,
bottom: 20,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT
});

image4.animate({
left: 160,
bottom: 20,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_LINEAR
});
}
});

Now launch the emulator from Titanium Studio and you should see the initial layout with our "Tap To Choose An Image" view visible. Tapping the choose ImageView should now animate our four funny face options onto the screen, as seen in the following screenshot:

How it works…

The first block of code creates the basic layout for our application, which consists of a couple of ImageViews, a footer view holding our "save" button, and the Slider control, which we'll use later on to increase the zoom scale of our own photograph. Our second block of code is where it gets interesting. Here, we're doing a simple check that the user hasn't already selected an image using the imageSelected Boolean, before getting into our animated ImageViews, named image1, image2, image3, and image4.

The concept behind the animation of these four ImageViews is pretty simple. All we're essentially doing is changing the properties of our control over a period of time, defined by us in milliseconds. Here, we are changing the top and left properties of all of our images over a period of half a second so that we get an effect of them sliding into place on our screen. You can further enhance these animations by adding more properties to animate, for example, if we wanted to change the opacity of image1 from 50 percent to 100 percent as it slides into place, we could change the code to look something similar to the following:

image1 = Titanium.UI.createImageView({
backgroundImage: 'images/clown.png',
left: -160,
top: -140,
width: 160,
height: 220,
zIndex: 2,
opacity: 0.5
});
image1.addEventListener('click', setChosenImage);
win1.add(image1);

image1.animate({
left: 0,
top: 0,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN,
opacity: 1.0
});

Finally, the curve property of animate() allows you to adjust the easing of your animated component. Here, we used all four animation-curve constants on each of our ImageViews. They are:

  • Titanium.UI.ANIMATION_CURVE_EASE_IN: Accelerate the animation slowly
  • Titanium.UI.ANIMATION_CURVE_EASE_OUT: Decelerate the animation slowly
  • Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT: Accelerate and decelerate the animation slowly
  • Titanium.UI.ANIMATION_CURVE_LINEAR: Make the animation speed constant throughout the animation cycles

Animating a View using 2D matrix and 3D matrix transforms

You may have noticed that each of our ImageViews in the previous recipe had a click event listener attached to them, calling an event handler named setChosenImage. This event handler is going to handle setting our chosen "funny face" image to the imageViewFace control. It will then animate all four "funny face" ImageView objects on our screen area using a number of different 2D and 3D matrix transforms.

Complete source code for this recipe can be found in the /Chapter 7/Recipe 2 folder.

How to do it…

Replace the existing setChosenImage function, which currently stands empty, with the following source code:

//this function sets the chosen image and removes the 4
//funny faces from the screen
function setChosenImage(e){
imageViewFace.image = e.source.backgroundImage;
imageViewMe.visible = true;

//create the first transform
var transform1 = Titanium.UI.create2DMatrix();
transform1 = transform1.rotate(-180);

var animation1 = Titanium.UI.createAnimation({
transform: transform1,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT
});
image1.animate(animation1);
animation1.addEventListener('complete',function(e){
//remove our image selection from win1
win1.remove(image1);
});

//create the second transform
var transform2 = Titanium.UI.create2DMatrix();
transform2 = transform2.scale(0);

var animation2 = Titanium.UI.createAnimation({
transform: transform2,
duration: 500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT
});
image2.animate(animation2);
animation2.addEventListener('complete',function(e){
//remove our image selection from win1
win1.remove(image2);
});

//create the third transform
var transform3 = Titanium.UI.create2DMatrix();
transform3 = transform3.rotate(180);
transform3 = transform3.scale(0);

var animation3 = Titanium.UI.createAnimation({
transform: transform3,
duration: 1000,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT
});
image3.animate(animation3);
animation3.addEventListener('complete',function(e){
//remove our image selection from win1
win1.remove(image3);
});

//create the fourth and final transform
var transform4 = Titanium.UI.create3DMatrix();
transform4 = transform4.rotate(200,0,1,1);
transform4 = transform4.scale(2);
transform4 = transform4.translate(20,50,170);
//the m34 property controls the perspective of the 3D view
transform4.m34 = 1.0/-3000; //m34 is the position at [3,4]
//in the matrix

var animation4 = Titanium.UI.createAnimation({
transform: transform4,
duration: 1500,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT
});
image4.animate(animation4);
animation4.addEventListener('complete',function(e){
//remove our image selection from win1
win1.remove(image4);
});

//change the status of the imageSelected variable
imageSelected = true;
}

How it works…

Again, we are creating animations for each of the four ImageViews, but this time in a slightly different way. Instead of using the built-in animate method, we are creating a separate animation object for each ImageView, before calling the ImageView's animate method and passing this animation object to it. This method of creating animations allows you to have finer control over them, including the use of transforms.

Transforms have a couple of shortcuts to help you perform some of the most common animation types quickly and easily. The image1 and image2 transforms, as shown in the previous code, use the rotate and scale methods respectively. Scale and rotate in this case are 2D matrix transforms, meaning they only transform the object in two-dimensional space along its X-axis and Y-axis. Each of these transformation types takes a single integer parameter; for scale, it is 0-100 percent and for rotate, the number of it is 0-360 degrees.

Another advantage of using transforms for your animations is that you can easily chain them together to perform a more complex animation style. In the previous code, you can see that both a scale and a rotate transform are transforming the image3 component. When you run the application in the emulator or on your device, you should notice that both of these transform animations are applied to the image3 control!

Finally, the image4 control also has a transform animation applied to it, but this time we are using a 3D matrix transform instead of the 2D matrix transforms used for the other three ImageViews. These work the same way as regular 2D matrix transforms, except that you can also animate your control in 3D space, along the Z-axis.

It's important to note that animations have two event listeners: start and complete. These event handlers allow you to perform actions based on the beginning or ending of your animation's life cycle. As an example, you could chain animations together by using the complete event to add a new animation or transform to an object after the previous animation has finished. In our previous example, we are using this complete event to remove our ImageView from the Window once its animation has finished.

Appcelerator Titanium Smartphone App Development Cookbook Over 80 recipes for creating native mobile applications specifically for iPhone and Android smartphones – no Objective-C or Java required with this book and ebook
Published: December 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

(For more resources related to this subject, see here.)

Dragging an ImageView using touch events

Now that we have allowed the user to select a funny face image from our four animated ImageView controls, we need to allow them to adjust the position of their own photo so it fits within the transparent hole that makes up the face portion of our funny face. We are going to do this using the touch events provided by the ImageView control.

Complete source code for this recipe can be found in the /Chapter 7/Recipe 3 folder.

How to do it…

The simplest way to perform this task is by capturing the X and Y touch points and moving the ImageView to that location. The code for this is simple. Just add the following code after your declaration of the imageViewFace control, but before you add this control to your window:

imageViewFace.addEventListener('touchmove', function(e){
imageViewMe.left = e.x;
imageViewMe.top = e.y;
});

Now, run your app in the emulator, and after selecting a funny face image, attempt to touch-and-drag your photograph around the screen. You should notice that it works but it doesn't seem quite right, does it? This is because we are moving the image based on the top corner position, instead of the center of the object. Let's change our code to instead work on the center point of the imageViewMe control, by replacing the previous code with the following source code:

imageViewFace.addEventListener('touchstart', function (e) {
imageViewMe.ox = e.x - imageViewMe.center.x;
imageViewMe.oy = e.y - imageViewMe.center.y;
});

imageViewFace.addEventListener('touchmove', function(e){
imageViewMe.center = {
x:(e.x - imageViewMe.ox),
y:(e.y - imageViewMe.oy)
};
});

Run your app in the emulator again and after selecting a funny face image, attempt to touchand- drag your photograph around the screen. This time you should notice a much smoother, more natural feeling drag-and-drop effect! Try positioning your "me" photograph into the center of one of your funny faces, and you should be able to replicate the following screenshot:

How it works…

Here we are using two separate touch events to transform the left and top positioning properties of our imageViewMe control. First, we need to find the center point. We do this in our touchstart event by the center.x and center.y properties of our ImageView control and then assign these to a couple of custom variables we have named "ox" and "oy". Doing this within the touchstart event ensures that these variables are immediately available to us when the touchmove event occurs. Then, within our touchmove event, instead of changing the top and left properties of imageViewMe, we pass its center property to our new x and y co-ordinates based on the touch events x and y properties, minus the center point we saved as our object's ox and oy variables. This ensures that the movement of the image is nice and smooth!

Scaling an ImageView using a Slider control

We have now created a code to select an animated funny face and we have the ability to move our image around using the drag-and-drop method. We need to be able to scale our "me" photograph using a Slider control and a new transformation.

In the following recipe, we will hook up the event listener of our Slider control and use another 2D matrix transformation, and this time change the scale of our imageViewMe control based on the user input.

Complete source code for this recipe can be found in the /Chapter 7/Recipe 4 folder.

How to do it…

Near the bottom of your current source code, you should have instantiated a Slider control named "zoomSlider". We are going to replace that code with a slightly updated version, and then capture the slider's change event in order to scale our imageViewMe component based on the value selected. Replace your declaration of the zoomSlider component with the following code:

var zoomSlider = Titanium.UI.createSlider({
left: 125,
top: 8,
height: 30,
width: 180,
minValue: 1,
maxValue: 100,
value: 50
});

//create the sliders event listener/handler
zoomSlider.addEventListener('change', function(e){
//create the scaling transform
var transform = Titanium.UI.create2DMatrix();
transform = transform.scale(zoomSlider.value);
var animation = Titanium.UI.createAnimation({
transform: transform,
duration: 100,
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT
});
imageViewMe.animate(animation);
});

//finally, add our slider to the footer view
footer.add(zoomSlider);

Try running your application in the emulator now, and after selecting a funny face image, you should be able to scale the "me" photograph using the Slider control. Try using it in conjunction with the touch-and-drag from the previous recipe to fit your face inside the funny picture hole, as seen in the following screenshot:

How it works…

We are performing a very similar action to what we did in the second recipe of this chapter. Within the change event handler of our Slider control, we are applying a 2D matrix transform to the imageViewMe control, using the scale method. Our slider has been given a minimum value of 0 and a maximum of 100. These values are the relative percentages that we are going to scale our image by. By using a very short duration (for example, 100 milliseconds) on our animation, we can make the movement of the slider almost instantaneously relate to the scale of the "me" photograph.

Saving our "Funny Face" image using the toImage() method

For the very last part of this application, we want to combine the two images together (our "me" photograph and the funny face image we have chosen) and save them to the filesystem as one complete image. To do this, we will hook up the event listener of our save button control and use another common method found on almost all views and control types; toImage. Once we've combined our two images together and saved it off to the local filesystem, we'll then create a quick e-mail dialog and attach our funny face to it, allowing the user to send the complete image to his/her friends.

Complete source code for this recipe can be found in the /Chapter 7/Recipe 5 folder.

How to do it…

Underneath the instantiation of your btnSave object, add the following event listener and handler code:

btnSave.addEventListener("click", function(e){
//hide the footer
footer.visible = false;

//do a slight delay before capturing the image
//so we are certain the footer is hidden!
setTimeout(function(e){
//get the merged blob -- note on android you
//might want to use toBlob() instead of toImage()
var mergedImage = win1.toImage();

writeFile = Titanium.Filesystem.getFile(
Titanium.Filesystem.applicationDataDirectory,
'funnyface.jpg');
writeFile.write(mergedImage);

//now email our merged image file!
var emailDialog = Titanium.UI.createEmailDialog();
emailDialog.setSubject("Check out funny face!");
emailDialog.addAttachment(writeFile);
emailDialog.addEventListener('complete',function(e) {
//reset variables so we can do another funny face
footer.visible = true;
imageViewFace.image = 'images/choose.png';
imageSelected = false;
});

emailDialog.open();

}, 250);
});

Now, launch your application in the emulator or on your device, again going through all of the steps until you have chosen a funny face, and adjust the layout of your photograph accordingly. When done, hit the save button and you should see an e-mail dialog appear with your combined image visible as an attachment.

How it works…

The toImage method simply takes a combined screenshot of the element in question. In our case, we are performing the command on win1, our root Window object. To do this, we are simply hiding our footer control and then setting a short timeout. When elapsed, it uses toImage to take a combined screenshot of both our imageViewMe and imageViewFace controls, which we then save to the filesystem.

There is another method that most controls have called toBlob, which works in a very similar manner to toImage. Depending on what you are trying to achieve, you can generally use either one of these methods. However, at times, you will find that the Titanium API will contain bugs and only one of them may work. In particular, the toBlob method works far better on Android devices than the toImage method does. However, as the Titanium platform grows more stable, you can expect better performance from both of these API calls. Additionally, you would use the toBlob method in order to store blob data in a local database using SQLite, though in general this approach is not normally used as it is very memory intensive. Saving blob objects to the filesystem is the recommended approach.

The following screenshot shows our final combined image, which has been saved to the filesystem and attached to a new e-mail dialog ready to be shared among the user's friends and family!

Summary

In this article, we saw how to create animations, and how to transform your objects using 2D and 3D matrices in Titanium. We also ran through dragging and dropping controls and capturing screenshots using the inbuilt "toImage" functionality.


Further resources on this subject:


Appcelerator Titanium Smartphone App Development Cookbook Over 80 recipes for creating native mobile applications specifically for iPhone and Android smartphones – no Objective-C or Java required with this book and ebook
Published: December 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Boydlee Pollentine

Boydlee Pollentine is a keen mobile developer who has created numerous apps for the iTunes and Android store and a number of indie games. He is passionate about mobile development and in particular the Appcelerator Titanium platform, and is both a Titanium-certified application developer and a member of the Titans evangelist group. He is also the organizer of Europe's first Titanium mobile development conference, tiConf EU (http://ticonf.eu).

Boydlee has been a software engineer and programmer for the last 10 years, primarily focused on web technologies and Microsoft's .NET platform, and during that time has worked for numerous small and large organizations, those of particular note including a number of Australian federal government departments, state departments, banks, and media organizations.

He currently lives in Norfolk and works as a freelance Titanium developer via his own digital agency, Tipsy & Tumbler Limited (http://www.tipsyandtumbler.co.uk), and runs a small blog dedicated to mobile development at http://boydlee.com.

Books From Packt


iPhone User Interface Cookbook
iPhone User Interface Cookbook

Cocos2d for iPhone 1 Game Development Cookbook
Cocos2d for iPhone 1 Game Development Cookbook

Xcode 4 iOS Development Beginner's Guide
Xcode 4 iOS Development Beginner's Guide

iPhone JavaScript Cookbook
iPhone JavaScript Cookbook

Android User Interface Development: Beginner's Guide
Android User Interface Development: Beginner's Guide

Android 3.0 Application Development Cookbook
Android 3.0 Application Development Cookbook

Android 3.0 Animations: Beginner’s Guide
Android 3.0 Animations: Beginner’s Guide

Flash Development for Android Cookbook
Flash Development for Android Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
p
b
K
X
S
Q
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software