I am trying to get this animation to work but for some reason nothing is happening when it is called by the Dispatch timer, any ideas?
public static void Grass2(Canvas canvas, int boundry)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "grass")
{
var skewGrass = new DoubleAnimation
{
From = 0,
To = boundry,
Duration = new Duration(TimeSpan.FromMilliseconds(100)),
RepeatBehavior = RepeatBehavior.Forever,
EasingFunction = new BackEase(),
AutoReverse = true
};
element.BeginAnimation(SkewTransform.AngleXProperty, skewGrass);
}
}
}
You're trying to animate the SkewTransform.AngleXProperty on an object of type Image. That won't work since Image does not have this property. However, the Image's RenderTransform might be set to a SkewTransform, and that SkewTransform can be animated:
...
// each element's RenderTransform must be a SkewTransform
var transform = (SkewTransform)element.RenderTransform;
transform.BeginAnimation(SkewTransform.AngleXProperty, skewGrass);
Related
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!
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
...
I got this UserControl which is a Grid with around 10 rows and 10 columns. Each cell contains 1 textblock or 1-6 images.
What I want to do is animate all elements on each row to slide in. However I can't find a way to animate the whole Grid.Row to do that.
I can't wrap all elements in a stackpanel/canvas either since that puts them into the same column...
Anyone has a solution to this?
Thanks
So I've come up with a working solution and it is working. Maybe not that elegant but it does what it is supposed to do and works very well on a Lumia 920 device.
void CreateLoadAnimationForRow(int row, int startTime)
{
foreach (var child in LayoutRoot.Children.Cast<FrameworkElement>().Where(x => Grid.GetRow(x) == row))
{
AddToStoryboard(child, startTime);
}
}
void AddToStoryboard(FrameworkElement element, int startTime)
{
element.RenderTransform = new CompositeTransform();
var doubleAnimation = new DoubleAnimation
{
From = -200,
To = 0,
Duration = new Duration(TimeSpan.FromSeconds(1)),
BeginTime = TimeSpan.FromMilliseconds(startTime)
};
var objectAnimationUsingKeyFrames = new ObjectAnimationUsingKeyFrames()
{
BeginTime = TimeSpan.FromMilliseconds(startTime + 10)
};
var discreteObjectKeyFrame = new DiscreteObjectKeyFrame()
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)),
Value = Visibility.Visible
};
objectAnimationUsingKeyFrames.KeyFrames.Add(discreteObjectKeyFrame);
IEasingFunction easingFunction = new SineEase();
easingFunction.Ease(2);
doubleAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(objectAnimationUsingKeyFrames, element);
Storyboard.SetTargetProperty(objectAnimationUsingKeyFrames, new PropertyPath("Visibility"));
Storyboard.SetTarget(doubleAnimation, element);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(FrameworkElement.RenderTransform).(CompositeTransform.TranslateX)"));
storyboardSlideIn.Children.Add(doubleAnimation);
storyboardSlideIn.Children.Add(objectAnimationUsingKeyFrames);
}
Why below code doesn't change ScaleY to 1?
var transform = new ScaleTransform { ScaleY = 0 };
var story = new Storyboard();
var animation = new DoubleAnimation {
Duration = new Duration(new TimeSpan(0)), To = 1 };
Storyboard.SetTarget(animation, transform);
Storyboard.SetTargetProperty(animation, new PropertyPath("ScaleY"));
story.Children.Add(animation);
story.Begin();
I use transform indirectly: it use for render some UIElements and kept in their DependencyProperty.
Does it perhaps work if you drop the Storyboard and just call BeginAnimation directly?
var transform = new ScaleTransform { ScaleY = 0 };
var animation = new DoubleAnimation { Duration = TimeSpan.Zero, To = 1 };
transform.BeginAnimation(ScaleTransform.ScalyYProperty, animation);
Note that this will only have any effect if the animation's FillBehavior has a value of HoldEnd. Otherwise the animated property will immediately revert back to its local value (which is 0 here). Fortunately HoldEnd is the default value for FillBehavior.
And of course the transform should be used somewhere.
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.