Thickness animation trouble? (with Begin function parameters) - c#

There is Main frame on which there is a button and control and also stackpanel. Attempting to move worked animation from xaml to .cs I have wrote next cool function)):
private void ToggleButton_Checked(object sender, RoutedEventArgs e)
{
int heightBottom = 100;
System.Windows.Thickness th = stackPanel1.Margin;
th.Top = stackPanel1.Margin.Top + heightBottom;
ThicknessAnimation animStackPanel1 = new ThicknessAnimation
{
From = stackPanel1.Margin,
To = th,
AccelerationRatio = 0.2,
FillBehavior = FillBehavior.Stop,
DecelerationRatio = 0.8,
Duration = DURATION
};
System.Windows.Thickness th2 = fxPSEditorView.Margin;
th2.Right = fxPSEditorView.Margin.Right - heightBottom;
th2.Bottom = fxPSEditorView.Margin.Bottom - heightBottom;
ThicknessAnimation animPSEditorView = new ThicknessAnimation
{
From = fxPSEditorView.Margin,
To = th2,
AccelerationRatio = 0.2,
FillBehavior = FillBehavior.Stop,
DecelerationRatio = 0.8,
Duration = DURATION
};
Storyboard.SetTarget(animPSEditorView, fxPSEditorView);
Storyboard.SetTargetProperty(fxPSEditorView, new PropertyPath(MarginProperty));
sb.Children.Add(animPSEditorView);
Storyboard.SetTarget(animStackPanel1, stackPanel1);
Storyboard.SetTargetProperty(stackPanel1, new PropertyPath(MarginProperty));
sb.Children.Add(animStackPanel1);
sb.Begin();//null reference error here!
};
As I understand I must specify TWO parameters for sb.Begin - one for stackPanel1 and the other is for fxPSEditorView. But It does not takes a set of objects as first parameter.
Any ideas how to run this animation will be wellcome ! Thank you

You do not need two parameters, but you should pass the control which contains the controls which are being animated (which need to be in the same name-scope). Read the documentation of Begin(FrameworkElement).

Related

How can I change the animation speed?

This is the code for the animation:
private void Pass_GotFocus(object sender, RoutedEventArgs e)
{
DoubleAnimation doubleAnimation = new()
{
Duration = new(TimeSpan.FromMilliseconds(800)),
From = 0,
To = 200
};
ColorAnimation colorAnimation = new()
{
Duration = new(TimeSpan.FromMilliseconds(800)),
To = Color.FromRgb(135, 206, 250)
};
Storyboard sb = new() { Duration = new(TimeSpan.FromMilliseconds(800)) };
Storyboard.SetTarget(doubleAnimation, passwordbox_underline);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(Rectangle.WidthProperty));
Storyboard.SetTarget(colorAnimation, passwordbox_underline);
Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("(0).(1)", Rectangle.FillProperty, SolidColorBrush.ColorProperty));
sb.Children.Add(doubleAnimation);
sb.Children.Add(colorAnimation);
sb.Begin(this);
}
So I have an animated rectangle. It goes from 0 with to 200 width for 1 second with the same speed. My question is how can i make it to start faster at the beginning and gradually slow it down until stop?
You can use an easing function to achieve this kind of behavior.
Easing functions allow you to apply custom mathematical formulas to your animations. For example, you may want an object to realistically bounce or behave as though it were on a spring. You could use Key-Frame or even From/To/By animations to approximate these effects but it would take a significant amount of work and the animation would be less accurate than using a mathematical formula.
For example, the exponential function (ease out) will slow down the animation towards the end.
DoubleAnimation doubleAnimation = new DoubleAnimation
{
Duration = TimeSpan.FromMilliseconds(800),
From = 0,
To = 200,
EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut, Exponent = 5}
};

How to create a Turning animation?

I want to make a turning animation to show the vehicle turning in Junction. Also, I want to do it with c#(code behind) because my vehicles are dynamically added.
Solution tried:
I tried to use TranslateTransform and RotateTransform but I could only create sharp turn animation. I want to create a smooth turn animation.
Current Output:
Sample Code
//Code to add car
private void Click1_Click(object sender, RoutedEventArgs e)
{
var myCar = new Image()
{
Source = new BitmapImage(new Uri("ms-appx:///Assets/RedCar.png")),
Width = 140,
Height = 65,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
RenderTransform = new TranslateTransform()
{
X = 0,
Y = actualHeight / 2 - 145
}
};
VehicleGrid.Children.Add(myCar);
}
//Code to create forward animation
private void MoreForward(UIElement element)
{
Storyboard storyboard = new Storyboard();
DoubleAnimation doubleAnimation = new DoubleAnimation()
{
Duration = new Duration(new TimeSpan(0, 0, 3)),
To = LeftRoad.ActualWidth - 140
};
Storyboard.SetTarget(doubleAnimation, element.RenderTransform);
Storyboard.SetTargetProperty(doubleAnimation, "X");
storyboard.Children.Add(doubleAnimation);
storyboard.Begin();
}
Full Code
You can see my full code in Github: TrafficManagementSystem
There's an interesting post that details how to create a layout path where your object can move along with.
But in a simple scenario like yours, you basically just want a curved motion like what's described in this Android's Material Design Guideline. Yeah... Android's, as we don't have native curved motion API support just yet (the Windows UI team did mention that they are looking to support this in the future though).
However, it's not too difficult to create your own curved motion. In fact, many have used this trick on the web already - apply the opposite speed of an easing on each axis. Also in your case, you will want the curve of the easing to be sharp in order to produce a nice turning animation.
For example, for a car coming from left to right and then doing a left turn, you can apply a QuinticEase with EaseIn mode on x-axis and one with EaseOut on y-axis. To turn the vehicle, just apply another rotation animation to it but with a short delay and lesser duration to ensure the turning only happens at the crossroad.
By slightly modifying my answer in this question, you can achieve what I described above with the following code
The AnimateTransform helper method
public static void AnimateTransform(this UIElement target, string propertyToAnimate, Orientation? orientation, double? from, double to, int duration = 3000, int startTime = 0, EasingFunctionBase easing = null)
{
if (easing == null)
{
easing = new ExponentialEase();
}
var transform = target.RenderTransform as CompositeTransform;
if (transform == null)
{
transform = new CompositeTransform();
target.RenderTransform = transform;
}
target.RenderTransformOrigin = new Point(0.5, 0.5);
var db = new DoubleAnimation
{
To = to,
From = from,
EasingFunction = easing,
Duration = TimeSpan.FromMilliseconds(duration)
};
Storyboard.SetTarget(db, target);
var axis = string.Empty;
if (orientation.HasValue)
{
axis = orientation.Value == Orientation.Horizontal ? "X" : "Y";
}
Storyboard.SetTargetProperty(db, $"(UIElement.RenderTransform).(CompositeTransform.{propertyToAnimate}{axis})");
var sb = new Storyboard
{
BeginTime = TimeSpan.FromMilliseconds(startTime)
};
sb.Children.Add(db);
sb.Begin();
}
Create the turning animations
MyCar.AnimateTransform("Translate", Orientation.Horizontal, null, -600, duration: 3000, easing: new QuinticEase
{
EasingMode = EasingMode.EaseIn
});
MyCar.AnimateTransform("Translate", Orientation.Vertical, null, -600, duration: 3000, easing: new QuinticEase
{
EasingMode = EasingMode.EaseOut
});
MyCar.AnimateTransform("Rotation", null, null, -90, duration: 2000, startTime: 500);
Result in motion
Alternatively, you can replace the traditional Storyboard animation with the new Composition API, which provides fully customizable easing functions (see below), but the idea is the same.
public static CubicBezierEasingFunction EaseOutExpo(this Compositor compositor) =>
compositor.CreateCubicBezierEasingFunction(new Vector2(0.14f, 1f), new Vector2(0.34f, 1f));
Hope this helps!

TextBlock animation for Opacity not working as expected

I have a UWP app project and I am trying to add a couple of DoubleAnimations and I am using below code:
private static void CreateStoryboardAnimation(StackPanel sp, ItemHelper item, EnumHelper.AddRemoveFavorites favType)
{
var image = (Image)sp.FindName("ImageView");
var tb = (TextBlock)sp.FindName("FavStatusTB");
if (image != null)
{
image.RenderTransform = new CompositeTransform();
//tb.RenderTransform = new CompositeTransform();
Storyboard tbStory = new Storyboard();
var tbAnimateOpacity = new DoubleAnimation()
{
From = 1.0,
To = 0.0,
Duration = new Duration(TimeSpan.FromMilliseconds(500)),
};
Storyboard.SetTarget(tbAnimateOpacity, tb);
Storyboard.SetTargetProperty(tbAnimateOpacity, "Opacity");
tbStory.Children.Add(tbAnimateOpacity);
Storyboard storyboard1 = new Storyboard();
storyboard1.Completed += async delegate
{
// set text
if (favType == EnumHelper.AddRemoveFavorites.Add)
{
tb.Text = "Added to favorites";
}
else
{
tb.Text = "Removed from favorites";
}
await Task.Delay(500);
// run 2nd animation
var storyboard2 = new Storyboard();
var translateYAnimation2 = new DoubleAnimation()
{
From = -20,
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(700)),
};
Storyboard.SetTarget(translateYAnimation2, image);
Storyboard.SetTargetProperty(translateYAnimation2, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
storyboard2.Children.Add(translateYAnimation2);
storyboard2.Begin();
tbStory.Begin();
};
DoubleAnimation translateYAnimation = new DoubleAnimation()
{
From = 0,
To = -20,
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
Storyboard.SetTarget(translateYAnimation, image);
Storyboard.SetTargetProperty(translateYAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
storyboard1.Children.Add(translateYAnimation);
storyboard1.Begin();
}
}
The first time I run the animation it works fine but after that it doesn't. This code gets applied on GridView items as you can see below:
The image animation works fine, it goes up and then the TextBlock animation runs poorly. It is supposed to display the text for 500 milliseconds but it shows the text and then starts the animation to make the opacity go zero.
I want this text to be visible to the user for at least 500 milliseconds and then the animation should start. Is there something that I am missing? I also tried BeginTime property of DoubleAnimation but to no avail. Please share your suggestions. Thanks
I think the problem is with the fact that you are starting the opacity animation inside the Completed handler of the first "slide up" animation. It works the first time, because the Opacity of the TextBlock is 1.0, but after the first round it is 0.0 and flips to 1.0 only after the await Task.Delay(500); finishes. The easiest fix would be to set the opacity right after the if:
if (image != null)
{
tb.Opacity = 1; //add this line
...

Animate RenderTransform in WPF

I've got a question regarding animations for WPF in C# code!
I have an handler function for the MouseWheel event. It just checks if you 'zoomed in' or 'zoomed out'. Just have a look at the code, the important line here is the 4th line, where the RenderTransform is set.
private void ZoomPanCanvas_MouseWheel(object sender, MouseWheelEventArgs e) {
var factor = (e.Delta > 0) ? (1.1) : (1 / 1.1);
currrentScale = factor * currrentScale;
mNetworkUI.RenderTransform = new ScaleTransform(currrentScale, currrentScale);
var pos = e.GetPosition(mNetworkUI);
mNetworkUI.Width = ZoomPanCanvas.ActualWidth / currrentScale;
mNetworkUI.Height = ZoomPanCanvas.ActualHeight /currrentScale;
var dummyTransform = new ScaleTransform(factor, factor, pos.X, pos.Y);
var offSet = new Point(dummyTransform.Value.OffsetX, dummyTransform.Value.OffsetY);
mNetworkUI.ViewModel.Network.SetTransformOffset(offSet);
}
I kept the rest of the function in the code for completeness.
What I want to do, is to animate this change of the RenderTransform!
I already tried to use a Storyboard (with UIElement.RenderTransformProperty set). Best result was a not-animated change of the RenderTransform (but it was not the same result as this 4th line from the code achieves).
Maybe you can help me, I already tried a few suggested methods from the other questions here.
Edit:
Here's the non-working attempt and at first the chenged code from above:
private void ZoomPanCanvas_MouseWheel(object sender, MouseWheelEventArgs e) {
var factor = (e.Delta > 0) ? (1.1) : (1 / 1.1);
currrentScale = factor * currrentScale;
///mNetworkUI.RenderTransform = new ScaleTransform(currrentScale, currrentScale);
Helper.Animations.RenderTransformAnimation(mNetworkUI, new ScaleTransform(currrentScale, currrentScale));
var pos = e.GetPosition(mNetworkUI);
mNetworkUI.Width = ZoomPanCanvas.ActualWidth / currrentScale;
mNetworkUI.Height = ZoomPanCanvas.ActualHeight /currrentScale;
var dummyTransform = new ScaleTransform(factor, factor, pos.X, pos.Y);
var offSet = new Point(dummyTransform.Value.OffsetX, dummyTransform.Value.OffsetY);
mNetworkUI.ViewModel.Network.SetTransformOffset(offSet);
}
And it follows the static helper function:
public static void RenderTransformAnimation(FrameworkElement element, Transform newTransform) {
MatrixAnimationUsingKeyFrames anim = new MatrixAnimationUsingKeyFrames();
var key1 = new DiscreteMatrixKeyFrame(element.RenderTransform.Value, KeyTime.FromPercent(0));
var key2 = new DiscreteMatrixKeyFrame(newTransform.Value, KeyTime.FromPercent(1));
Storyboard.SetTarget(anim, element.RenderTransform);
Storyboard.SetTargetProperty(anim, new PropertyPath(UIElement.RenderTransformProperty));
Storyboard sb = new Storyboard();
sb.Children.Add(anim);
sb.Duration = AnimationDuration;
sb.Begin();
}
It always throws an exception on the sb.Begin() call, telling me, something was not ok with my 'PropertyPath'. I don't know how to do it :(.
I mean, there is no way to directly create a "TransformAnimation", right? Only MatrixAnimations are available...
I've provided a simple animation of a ScaleTransform below. In the interests of providing a 'minimal' example, I only adjust the scale; I don't do any of the offset calculations that you're doing based on mouse position. You should be able to figure out where to go from here:
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var factor = e.Delta > 0d ? 1.1d : 0.9d;
var t = mNetworkUI.RenderTransform as ScaleTransform;
if (t == null)
{
mNetworkUI.RenderTransform = t = new ScaleTransform(1d, 1d)
{
CenterX = 0.5d,
CenterY = 0.5d
};
}
var oldScale = (double)t.GetAnimationBaseValue(ScaleTransform.ScaleXProperty);
var newScale = oldScale * factor;
//
// Make sure `GetAnimationBaseValue()` reflects the `To` value next time
// (needed to calculate `oldScale`, and for the animation to infer `From`).
//
t.ScaleX = newScale;
t.ScaleY = newScale;
var animation = new DoubleAnimation
{
To = newScale,
Duration = TimeSpan.FromSeconds(0.5d),
DecelerationRatio = 0.5d,
FillBehavior = FillBehavior.Stop
};
//
// Use `HandoffBehavior.Compose` to transition more smoothly if an animation
// is already in progress.
//
t.BeginAnimation(
ScaleTransform.ScaleXProperty,
animation,
HandoffBehavior.Compose);
t.BeginAnimation(
ScaleTransform.ScaleYProperty,
animation,
HandoffBehavior.Compose);
}

Convert C# animation code to storyboard

I have the following method which works. I'd like to put it in a utility method that returns a Storyboard. Every attempt I have made at converting this to a Storyboard has failed, and I've spent a lot of time researching. I'm ready to give up unless someone comes to my rescue.
Here's the code I want to convert:
public override void Begin(FrameworkElement element, int duration)
{
var transform = new ScaleTransform();
element.LayoutTransform = transform;
var animation = new DoubleAnimation
{
From = 1,
To = 0,
Duration = TimeSpan.FromMilliseconds(duration),
FillBehavior = FillBehavior.Stop,
EasingFunction = new QuinticEase { EasingMode = EasingMode.EaseIn }
};
transform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
transform.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
}
So, instead of the two BeginAnimation() calls, I want to return a Storyboard so all I have to do is call storyboard.Begin(). I know this shouldn't be that hard to do, but I'm just not getting it.
Thanks.
EDIT: In response to H.B's suggestions, I tried the following code, which still does not work:
private static Storyboard CreateAnimationStoryboard(FrameworkElement element, int duration)
{
var sb = new Storyboard();
var scale = new ScaleTransform(1, 1);
element.RenderTransform = scale;
element.RegisterName("scale", scale);
var animation = new DoubleAnimation
{
From = 1,
To = 0,
Duration = TimeSpan.FromMilliseconds(duration),
FillBehavior = FillBehavior.Stop,
EasingFunction = new QuinticEase { EasingMode = EasingMode.EaseIn }
};
sb.Children.Add(animation);
Storyboard.SetTarget(animation, scale);
Storyboard.SetTargetProperty(animation, new PropertyPath(ScaleTransform.ScaleXProperty));
return sb;
}
I know I only animated the X axis - just want to get something to work first.
You'll need two animations and then set the attached Storyboard properties to animated the right property on the right object using SetTargetProperty and SetTargetName.
Due to how storyboards work you also need to set a namescope (NameScope.SetNameScope), register the name of the transform, and call StoryBoard.Begin with the containing element overload.
e.g.
NameScope.SetNameScope(element, new NameScope());
var transform = new ScaleTransform();
var transformName = "transform";
element.RegisterName(transformName, transform);
element.RenderTransform = transform;
var xAnimation = new DoubleAnimation(2, TimeSpan.FromSeconds(1));
var yAnimation = xAnimation.Clone();
var storyboard = new Storyboard()
{
Children = { xAnimation, yAnimation }
};
Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(ScaleTransform.ScaleX)"));
Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(ScaleTransform.ScaleY)"));
Storyboard.SetTargetName(xAnimation, transformName);
Storyboard.SetTargetName(yAnimation, transformName);
storyboard.Begin(element);
I suggest using Expression Blend and start recording from there, it should create your storyboards in XAML. Rather than hard coding it with C# and trying to translate it 1 by 1 to storyboard thus it can be a prone error.

Categories

Resources