I want to apply a Blink animation to a Canvas so that all the objects I have drawn on it would blink with it.
I have been somewhat successful using the code below which changes the Opacity property of the Canvas rather fast to achieve this effect but I'm kinda not satisfied with it.
I would prefer a pure blink without any FadeOut/FadeIn as in my current code. How can I do it the right way?
var blinkAnimation = new DoubleAnimation
{
From = 1,
To = 0
};
var blinkStoryboard = new Storyboard
{
Duration = TimeSpan.FromMilliseconds(500),
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = true
};
Storyboard.SetTarget(blinkAnimation, MyCanvas);
Storyboard.SetTargetProperty(blinkAnimation, new PropertyPath(OpacityProperty));
blinkStoryboard.Children.Add(blinkAnimation);
MyCanvas.BeginStoryboard(blinkStoryboard);
Maybe I can do that using the VisibilityProperty but I couldn't get it right.
You might use a second animation with an appropriate BeginTime:
var switchOffAnimation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.Zero
};
var switchOnAnimation = new DoubleAnimation
{
To = 1,
Duration = TimeSpan.Zero,
BeginTime = TimeSpan.FromSeconds(0.5)
};
var blinkStoryboard = new Storyboard
{
Duration = TimeSpan.FromSeconds(1),
RepeatBehavior = RepeatBehavior.Forever
};
Storyboard.SetTarget(switchOffAnimation, MyCanvas);
Storyboard.SetTargetProperty(switchOffAnimation, new PropertyPath(Canvas.OpacityProperty));
blinkStoryboard.Children.Add(switchOffAnimation);
Storyboard.SetTarget(switchOnAnimation, MyCanvas);
Storyboard.SetTargetProperty(switchOnAnimation, new PropertyPath(Canvas.OpacityProperty));
blinkStoryboard.Children.Add(switchOnAnimation);
MyCanvas.BeginStoryboard(blinkStoryboard);
If you want on/off state for your animation you can change your animation to DoubleAnimationUsingKeyFrames
var blinkAnimation = new DoubleAnimationUsingKeyFrames();
blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));
blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(250))));
var blinkStoryboard = new Storyboard
{
Duration = TimeSpan.FromMilliseconds(500),
RepeatBehavior = RepeatBehavior.Forever,
};
Storyboard.SetTarget(blinkAnimation, MyCanvas);
Storyboard.SetTargetProperty(blinkAnimation, new PropertyPath(OpacityProperty));
blinkStoryboard.Children.Add(blinkAnimation);
blinkStoryboard.Begin();
Related
I created an animation of Dependency Property Canvas.Left with code:
DoubleAnimation myDoubleAnimation = new DoubleAnimation { From =1, To = 100, Duration = new Duration(TimeSpan.FromMilliseconds(500)) };
Storyboard.SetTarget(myDoubleAnimation, Image);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath("(Canvas.Left)"));
Storyboard myMovementStoryboard = new Storyboard();
myMovementStoryboard.Children.Add(myDoubleAnimation);
myMovementStoryboard.Begin();
After the animation, I need to clear it with
UIElement.BeginAnimation(DependencyProperty, AnimationTimeline)
by a null AnimationTimeline.
Otherwise, the Canvas.Left will never change after I modify it by code.
However, the UIElement.BeginAnimation only accepts DependencyProperty but not a PropertyPath.
How can I solve this? Thank you.
You would call
Image.BeginAnimation(Canvas.LeftProperty, null);
The Storyboard is redundant. You could as well call
Image.BeginAnimation(Canvas.LeftProperty, myDoubleAnimation);
You may also avoid that the animation holds the value by setting
myDoubleAnimation = new DoubleAnimation
{
From = 1,
To = 100,
Duration = TimeSpan.FromMilliseconds(500),
FillBehavior = FillBehavior.Stop
};
I am making a sidebar-style app which is located on the left of right side of the screen.
I am trying to make it look like it's sliding in and out of the screen, but when it's on the right, it slides in from the left still.
I can't find any right to left options regarding window width, so I was hoping to find the answer here.
Here is my current working animation code for left to right:
public static void AnimateSize(this Window target, double newWidth, EventHandler completed)
{
var length = new TimeSpan(0, 0, 0, 0, Settings.Default.AnimationTime);
var sb = new Storyboard {Duration = new Duration(length)};
var aniWidth = new DoubleAnimationUsingKeyFrames();
aniWidth.Duration = new Duration(length);
aniWidth.KeyFrames.Add(new EasingDoubleKeyFrame(target.ActualWidth,
KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 00))));
aniWidth.KeyFrames.Add(new EasingDoubleKeyFrame(newWidth, KeyTime.FromTimeSpan(length)));
Storyboard.SetTarget(aniWidth, target);
Storyboard.SetTargetProperty(aniWidth, new PropertyPath(FrameworkElement.WidthProperty));
sb.Children.Add(aniWidth);
sb.Completed += completed;
sb.Begin();
}
called like this:
_window.AnimateSize(0, delegate { _window.Hide(); });
and this:
_window.Width = 0;
_window.Show();
_window.AnimateSize(Settings.Default.Width, delegate { });
Thanks.
private void Animate(double beginfrom, double to, DependencyProperty dp)
{
var da = new DoubleAnimation {
From = beginfrom,
To = to,
FillBehavior = FillBehavior.Stop,
Duration = new Duration(TimeSpan.FromSeconds(0.3)),
AccelerationRatio = 0.1
};
var storyBoard = new Storyboard();
storyBoard.Children.Add(da);
Storyboard.SetTarget(da, this); //this = your control object
Storyboard.SetTargetProperty(da, new PropertyPath(dp));
storyBoard.Begin();
}
Simple usage: Animate(0, 250, WidthProperty);
I had similar task to accomplish. I used to animate the left property of my window and make it appear as though it slides from the outside and disappears in the same fashion. but when a colleague who uses two screens used it , the window would slide onto the other screen.
The work around used was to simultaneously animate the width and left properties. this gives the effect of the window appearing at the desired location and unfurling towards the left.
DoubleAnimation daLeftAppear = new DoubleAnimation(popupWidthDetails.workAreaWidth, (<workAreaWidth> - <windowWidth> - 5), new Duration(TimeSpan.FromSeconds(1)));
daLeftAppear.EasingFunction = new QuarticEase();
Storyboard.SetTargetName(daLeftAppear, this.Name);
Storyboard.SetTargetProperty(daLeftAppear, new PropertyPath(LeftProperty));
DoubleAnimation daWidthAppear = new DoubleAnimation(0, 100, new Duration(TimeSpan.FromSeconds(1)));
daWidthAppear.EasingFunction = new QuarticEase();
Storyboard.SetTargetName(daWidthAppear, this.Name);
Storyboard.SetTargetProperty(daWidthAppear, new PropertyPath(WidthProperty));
sbAppear = new Storyboard();
sbAppear.Children.Add(daLeftAppear);
sbAppear.Children.Add(daWidthAppear);
Im attempting to create a storyboard in C# not XAML to control scaling of an image, so i can easily alter the ScaleTransform.ScaleX and ScaleTransform.ScaleY values in a DoubleAnimation.
So far I believe i have created the animations and added it to a new storyboard, and the appropriate values change in the C# when i check with breakpoints, but it's not actually working.
My C# looks like this:
public void SetStatistics(double[] value)
{
Storyboard sb = new Storyboard();
sb.Duration = new Duration(TimeSpan.FromSeconds(1));
//Wedge Animation X-Axis
DoubleAnimation wax = new DoubleAnimation();
//Wedge Animation Y-Axis
DoubleAnimation way = new DoubleAnimation();
ScaleTransform st = ((ScaleTransform)FindName("wedge1scale"));
wax = new DoubleAnimation();
way = new DoubleAnimation();
wax.Duration = sb.Duration;
way.Duration = sb.Duration;
sb.Children.Add(wax);
sb.Children.Add(way);
Storyboard.SetTargetProperty(wax, new PropertyPath("(ScaleTransform.ScaleX)"));
//End scale from calculation with an Enum value
wax.To = StatMin + (StatPercent * value[1]);
//Start scale from current value
wax.From = st.ScaleX;
Storyboard.SetTargetProperty(way, new PropertyPath("(ScaleTransform.ScaleY)"));
//End scale from calculation with an Enum value
way.To = StatMin + (StatPercent * value[1]);
//Start scale from current value
way.From = st.ScaleY;
Storyboard.SetTarget(wax, Wedge1);
Storyboard.SetTarget(way, Wedge1);
Main.Resources.Add("animation", sb);
sb.Begin();
}
My XAML Image is like this:
<Image x:Name="Wedge1" Source="Images/Wedge.png" RenderTransformOrigin="-0.008,1.027" Height="682" Width="263" Canvas.Left="869.04" Canvas.Top="-158.251" >
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="wedge1scale" ScaleX="0.555" ScaleY="0.555"/>
<TranslateTransform X="88.102" Y="-4.381"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
Thanks in advance for any info :)
The problem is your PropertyPath. You would have to write RenderTransform.Children[0].ScaleX and RenderTransform.Children[0].ScaleY in order to animate the ScaleX and ScaleY properties of the first child of the TransformGroup in the Image's RenderTransform.
var wax = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
var way = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
wax.To = ...
way.To = ...
Storyboard.SetTargetProperty(
wax, new PropertyPath("RenderTransform.Children[0].ScaleX"));
Storyboard.SetTargetProperty(
way, new PropertyPath("RenderTransform.Children[0].ScaleY"));
Storyboard.SetTarget(wax, Wedge1);
Storyboard.SetTarget(way, Wedge1);
var sb = new Storyboard();
sb.Children.Add(wax);
sb.Children.Add(way);
sb.Begin();
And it would be less code without the Storyboard:
var wax = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
var way = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
wax.To = ...
way.To = ...
wedge1scale.BeginAnimation(ScaleTransform.ScaleXProperty, wax);
wedge1scale.BeginAnimation(ScaleTransform.ScaleYProperty, way);
And I guess in your case it isn't necessary to set the From property, as the animations start from the current property values by default.
I am trying to create an animation but am having problems executing some code after the animation has occured. The animation code is as follows...
public static void Friend(Canvas canvas)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1)),
AutoReverse = true
};
element.BeginAnimation(Canvas.BottomProperty, slideDown);
slideDown.Completed += (sender, e) => Test(sender, e, element, canvas);
}
}
}
The event to happen afterwards is...
var random = new Random();
element.Source = Core.StreamImage(Wardrobe.Friends[Wardrobe.RandomFriend()]);
Canvas.SetLeft(element, random.Next(0, (int)(canvas.ActualWidth - element.Width)));
// then reverse the previous animation.
As you can see, the event needs to keep the context of the animation in order for it to work but event handlers don't allow this. I have tried adding this code directly underneath the element.BeginAnimation but it is executed prematurely.
I have also tried splitting the parts of the animation up into seperate functions but of course you can't have multiple animations on one object as the functions don't get executed in order resulting in only the last section being played.
Here is the code i used to fix the problem i was having...
public static void Friend(Canvas canvas)
{
var random = new Random();
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1))
};
slideDown.Completed += (sender, e) =>
{
element.Source = Core.StreamImage(Wardrobe.Friends[Wardrobe.RandomFriend()]);
Canvas.SetLeft(element, random.Next(0, (int)(canvas.ActualWidth - element.Width)));
var slideUp = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) + element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1))
};
element.BeginAnimation(Canvas.BottomProperty, slideUp);
};
element.BeginAnimation(Canvas.BottomProperty, slideDown);
}
}
}
Try moving the event above the execution
public static void Friend(Canvas canvas)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1)),
AutoReverse = true
};
slideDown.Completed += (sender, e) => Test(sender, e, element, canvas);
element.BeginAnimation(Canvas.BottomProperty, slideDown);
}
}
}
I'm not 100% but I think the Animation becomes Freezable after it executes, so we cant modify after
BTW, you can have multiple animations on a object but you need to use "StoryBoard".
Storyboard example:
var slideDown = new DoubleAnimation { From = 100, To = 200, Duration = new Duration(TimeSpan.FromSeconds(5)), AutoReverse = true };
var slideLeft = new DoubleAnimation { From = 100, To = 200, BeginTime = TimeSpan.FromSeconds(5), Duration = new Duration(TimeSpan.FromSeconds(5)), AutoReverse = true };
Storyboard storyBoard = new Storyboard();
storyBoard.Children.Add(slideDown);
storyBoard.Children.Add(slideLeft);
Storyboard.SetTargetName(slideDown, myCanvas.Name);
Storyboard.SetTargetName(slideLeft, myCanvas.Name);
Storyboard.SetTargetProperty(slideLeft, new PropertyPath(Canvas.LeftProperty));
Storyboard.SetTargetProperty(slideDown, new PropertyPath(Canvas.TopProperty));
myCanvas.BeginStoryboard(storyBoard);
Animations in WPF provide the boolean parameter AutoReverse. Is it possible to make a call to the functionality implemented by AutoReverse = "true"?
My goal is to spare some troublesome reverse animation and especially spare alot of code. The reverse must not happen directly after the animation ended.
Example of using AutoReverse and True to animate only the reverse animation, but this does not work as required - it still animates the actual animation and the reverse animation.
TranslateTransform transform = new TranslateTransform(0.0, 0.0);
myBox.RenderTransform = transform;
sb = new Storyboard();
Duration dur = new Duration(TimeSpan.FromSeconds(0.5));
DoubleAnimation shiftAnimation = new DoubleAnimation(100.0, dur);
shiftAnimation.AutoReverse = true;
sb.Children.Add(shiftAnimation);
Storyboard.SetTarget(shiftAnimation, myBox);
Storyboard.SetTargetProperty(shiftAnimation, new PropertyPath("RenderTransform.X"));
sb.Seek(TimeSpan.FromSeconds(0.5));
sb.Begin();
you could set the timeclock to 100% of the animationduration so when the animation starts, it will skip the non-reversed part!
Edit:
if Duration is set to 0:0:1.5 you apply a new timeline to your animation set to 0:0:1.5. Autoreverse has to be set to on and then you yust have to start the animation.
Edit 2:
TranslateTransform transform = new TranslateTransform(0.0, 0.0);
myBox.RenderTransform = transform;
sb = new Storyboard();
Duration dur = new Duration(TimeSpan.FromSeconds(0.5));
DoubleAnimation shiftAnimation = new DoubleAnimation(100.0, dur);
shiftAnimation.AutoReverse = true;
sb.Children.Add(shiftAnimation);
Storyboard.SetTarget(shiftAnimation, myBox);
Storyboard.SetTargetProperty(shiftAnimation, new PropertyPath("RenderTransform.X"));
sb.Begin();
sb.Pause();
sb.Seek(sb.Duration.TimeSpan);
sb.Resume();
I have found that the following code works nicely:
private bool reverse=false;
TimeSpan animationDuration = TimeSpan.FromMilliseconds(500);
Storyboard storyboard1 = new Storyboard();
private void prepareStoryBoard(Button btn)
{
btn.RenderTransform = new CompositeTransform();
DoubleAnimation animationShrink = new DoubleAnimation() { To = 0,
Duration =animationDuration , FillBehavior = FillBehavior.HoldEnd };
storyboard1.Children.Add(animationShrink);
Storyboard.SetTarget(animationShrink, btn);
Storyboard.SetTargetProperty(animationShrink,
"(Button.RenderTransform).(CompositeTransform.ScaleX)");
}
private void toggleAnimate()
{
if(reverse==false)
{
storyboard1.AutoReverse = false;
storyboard1.Begin();
reverse=true;
}
else
{
storyboard1.AutoReverse = true;
storyboard1.Seek(animationDuration);
storyboard1.Resume();
reverse = false;
}
}
The first time toggleAnimate() is called, the animation is executed in the normal direction, the second time toggleAnimate() is invoked the animation is reversed.