Trace the WPF easing functions

homme forme fonction

Wpf Easing fonctions, otherwise known as the acceleration functions, are functions that slow down or accelerate the motion of an animation according to a mathematical formula. In this post we will see how to make a small WPF application that lists and draws the Easing functions available in the WPF framework.

Let’s start with the design of the main window which will contain:

  • a canvas to draw the functions
  • a drop-down list to choose the easing function to represent
  • a check box to activate / deactivate the animations, because one wishes to move plots on the curves of the graphs, in order to highlight the effects of acceleration and slowing down introduced by the formulas

it will look something like this:

Easing Function Demo Design Window
Easing function demo : window design

the XAML code of the main window (MainWindow.xaml) is :

<Window x:Class="FranckGaspoz.Wordpress.WPF.CSharp.EasingFunctionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:EasingFunctionDemo"
        mc:Ignorable="d"
        Title="EasingFunctionDemo" 
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen"
        Height="402" 
        Width="519"
        SizeToContent="WidthAndHeight"
        >
    <Grid>
        <Grid.Resources>
            <!-- diagram background brush -->
            <DrawingBrush 
                x:Key="BackBrush"
                Viewport="0,0,10,10" 
                ViewportUnits="Absolute"
                TileMode="Tile">
                <DrawingBrush.Drawing>
                    <DrawingGroup>
                        <GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z" Brush="#EEEEEE" />
                        <GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#EEEEEE" />
                    </DrawingGroup>
                </DrawingBrush.Drawing>
            </DrawingBrush>
        </Grid.Resources>
        <!-- main layout -->
        <Grid 
            Name="GR_Layout"
            Background="WhiteSmoke">
            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition Height="320"/>
                <RowDefinition Height="20"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="470"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <!-- control bar -->
            <StackPanel
                Grid.ColumnSpan="3"
                HorizontalAlignment="Left"
                Orientation="Horizontal"
                VerticalAlignment="Center"
                Margin="0"                
                >
                <Label Content="easing function: "/>
                <!-- functions list -->
                <ComboBox
                    Name="CB_EaseFunctions"
                    Width="Auto"
                    SelectionChanged="CB_EaseFunctions_SelectionChanged"
                    />
                <!-- enable / disable animations -->
                <CheckBox 
                    Margin="8,0,0,0"
                    VerticalAlignment="Center"
                    IsChecked="{Binding AnimationEnabled}"
                    Name="CKB_AnimationsOn"
                    Click="CKB_AnimationsOn_Click">
                    animation enabled
                </CheckBox>
            </StackPanel>
            <Border
                BorderThickness="1"
                BorderBrush="Gray"
                Grid.Row="1"
                Grid.Column="1">
                <!-- and finally the drawing area -->
                <Canvas                                     
                    Background="{StaticResource BackBrush}"
                    Name="Canvas"
                />
            </Border>
        </Grid>
    </Grid>
</Window>

The code behind is relatively simple and revolves around the 4 properties of the class:

// drawer of the graph
EasingFunctionDrawer drawer = new EasingFunctionDrawer();

// list of functions
BindingList<string> EasingFunctions = new BindingList<string>();

// easing function being displayed
EasingFunctionBase EasingFunction = null;

// indicates whether the animation is enabled or disabled
public bool AnimationEnabled
{
   get { return (bool)GetValue(AnimationEnabledProperty); }
   set { SetValue(AnimationEnabledProperty, value); }
}

// dependency property to bind the CheckBox
public static readonly DependencyProperty AnimationEnabledProperty = DependencyProperty.Register("AnimationEnabled",
typeof(bool), typeof(MainWindow), new PropertyMetadata(true));

 

The list of functions is built by introspecting the classes of the System.Windows.Media.Animation namespace in which the classes that interest us respect the naming convention {fonctionName}Easing

The InitGraph method is called when the graph needs to be redrawn (window constructor, selection in the list or click on checkbox)

All drawing tasks are assigned to the EasingFunctionDrawer class , whose method is to represent a easing function:

/// <summary>
/// draws an easing function on a canvas
/// </summary>
/// <param name="canvas">target canvas</param>
/// <param name="origin">coordinates of the graph origin</param>
/// <param name="Length">chart size (enclosing square)</param>
/// <param name="easeColor">line color of the function</param>
/// <param name="easingFunc">easing function</param>
/// <param name="scaley">scale on the Y axis (reduction factor)</param>
internal void Draw(
    Canvas canvas,
    Point origin,
    double Length,
    Brush easeColor,
    EasingFunctionBase easingFunc,
    double scaley
    )
{
    double stp = 0.025/10d,d,x,y;
    // the X axis is linearly traversed
    for (double a = 0;a<=1;a+=stp)
    {
        x = origin.X + a * Length;
        y = origin.Y;
        /**
         * the easing function is called to determine the value
         * for the X position, which is here provided as a parameter of
         * normalized time t (for f(t) where f is the easing function)
         */
        d = easingFunc.Ease(a);
        // the ordinate is determined according to the value of the easing function
        y = origin.Y - d * Length / scaley;
        // the point is drawn
        DrawPlot(canvas, x, y, easeColor);
    }
}

 

The animation of a plot that follows the curve is done via the Animate(…) :

/// <summary>
/// animates a point that runs through the representation of a easing function
/// </summary>
/// <param name="canvas">target canvas</param>
/// <param name="origin">chart origin</param>
/// <param name="Length">chart size (enclosing square)</param>
/// <param name="easeColor">line color of the function</param>
/// <param name="easingFunc">easing function</param>
/// <param name="scaley">scale on the Y axis (reduction factor)</param>
internal void Animate(
    Canvas canvas,
    Point origin,
    double Length,
    Brush easeColor,
    EasingFunctionBase easingFunc,
    double scaley
    )
{
    var duration = TimeSpan.FromSeconds(3);
    var r = DrawPlot(
        canvas,
        origin.X,
        origin.Y,
        easeColor,
        6d);
    var ta = new TranslateTransform(0,0);
    r.RenderTransform = ta;
    // linear progression on the X axis (time axis)
    var x_move =
        new DoubleAnimation()
        {
            From = 0,
            To = Length,
            Duration = duration,
            AutoReverse = true,
            RepeatBehavior = RepeatBehavior.Forever
        };
    // progression according to the function of ease on the Y axis
    var y_move =
        new DoubleAnimation()
        {
            From = 0,
            To = - Length/scaley,
            Duration = duration,
            EasingFunction = easingFunc,
            AutoReverse = true,
            RepeatBehavior = RepeatBehavior.Forever
        };<br />
    // start the animation
    ta.BeginAnimation(
        TranslateTransform.XProperty,
        x_move
        );<br />
    ta.BeginAnimation(
        TranslateTransform.YProperty,
        y_move
        );
}

 

Here is an example of the result:

Easing Function Demo
Easing Function Demo : Elastic Ease
download sample code
download the source code of the example (Visual Studio 2015 solution): WPF C# Easing Function demo code source (VS2015 project) MIT-Licensesource code released under free MIT license

Leave a Reply