Animation Easing, Interpolation

Hi people!

I really would like to tell everything I found about developing operating systems, but I don’t even understand what’s going on there. I’ll leave that there for a while and write about animations and interpolation functions.

Animations make UI(User Interface) feel much more alive in applications, if done right of course. If it is not, it just makes the application look funny and sometimes frustrating for user. Well, I am not an experienced designer, but at least I know how to implement animations.

Think about videos, movies. They’re just a lot of images in a sequence that if played in right timing, it looks like the objects on these static images look like they’re moving. In animations we generate all the images on runtime according to time. The keyword here is timing. Say in a duration of 1 second, a button should move from its original position to the left by 100 pixels. Let’s calculate where the button should be at t=0.4:

posX = originalPosX + 0.4 * 100;

If we were to draw the button at t=0.4 we should draw it on posX which is 40 pixels more than the original position of the box. Now we know what we have to draw on a specific time. But we don’t know when we have to draw, right?

We should ask in what frequency we will draw the frames(We call those images frames. Don’t be confused.). For a really smooth animation, drawing 60 frames per second is more than enough. But you should consider saving some CPU power by reducing frames drawn in seconds. We implement an infinite loop that works every (1000 / FPS) miliseconds. The function should draw the frame and display it, then it should calculate how much time passed while generating the previous frame. We should also generate a new thread to call these draw and wait methods. Clogging the UI Thread with some animation work is not a good practice. 🙂

Let’s check out an example:
I have been working with Java for about 2 years. I’ll use Java in my examples as I am too much comfortable with it.

I’ll suppose we have a Button class that was extended from UIElement class that has a method moveTo(x, y) that moves the element to x, y coordinates(Obviously).

//30 should be good for UI animations.
//Don't hesitate to change it to your liking though.
public static final int FPS = 30;

boolean applicationRunning = true;

long deltaTime = 0;

//Open a seperate thread to control draw operations.
Thread animationRunner = new Thread(new Runnable() {
	public void run() {
		//Always have a handle to control this infinite loop.
		//while(true) is not a good practice.
		while(applicationRunning) {
			//record starting time.
			long ti = System.currentTimeMillis();
			//draw the frame
			draw(deltaTime);
			//wait for a while (yeah, drawing a frame can take very little time.)
			Thread.sleep(1000 / FPS);
			//record final time.
			long tf = System.currentTimeMillis();
			//store deltaTime for the next frame
			deltaTime = tf - ti;
		}
	}
});

public void draw(long dt) {
	//Update the previously set animation.
	anim.update(dt);

	//draw stuff
	drawUI();
}

Here’s the animation class.

public class Animation {
	//our animation only supports X translation :V
	public int fromX;
	public int toX;

	public UIElement target;
	public long timer;

	public long duration;

	public void update(long dt) {
		//Animation factor. Decides where the element should be.
		float factor = (float)timer / duration;

		//The change in element's position.
		int change = toX - fromX;

		//Update the element.
		target.moveTo((int)(fromX + change * factor), target.getY());
		timer += dt;
	}
}

I omitted a lot of things in this code. But let’s check out all the things going on here:
In Animation class there’s a method that updates the UIElement named target whenever called. In each draw() call, every UIElement that is bound to an Animation will be updated accordingly to their animations.

Draw calls are coming from the seperate thread we created. We never started the seperate thread in this code. Calling animationRunner.start() in some arbitrary thread should work. The thread is responsible for calling draw, waiting and calculating deltaTime by subtracting the initial time(ti) from final time(tf). The value of deltaTime is guaranteed to be above 1000/FPS because we wait that much miliseconds in our code. Putting an upper bound to the deltaTime is actually a good practice, but its not really necessary as long as you don’t use a lot of objects on screen. 😀

The factor value in animation can be manipulated to create easing effects. Check this out:
Function graph generator link
This shows how our animation goes over time. f(x) is position and x is time factor. By changing the function to x^2, (See the red line.) it is possible to start the animation slow and get faster by time.

Check this out for some live demo.
Just by changing the factor line in Animation class, we can achieve this interpolation tricks in the simple way. But simple way is not enough for us. What if we want different interpolations in different animations?
Using an interface named Interpolator is one of the solutions.

public interface Interpolator {
	public float calculate(float factor);
}

Using interface to create different interpolators:

public class EaseInInterpolator implements Interpolator{
	@Override
	public float calculate(float factor) {
		return Math.pow(factor, 2);
	}
}

By changing the animation class a little bit, we can use interpolators in our animations.

public class Animation {
	private Interpolator interpolator = new LinearInterpolator();
	...

	public void setInterpolator(Interpolator i) {
		this.interpolator = i;
	}

	public void update(long dt) {
		float factor = interpolator.calculate((float) timer / duration);

		...
	}
}

Here’s the usage:

anim.setInterpolator(new EaseInInterpolator());

Beware! This doesn’t include a null check for the interpolator. Having it initialized is a must. I initialized it with LinearInterpolator which just returns factor value itself. Either initialize your Interpolator, or have a null check inside code, so that your users won’t get a NullPointerException and complain about how program crashes with one click of a button.

There’re many more interpolation functions to discover. You can even create your own interpolation function. I’ll not go into the details of that. Hope this gives you an idea about how animations are implemented.

Should you have any questions, please do not hesitate to ask in the comments.
I’ll see you again whenever I finish building my first operating system. 😀

More Reading:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.