(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.