My goal is to draw and animate 2D UI elements on the screen. The problem is, I’m not exactly sure how I can animate it without taking up a lot of space doing so. This is what my method would look like to draw a red rectangle at 0, 0 with a width and height of 50
public void render() { Gui.drawRect(0, 0, 50, 50, Color.red); // Drawing an object would look something like this }
Now, what I would do is define a variable such as rectanglePos = 0
and add that to the axis that I want to change. drawRect(0 + rectanglePos, 0, 50, 50, Color.red);
Now when I want to animate the rectangle, I create a new Thread and animate it from there.
public void render() { Gui.drawRect(0 + rectanglePos, 0, 50, 50, Color.red); // Drawing an object would look something like this } public void animate() { Thread t = new Thread(() -> { float target = 50; for (int i = 0; i < 50; i++) { //ease(int start, int end, float time, EasingType type) rectanglePos = AnimationUtil.ease(0, target, 5, EasingType.CubicInOut); } }); t.start(); }
This example here clearly is just bad. My question is, what is the best way to go about structuring this?
Advertisement
Answer
Using System.currentTimeMillis()
, we can animate 2D UI elements on the screen with easing functions without Threads or other fancy stuff. Let’s assume that we want the animation to move on the x-axis, starting at 10 and ending at 50. Assuming we know when to start and the duration, we can calculate when the animation ends.
Let’s say time
is equal to the current time, and startTime
is equal to the time we start, and so on…
In a separate class, we need a few methods to help out. One method returns the progress of our animation. See Easing Functions for a list of standard easing functions. Another method converts this value into the start and end pixels.
The current progress would be equal to (time - startTime) / duration
. This already gives us the value to put into our ease method. The ease method returns a value between 0.0
and 1.0
, so all we have to do is “convert” this into the pixels. The formula would be: (end - start) * ease(alpha)
.
Here’s the implementation of that concept. It’s nothing perfect.
long startTime = 0L; public void animate() { this.startTime = System.currentTimeMillis(); } // 10, 10 -> 50, 50 public void render() { // 1000 milliseconds = 1 Second drawRect(10 + Ease.toPixels(10, 50,Ease.getProgress(this.startTime, 1000L)), 10, ... etc); }
Helping class:
// easeInOutCubic public static float ease(float alpha) { return (float) (alpha < 0.5 ? 4 * alpha * alpha * alpha : 1 - Math.pow(-2 * alpha + 2, 3) / 2); } public static float getProgress(long startTime, float duration) { long time = System.currentTimeMillis(); float progress = (time - startTime) / duration; if (progress <= 0.0F) return 0.0F; if (progress >= 1.0F) return 1.0F; return progress; } public static int toPixels(float start, float end, float alpha) { return (int) ((end - start) * ease(alpha)); }