Implicit Animation of Blur or Other Brush Effect - c#

I'm trying to create implicit hide and show animations that contain a blur. I have the following code to do a Gaussian Blur animation, but I don't know how to add it to a CompositionAnimationGroup and use it as an implicit animation. I only know how to start an animation using the SpriteVisual.
public static void CompositionAnimationBlur(UIElement element, int durationMilliseconds)
{
var visual = ElementCompositionPreview.GetElementVisual(element);
var compositor = visual.Compositor;
var effect = new GaussianBlurEffect()
{
Name = "Blur",
Source = new CompositionEffectSourceParameter("EffectSource"),
BlurAmount = 0f,
BorderMode = EffectBorderMode.Hard,
};
var blurEffectFactory = compositor.CreateEffectFactory(effect, new[] { effect.Name + "." + nameof(effect.BlurAmount) });
var brush = blurEffectFactory.CreateBrush();
var destinationBrush = compositor.CreateBackdropBrush();
brush.SetSourceParameter("EffectSource", destinationBrush);
var sprite = compositor.CreateSpriteVisual();
sprite.Size = new Vector2((float)(element.RenderSize.Width), (float)(element.RenderSize.Height));
sprite.Brush = brush;
var anim = compositor.CreateScalarKeyFrameAnimation();
anim.InsertKeyFrame(0.0f, 0f);
anim.InsertKeyFrame(1.0f, 50f);
anim.Duration = TimeSpan.FromMilliseconds(durationMilliseconds);
ElementCompositionPreview.SetElementChildVisual(element, sprite);
sprite.Brush.Properties.StartAnimation("Blur.BlurAmount", anim);
}

For you scenario, although you could add blur animation to CompositionAnimationGroup like the following, you can not start AnimationGroup in the element visual correctly. Because the blur animation apply to the sprite brush and the fade animation apply to element visual.
public static void CompositionAnimationBlur(UIElement element, int durationMilliseconds)
{
var visual = ElementCompositionPreview.GetElementVisual(element);
var compositor = visual.Compositor;
var AnimationGroup = compositor.CreateAnimationGroup();
var effect = new GaussianBlurEffect()
{
Name = "Blur",
Source = new CompositionEffectSourceParameter("EffectSource"),
BlurAmount = 0f,
BorderMode = EffectBorderMode.Hard,
};
var blurEffectFactory = compositor.CreateEffectFactory(effect, new[] { effect.Name + "." + nameof(effect.BlurAmount) });
var brush = blurEffectFactory.CreateBrush();
var destinationBrush = compositor.CreateBackdropBrush();
brush.SetSourceParameter("EffectSource", destinationBrush);
var sprite = compositor.CreateSpriteVisual();
sprite.Size = new Vector2((float)(element.RenderSize.Width), (float)(element.RenderSize.Height));
sprite.Brush = brush;
var anim = compositor.CreateScalarKeyFrameAnimation();
anim.InsertKeyFrame(0.0f, 0f);
anim.InsertKeyFrame(1.0f, 50f);
anim.Target = "Blur.BlurAmount";
anim.Duration = TimeSpan.FromMilliseconds(durationMilliseconds);
ElementCompositionPreview.SetElementChildVisual(element, sprite);
AnimationGroup.Add(anim);
sprite.Brush.Properties.StartAnimationGroup(AnimationGroup);
}
I suggest that you could use UWP Community Toolkit to integrate fade and blur animations.
await MyRec.Blur(value: 10, duration: 1000, delay: 0).Fade(value: 0.0f, duration: 1000, delay: 0).StartAsync();

Related

Issue adding animation to programmatically created UI element

My aim is to create a textBlock containing a name, after doing something with the method GetThings() I want an animation to play on the textBox basically moving it out of the view. as it is being animated the next textBlock is created with the next name.
But when I try and run the program I get the error:
No installed components were detected. Cannot resolve TargetProperty
(UIElement.RenderTransform).(CompositeTransform.TranslateY) on
specified object.
There is probably a better way of doing this, but I am limited in terms of my ability and would appreciate any help on the current issue.
Below is the code for the project:
await Task.Run(async () =>
{
TextBlock tb = null;
foreach (KeyValuePair<string, string> s in things)
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
TextBlock _txtBlock = new TextBlock()
{
Text = s.Key,
Margin = new Thickness(131, 0, 0, 0),
FontSize = 26,
Height = 40,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
Width = 168,
};
innerOverlay.Children.Add(_txtBlock);
tb = _txtBlock;
});
string tested = GetThings(s.Value);
if (tested == string.Empty)
{
MessageDialog failedMsg = new MessageDialog("failed", "problem");
failedMsg.Commands.Add(new UICommand("Exit"));
IUICommand command = await failedMsg.ShowAsync();
if (command.Label.Equals("Exit", StringComparison.CurrentCultureIgnoreCase))
{
Windows.UI.Xaml.Application.Current.Exit();
}
return;
}
else
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
var animation = new DoubleAnimation
{
EnableDependentAnimation = true,
To = 40,
Duration = new Duration(TimeSpan.FromMilliseconds(500)),
};
Storyboard.SetTarget(animation, tb);
Storyboard.SetTargetProperty(animation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
var sb = new Storyboard();
sb.Children.Add(animation);
sb.Begin();
});
thingGroup.Add(s.Key, tested);
}
}
});
It seems that you are having questions about how to animate the transformation of the TextBlock. #Clemens is right, you will need to create a CompositeTransform object and assign it to the RenderTransform property of the TextBlock first.
I've made a simple demo here. You could refer to it and adjust it to your real scenario.
Code:
TextBlock _txtBlock = new TextBlock()
{
Text = "Frist One",
//Margin = new Thickness(131, 0, 0, 0),
FontSize = 26,
Height = 40,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
Width = 168,
};
RootGrid.Children.Add(_txtBlock);
//create a CompositeTransform for the TextBlock
CompositeTransform TextBlockTransform = new CompositeTransform();
_txtBlock.RenderTransform = TextBlockTransform;
//Create animation to animate the CompositeTransform
Storyboard storyboard = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
animation.From = 0;
animation.To = 40;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
Storyboard.SetTarget(animation, _txtBlock);
Storyboard.SetTargetProperty(animation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
storyboard.Children.Add(animation);
storyboard.Begin();
You could get more information about Transforms and Animation here: Transforms overview, DoubleAnimation Class and CompositeTransform Class

Live Tile like Animation

I'm trying to recreate the slide-up part animation of the notification-queue on Windows 10 live tiles with an image I have ontop of another image. Below I have a "slide-up" storyboard working...but it's not the same.
Is the live tile ani actually growing in height as it slides up over the first?
I can't "see/figure" what it's doing.
public static async Task SlideUp(FrameworkElement element, double duration, int to = 0)
{
var tempTransform = new TranslateTransform();
element.RenderTransform = tempTransform;
var animation = new DoubleAnimation
{
From = element.ActualHeight * 2,
To = to,
Duration = TimeSpan.FromSeconds(duration),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
Storyboard.SetTargetProperty(animation, "Y");
Storyboard.SetTarget(animation, tempTransform);
var sb = new Storyboard();
sb.Duration = animation.Duration;
sb.Children.Add(animation);
await sb.BeginAsync();
}
The flip part of the animations would be nice too.
You could take a look at the DoubleAnimationUsingKeyFrames class:
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.animation.doubleanimationusingkeyframes.aspx
Update: You can also animate the Height of the image
public static async Task SlideUp(FrameworkElement element, double duration, int to = 0)
{
var trTransform = new TranslateTransform();
element.RenderTransform = trTransform;
double from = element.ActualHeight;
duration *= 1.5;
var animation = new DoubleAnimationUsingKeyFrames();
animation.KeyFrames.Add(new DiscreteDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0,0,0,0)),
Value = from / 2
});
var ks = new KeySpline { ControlPoint1 = new Point(0.0, 0.0), ControlPoint2 = new Point(0.9, 0.1) };
animation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(duration*1000/2)),
KeySpline = ks,
Value = (from - to) / 3 + to
});
var ks2 = new KeySpline { ControlPoint1 = new Point(0.1, 0.9), ControlPoint2 = new Point(0.2, 1.0) };
animation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(duration)),
KeySpline = ks2,
Value = to
});
Storyboard.SetTargetProperty(animation, "Y");
Storyboard.SetTarget(animation, trTransform);
var sb = new Storyboard();
sb.Duration = animation.Duration;
sb.Children.Add(animation);
DoubleAnimationUsingKeyFrames resizeHeightAnimation = new DoubleAnimationUsingKeyFrames()
{
EnableDependentAnimation = true
};
resizeHeightAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0)),
Value = 0
});
var heightSpline1 = new KeySpline { ControlPoint1 = new Point(0.0, 0.0), ControlPoint2 = new Point(0.9, 0.1) };
resizeHeightAnimation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(duration * 1000 / 2)),
KeySpline = heightSpline1,
Value = from / 3
});
var heightSpline2 = new KeySpline { ControlPoint1 = new Point(0.1, 0.9), ControlPoint2 = new Point(0.2, 1.0) };
resizeHeightAnimation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(duration)),
KeySpline = heightSpline2,
Value = from
});
Storyboard.SetTarget(resizeHeightAnimation, element);
Storyboard.SetTargetProperty(resizeHeightAnimation, "RenderTransform.Height");
sb.Children.Add(resizeHeightAnimation);
sb.Begin();
}
If that looks to laggy for you, you can also try setting the VerticalAlignment of the element to Bottom and remove the position animation part of the method.

Making a user control pulse

What I'm trying to do is make my control "pulse" (as in enlarge a bit, then shrink a bit, then back to its normal size). The following code does exactly what I want, but it just seems like there should be a simpler way. It has to be done in code behind because this is an abstract class that most of our controls derive from. I don't have much experience with animation controls and story boards.
So specifically my question is, is there a simpler way to get the desired results than the following method (while still being done in the code-behind)?
public void Pulse()
{
var storyboard = new Storyboard
{
FillBehavior = FillBehavior.Stop,
RepeatBehavior = new RepeatBehavior(2)
};
double timeIncrement = .15;
double changePercent = 20;
var firstTime = TimeSpan.FromSeconds(timeIncrement);
var secondTime = TimeSpan.FromSeconds(timeIncrement * 3);
var thirdTime = TimeSpan.FromSeconds(timeIncrement * 4);
var scale = new ScaleTransform(1.0, 1.0);
RenderTransformOrigin = new Point(.5, .5);
RenderTransform = scale;
//enlarge
{
DoubleAnimation growX = new DoubleAnimation
{
Duration = firstTime,
To = 1 + changePercent
};
storyboard.Children.Add(growX);
Storyboard.SetTargetProperty(growX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation growY = new DoubleAnimation
{
Duration = firstTime,
To = 1 + changePercent
};
storyboard.Children.Add(growY);
Storyboard.SetTargetProperty(growY, new PropertyPath("RenderTransform.ScaleY"));
}
//shrink
{
DoubleAnimation shrinkX = new DoubleAnimation
{
Duration = secondTime,
To = 1 - changePercent
};
storyboard.Children.Add(shrinkX);
Storyboard.SetTargetProperty(shrinkX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation shrinkY = new DoubleAnimation
{
Duration = secondTime,
To = 1 - changePercent
};
storyboard.Children.Add(shrinkY);
Storyboard.SetTargetProperty(shrinkY, new PropertyPath("RenderTransform.ScaleY"));
}
//back to normal
{
DoubleAnimation normX = new DoubleAnimation
{
Duration = thirdTime,
To = 1
};
storyboard.Children.Add(normX);
Storyboard.SetTargetProperty(normX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation normY = new DoubleAnimation
{
Duration = thirdTime,
To = 1
};
storyboard.Children.Add(normY);
Storyboard.SetTargetProperty(normY, new PropertyPath("RenderTransform.ScaleY"));
}
BeginStoryboard(storyboard, HandoffBehavior.SnapshotAndReplace, false);
}
You could perhaps simplify things a little by moving your grow/shrink logic into its own method. This reduces duplication and provides a potentially more reusable code kibble.
public void Pulse()
{
var storyboard = new Storyboard
{
FillBehavior = FillBehavior.Stop,
RepeatBehavior = new RepeatBehavior(2)
};
double timeIncrement = .15;
double growPercent = 20;
double shrinkPercent = -20;
var firstTime = TimeSpan.FromSeconds(timeIncrement);
var secondTime = TimeSpan.FromSeconds(timeIncrement * 3);
var thirdTime = TimeSpan.FromSeconds(timeIncrement * 4);
var scale = new ScaleTransform(1.0, 1.0);
RenderTransformOrigin = new Point(.5, .5);
RenderTransform = scale;
storyboard = AddSizeChange(firstTime, growPercent, storyboard);
storyboard = AddSizeChange(secondTime, shrinkPercent, storyboard);
storyboard = AddSizeChange(thirdTime, growPercent, storyboard);
BeginStoryboard(storyboard, HandoffBehavior.SnapshotAndReplace, false);
}
public Storyboard AddSizeChange(TimeSpan animTime, double changePercent, Storyboard storyboard)
{
DoubleAnimation growX = new DoubleAnimation
{
Duration = animTime,
To = 1 + changePercent
};
storyboard.Children.Add(growX);
Storyboard.SetTargetProperty(growX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation growY = new DoubleAnimation
{
Duration = animTime,
To = 1 + changePercent
};
storyboard.Children.Add(growY);
Storyboard.SetTargetProperty(growY, new PropertyPath("RenderTransform.ScaleY"));
return storyboard;
}
That's a fine way of doing it, WPF does not provide methods or events to automatically pulse. Yours is simple and as you've created it, you know how to modify it as you wish.

Doubleanimation Skew not working

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);

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