I have a mini-game as part of a larger game I'm writing for Windows phone which uses a storyboard animation to move an arrow back and forth along a bar, which the user trying to stop it in the center.
The issue I'm having is when I stop the animation, you can visibily see the arrow being animated move back a frame.
I've tried many things:
animating with keyframes and without
animating by canvas.left or translate.X
after pausing/stopping the animation, manually setting the canvas.left to the getcanvas of the arrow
With the final option not working, I wonder if the animation is actually drawing a frame ahead before it actually sets the X/Canvas.Left, and the bounce back position is the true one
Here is my animation code:
double speed = .75;
miniGameStoryboard.Stop();
miniGameStoryboard.Children.Clear();
// setup
var _Translate = new TranslateTransform();
this.MiniGame1Arrow.RenderTransform = _Translate;
// translate (location X)
DoubleAnimationUsingKeyFrames _TranslateAnimateX = new DoubleAnimationUsingKeyFrames();
System.Windows.Media.Animation.Storyboard.SetTarget(_TranslateAnimateX, _Translate);
System.Windows.Media.Animation.Storyboard.SetTargetProperty(_TranslateAnimateX, new PropertyPath(TranslateTransform.XProperty));
_TranslateAnimateX.KeyFrames.Add(new LinearDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)),
Value = 0
});
_TranslateAnimateX.KeyFrames.Add(new LinearDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(speed)),
Value = 660
});
_TranslateAnimateX.AutoReverse = true;
_TranslateAnimateX.RepeatBehavior = RepeatBehavior.Forever;
_TranslateAnimateX.FillBehavior = FillBehavior.HoldEnd;
miniGameStoryboard.Children.Add(_TranslateAnimateX);
miniGameStoryboard.Begin();
Related
Say I start a four-second DoubleAnimation on the Canvas.Left property of a control that animates the value from its current value to 100 and that animation was started by calling BeginAnimation on that control in code-behind.
void Animate(Control someControlOnCanvas, int newX){
var canvasLeftAnimation = new DoubleAnimation();
canvasLeftAnimation.To = newX;
canvasLeftAnimation.FillBehavior = FillBehavior.Stop;
canvasLeftAnimation.Duration = new Duration(TimeSpan.FromSeconds(4));
canvasLeftAnimation.Completed += (s, e) => {
Canvas.SetLeft(targetButton, x);
};
someControlOnCanvas.BeginAnimation(Canvas.LeftProperty, canvasLeftAnimation);
}
Animate(somethingToMove, 100);
Say half-way through that animation--at the two-second mark--I need to cancel the current animation and start a new one with a new To position (the From position would be wherever it is right now) that's 100 past where the first animation would end (i.e. not where it currently is at 50, but where it's supposed to be, which is 100, so adding another 100 would yield a new target of 200.)
If I try getting the current Left value, it's the animated value of 50. If I try getting the base value, it's where the animation started, which is zero since it's not officially updated until the completion handler fires.
So outside of manually adding a variable to track where things should be, how can I start a new animation that says 'Go to the original end, plus 100?'
My thoughts are:
Get the existing/current animation
Get it's To value
Start a new animation with the previous animation's To value, plus 100
What I'm stuck on is getting #1.
...or am I going about this all wrong?
I am trying to implement a translation animation on a Grid in Windows Phone 8.
The behavior that I want to implement is that when the user drags from left to right a new panel comes from the left(in animation) and reverse happens when the user drags from right to left.
For this I have implemented the following code which is called on Manipulation_Completed :
public Storyboard AnimateContent(int direction)
{
DoubleAnimation animation = new DoubleAnimation();
if (direction == 1)
{ //content_trans is the object of composite transform of grid
animation.From =content_trans.TranslateX;
animation.To = 370;
}
else if(direction==0)
{
animation.From = content_trans.TranslateX;
animation.To = 0;
}
animation.Duration = new TimeSpan(0,0,0,0,500);
Storyboard.SetTarget(animation, Content);
Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateX)"));
Storyboard sb = new Storyboard();
sb.Children.Add(animation);
return sb;
}
Now problem I am facing is that after calling this function(and calling begin() on storyboard object), when I am writing
content_trans.TranslateX=250;//or any other value of my choice
it is not being reflected on the screen.
I want to change these values because I writing this line of code in Manipulation_Delta , so that the user can have a feeling of dragging something, but it is not being reflected after animation.
This is because animated values take precedence over local values. See Dependency Property Value Precedence
If you want to set the value yourself, you will need to stop the animation first.
EDIT: If you want to be able to drag after the animation, then set the END location manually just before animating, and tell the animation to stop applying itself when it finishes with:
animation.FillBehaviour = FillBehaviour.Stop;
This means that when the animation finishes, your local values will apply.
The purpose of the code below is that a thumb follows a horizontal mouse movement. The code is called upon a mouse event, so the target value of the animation gets updated continuously.
In the code, offset is the current mouse horizontal position. The problem is, that the animation of the thumb doesn't fully animate to the specified offset, but always seems to be stopping at a value smaller or higher (depending if the mouse is dragged left or right).
The SeekAlignedToLastTick() influences the behavior of the animation, although I couldn't figure out what this function does by reading the documentation.
How can I animate the thumb, so that it follows smoothly the drag event?
private Storyboard _thumbStoryboard;
private DoubleAnimation _thumbAnimation = new DoubleAnimation();;
private CompositeTransform _thumbTransform = new CompositeTransform();
private void UpdateUserInterface(double offset)
{
var thumbItem = Thumb as FrameworkElement;
if (_thumbStoryboard == null)
{
Storyboard.SetTarget(_thumbAnimation, _thumbTransform);
_thumbStoryboard = new Storyboard();
_thumbStoryboard.Children.Add(_thumbAnimation);
thumbItem.RenderTransform = _thumbTransform;
_thumbStoryboard.Duration = new Duration(TimeSpan.FromMilliseconds(100));
_thumbAnimation.EasingFunction = new ExponentialEase();
}
double from = _thumbTransform.TranslateX;
_thumbStoryboard.Stop();
Storyboard.SetTargetProperty(_thumbAnimation, new PropertyPath("TranslateX"));
_thumbAnimation.From = from;
_thumbAnimation.To = offset;
_thumbStoryboard.Begin();
_thumbStoryboard.SeekAlignedToLastTick(TimeSpan.Zero);
}
I've tried to solve your issue, So I've created a Silverlight application and added a Border element for testing.
<Border x:Name="Thumb" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50" height="25" Background="#ff0000" />
There was no need to set the "From" Property, since the DoubleAnimation object could automatically continue from the current Value to the "To" Property.
And you were setting the Duration to the Storyboard, which causes the DoubleAnimation to Cutoff its animation without reaching the "To" Value, You need to set the Duration Property to the DoubleAnimation itself instead.
Also there was no need to call _thumbStoryboard.Stop(), because it will reset the current animation to the first TranslateX Value.
Here is the updated "UpdateUserInterface" function code with comments:
private void UpdateUserInterface(double offset) {
var thumbItem = Thumb as FrameworkElement;
if ( _thumbStoryboard == null ) {
// UpdateLayout Method is update the ActualWidth Properity of the UI Elements
this.UpdateLayout();
// Applying the CompositeTransform on "thumbItem" UI Element
thumbItem.RenderTransform = _thumbTransform;
// Setting the Render Transform Origin to be the Center of X and Y
thumbItem.RenderTransformOrigin = new Point(0.5d, 0.5d);
// Setting the target of the DoubleAnimation to be the Thumb CompositeTransform
Storyboard.SetTarget(_thumbAnimation, _thumbTransform);
// Setting the Targeted Properity of the DoubleAnimation to be The "TranslateX" Properity
Storyboard.SetTargetProperty(_thumbAnimation, new PropertyPath("TranslateX"));
// Used QuinticEase instead of ExponentialEase
// and Added EaseOut to make the animation be more smoother.
_thumbAnimation.EasingFunction = new QuinticEase(){ EasingMode = EasingMode.EaseOut };
// Initializing the Storyboard
_thumbStoryboard = new Storyboard();
// Specifing the Duration of the DoubleAnimation not the StoryBoard
_thumbAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
// Adding the DoubleAnimation to the Children of the Storyboard
_thumbStoryboard.Children.Add(_thumbAnimation);
}
// Calculate the New Centered Position
double newPos = offset - (thumbItem.ActualWidth / 2);
// Set the New DoubleAnimation "To" Value,
// There is no need to set the "From" Value since it'll automatically continue from the current TranslateX Value
_thumbAnimation.To = newPos;
// Begin the animation.
_thumbStoryboard.Begin();
}
Hope that helps you :)
Regards,
Monir Abu Hilal
I have an button which I move, using a storyboard and a translateTransform :
_animatedTranslateTransform = new TranslateTransform();
this.RegisterName("slide", _animatedTranslateTransform);
DoubleAnimation slideDown = new DoubleAnimation();
slideDown.By = 20;
slideDown.Duration = TimeSpan.FromSeconds(0.5);
Storyboard.SetTargetName(slideDown, "slide");
Storyboard.SetTargetProperty(slideDown, new PropertyPath(TranslateTransform.YProperty));
After some iterations, my button have moved from 60 px, for example. How can I replace it to its initial position ?
I tried this (I set -60 to simplify the explanation) :
TranslateTransform t = (TranslateTransform) _button.RenderTransform;
t.Y = -60;
But it doesn't work if the animation already occurred once.
UPDATE:
To keep future readers from being confused by the back-and-forth below, the correct answer is to do:
_button.RenderTransform = new TranslateTransform();
Original answer:
I would store the original position to be used whenever you want to reset. I don't think there's a concept of the "original state" for a control.
I am trying to produce a path animation in WPF. It's composed of 3 points that form a triangle. And along that path is a dot that is being animated.
So far that is all working, however, it runs smoothly as the duration progresses (as you would expect!
What I would like to happen is the animation progresses incrementally, in discrete steps.
I thought I could use some kind of discrete key frame animation, but have had no luck.
So far I've got the path and the Storyboard which is (unfortunately!) working a bit too smoothly.
Is there a way to only update the animation every x seconds?
// Construct the animation path.
var pFigure = new PathFigure
{
StartPoint = pathToWalk[0],
Segments = new PathSegmentCollection
{
new LineSegment(pathToWalk[1], false),
new LineSegment(pathToWalk[2], false),
new LineSegment(pathToWalk[0], false)
}
};
var animationPath = new PathGeometry();
animationPath.Figures.Add(pFigure);
// Freeze the PathGeometry for performance
animationPath.Freeze();
// Create a PointAnimationgUsingPath to move the Dot(EllipseGeometry)
// along the animation path.
var dotAnimation = new PointAnimationUsingPath
{
PathGeometry = animationPath,
Duration = TimeSpan.FromSeconds(5),
RepeatBehavior = RepeatBehavior.Forever
};
// Set the animation to target the Center property of the
// Dot(EllipseGeometry) named "theDotToAnimate".
Storyboard.SetTargetName(dotAnimation, "theDotToAnimate");
Storyboard.SetTargetProperty(dotAnimation, new PropertyPath(EllipseGeometry.CenterProperty));
// Create a Storyboard to contain and apply the animation.
var pathAnimationStoryboard = new Storyboard
{
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = true
};
pathAnimationStoryboard.Children.Add(dotAnimation);
// Start the Storyboard when ellipsePath is loaded.
dotPath.Loaded += (sender, e) => pathAnimationStoryboard.Begin(theWindowToAddto);
I thought about getting the Animation Clock from the animation and pausing and un-pausing it in a look. That might work, but it would be the ugliest thing ever!
Can anyone help?
EDIT: I also noticed IsAdditive="True" as an option, maybe there's a way of just nudging it along a bit each time?