The New MFC Animation API
Introduction
Microsoft Visual Studio 2010 Service Pack 1 includes a number of enhancements and new features for MFC developers. One of those changes is an animation API to make it easy for you to create animations in MFC applications. This article will briefly introduce this animation API.
This articles was also posted on Codeguru.com.
The main parts of this new API are:
- Animation transitions
- Animation values
- Animation controllers
Animation Transitions
The animation API uses transitions to animate values. The API comes with a number of predefined transitions:
- CAccelerateDecelerateTransition: The animated value speeds up and then slows down again.
- CConstantTransition: The animated value will be kept at its initial value during the whole transition.
- CCubicTransition: The animated value will reach the target value with a specified velocity.
- CDiscreteTransition: The animated value will jump from the initial value directly to the target value after a specified delay.
- CInstantaneousTransition: The animated value immediately jumps to the target value. The duration is always zero.
- CLinearTransition: The animated value will go linearly from its initial value to its target value over a specified duration.
- CLinearTransitionFromSpeed: Is similar to CLinearTransition, but instead of specifying a duration, you have to specify a speed with which the animated value has to go from the initial value to the target value. The duration of the transition is calculated automatically based on the specified speed.
- CSmoothStopTransition: The animated value will go from its initial value to its target value, but the transition will slow down towards the target value to reach the target with a velocity of zero.
- CParabolicTransitionFromAcceleration: The animated value will reach its target value with a specified velocity and acceleration.
- CReversalTransition: The transition direction will be changed smoothly over a specified duration. The final value of the animated value will be the same as the initial value.
- CSinusoidalTransitionFromRange: The animated value will fluctuate between a specified minimum and maximum during the duration of the transition.
- CSinusoidalTransitionFromVelocity: The animated value will oscillate around the initial value during the duration of the transition.
Most of the above predefined animation transitions are pretty straightforward to use. All of them require at least the duration of the transition and most of them require a target value. For example, the CAccelerateDecelerateTransition transition has the following constructor:
CAccelerateDecelerateTransition(UI_ANIMATION_SECONDS duration, DOUBLE finalValue, DOUBLE accelerationRatio = 0.3, DOUBLE decelerationRatio = 0.3)
The first parameter is the duration of the transition. It is the time that it will take to go from the start value to the target value. The second parameter is this target value. The next parameters are specific algorithmic parameters which have default values in this case.
CConstantTransition is special. It will keep the animation value at its initial value during the whole transition, so its constructor is simply as follows:
CConstantTransition(UI_ANIMATION_SECONDS duration)
There is also a CCustomTransition that you can use if none of the above transitions deliver what you are looking for. It’s a bit more complicated to use. You will have to derive your own class from CCustomInterpolator and implement the InterpolateValue method and possibly a few others.
The next section will discuss animation values and will give examples on how to actually use the above animation transitions. Note that you need to initialize COM before the Windows Animation API will work. You can do this by adding the following line in your InitInstance method:
CoInitialize(0);
And the following line in your ExitInstance method:
CoUninitialize();
Instead of using CoInitialize and CoUnitialize, you can simply use the following block in your InitInstance method:
if (!AfxOleInit()) { AfxMessageBox(_T("AfxOleInit failed")); return FALSE; }
Animation Values
The new API works by animating values. There are several classes that encapsulate specific values and which can be animated with certain transitions. The different animation value classes are as follows:
- CAnimationValue: Animates a single value. This could be used to animate a transparency of an object or a rotation of an object.
- CAnimationPoint: Animates a point. A point contains and X and Y coordinates. Both coordinates can be animated separately.
- CAnimationSize: Animates a size. A size contains a width and height, both can be animated separately.
- CAnimationColor: Animates a color. A color contains an RGB value. Each color component can be animated separately.
- CAnimationRect: Animates a rectangle. A rectangle contains a left, top, right and bottom value and all of them can be animated separately.
Creating an animation value is pretty straightforward. Just create an instance of the animation value object you want, initialize it, add animation transitions to it and specify its parameters like the target value. For example, to define a color animation value, first define it as follows:
CAnimationColor m_animClr1;
Then the following line will initialize the start color to red:
m_animClr1 = RGB(255,0,0);
And finally assign transitions to each color component:
m_animClr1.AddTransition(new CLinearTransition(2, 0), new CLinearTransition(1, 255), new CLinearTransition(0.5, 128));
The above line adds 3 transitions:
- The red color component will go from 255 (=start value) to 0 over a period of 2 seconds.
- The green color component will go from 0 (=start value) to 255 over a period of 1 seconds.
- The blue color component will go from 0 (=start value) to 128 over a period of 0.5 seconds.
Similarly, a rectangle animation could be defined as follows:
CAnimationRect m_animRect; m_animRect = CRect(0, 0, 100 ,100); m_animRect.AddTransition(new CAccelerateDecelerateTransition(2, 100), new CAccelerateDecelerateTransition(2, 100), new CAccelerateDecelerateTransition(2, 400), new CAccelerateDecelerateTransition(2, 200));
This animation starts with a rectangle with upper-left coordinate (0, 0) and lower-right coordinate (100, 100). The transitions will animate this rectangle to an upper-left coordinate (100, 100) and a lower-right coordinate (400, 200) over a period of 2 seconds using an acceleration-deceleration transition.
Inside your WM_PAINT handler, you will use the animated values from your animation variables to do your drawing. For example:
int x1, y1, x2, y2; m_animRect.GetLeft().GetValue(x1); m_animRect.GetTop().GetValue(y1); m_animRect.GetRight().GetValue(x2); m_animRect.GetBottom().GetValue(y2); COLORREF clr; m_animClr1.GetValue(clr); pDC->FillSolidRect(x1, y1, x2-x1, y2-y1, clr);
The above code is first querying the animated rectangle about its current animated coordinates and the animated color variable about its current color value. After that it simply draws a rectangle with those coordinates and color.
Working with the other animation value classes is pretty similar.
Animation Controller
Now that you have learned about animation transitions and animations values, it’s time to learn how it all fits together. The CAnimationController class is the central part of the animation API. It will keep track of all animation values and animation transitions. You simply create one as a member variable in your class as follows:
CAnimationController m_animCtrl;
The next step is to link it with your target window in which you want to draw the animation:
m_animCtrl.SetRelatedWnd(this);
You are not required to link an animation controller with a window. Linking it will tell the animation API to send WM_PAINT messages to the specified window during the animation which makes it easier for you to redraw a frame of your animation. If you do not link the animation controller with a window, you will have to manually invalidate your window at appropriate intervals to redraw it. In this example we want the animation controller to send WM_PAINT message periodically, so you also need to tell it to use an animation timer:
m_animCtrl.EnableAnimationTimerEventHandler();
Now it’s time to link your animation values with the animation controller so it can take control of them:
CAnimationGroup* pGroup = m_animCtrl.AddAnimationObject(&m_animRect); m_animCtrl.AddAnimationObject(&m_animClr1); pGroup->m_bAutodestroyAnimationObjects = FALSE;
The first line will add the m_animRect animation value to the controller. Because this is the first animation value added to the controller, the controller will create a new animation group and will return a pointer to this group. The second line will add the m_animClr1 color animation value. Because you are not specifying any animation group, the controller will add m_animClr1 to the same animation group as m_animRect . The last line tells the animation controller to not automatically delete the linked animation values. This is done in this case because our animation values are member variables of our class. Instead of defining animation values as member variables, you could create an animation value using operator new, link it with the animation controller in a specific group and tell the animation controller to automatically delete objects when that animation group is destroyed. This makes it easier to manage dynamically created animation variables.
After all this, the animation controller is linked with certain animation values and each animation value is linked with certain animation transitions. Now it’s time to start the animation and this is simply done with the following line:
m_animCtrl.AnimateGroup(0);
This function required the ID of the group that you want to animate. If you didn’t specify your own group ID earlier while adding your animation variables to the controller, the default group ID 0 will be used. You can add an animation variable to a specific group by setting the ID on the animation variable as follows:
m_animClr1.SetID(0, 1); m_animCtrl.AddAnimationObject(&m_animClr1);
The first line will set the group ID of m_animClr1 to 1 and the second line will add the animation variable to the controller which will then create a new group with ID 1. If you want to start this group, you need the following line:
m_animCtrl.AnimateGroup(1);
That’s it, when you run the application you will see that a rectangle is changing color, position and size with a smooth animation transition. The initial look of the window is as follows:
After the smooth animation, the window will look as follows:
Download the following demo project to see it all working together:
MFCAnimationAPIDemo.zip
Conclusion
This article serves as a brief introduction to the animation API. The API includes some more advanced functionality like event handlers. For example, you could configure an event handler that will be called each time the status of the animation controller changes or each time a value of an animation value changes.
Errol said,
Wrote on February 3, 2011 @ 5:05 am
I simply wanted to thank you for preparing this article. I appreciate your effort very much.
James said,
Wrote on August 12, 2014 @ 2:33 am
Thank you for this good article and example.
Can I add a image object like png or bitmap to the animation controller?
Marc Gregoire said,
Wrote on September 28, 2014 @ 5:11 pm
The animation controller animates just some values like colors, points, sizes, and rectangles.
You do with it whatever you want.
For example, in my example I handle WM_PAINT, query the animated values and draw a solid filled rectangle.
If you want to draw a bitmap instead of a solid color, just change the line:
pDC->FillSolidRect(x1, y1, x2-x1, y2-y1, clr);
to render a bitmap in the animated rectangle instead.
Anton HEYMANN said,
Wrote on April 1, 2021 @ 12:00 pm
Hallo
How do you repeat an animation group?
Mariusz Kowalski said,
Wrote on January 9, 2022 @ 12:21 pm
Kids like me have it easy with this kind of resources and a lot more on MSDN!
Thank you for writing this. I made my first animation in MFC yesterday. 🙂
MFC Support for Windows Animation | codexpert blog said,
Wrote on May 20, 2023 @ 7:58 pm
[…] Marc Gregoire’s Blog: The New MFC Animation API […]