Reputation: 71
I try to code in UWP app an Android Effect (ripple). So I create an EllipseGeometry Inside a grid (in my usercontrol) but when RadiusX and RadiusY of my ellipseGeometry play their animation, my EllipseGeometry growth out of my grid... I have tried to bounds path with a visible area and clip it to the path, but no success.
Here my XAML code :
<UserControl
x:Class="UIComponents.POIButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UIComponents"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="650" Background="White">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Gray">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<!--Animation Ellipse-->
<Grid x:Name="ellipseContainer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Grid.ColumnSpan="3">
<Path x:Name="path" Fill="Red" Stroke="Black" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Path.Data>
<EllipseGeometry x:Name="circleGeometry" Center="0,0" RadiusX="5" RadiusY="5" />
</Path.Data>
</Path>
</Grid>
<Rectangle x:Name="clickableRect" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" PointerPressed="clickableRect_PointerPressed" Fill="Transparent" Tapped="clickableRect_Tapped"/>
</Grid>
<UserControl.Resources>
<Storyboard x:Name="RipplePath">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="RadiusX" Storyboard.TargetName="circleGeometry">
<EasingDoubleKeyFrame KeyTime="0" Value="5"/>
<EasingDoubleKeyFrame KeyTime="0:0:10" Value="200"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="RadiusY" Storyboard.TargetName="circleGeometry">
<EasingDoubleKeyFrame KeyTime="0" Value="5"/>
<EasingDoubleKeyFrame KeyTime="0:0:10" Value="200"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
Here my Cs code :
private void clickableRect_Tapped(object sender, TappedRoutedEventArgs e)
{
Point touchPosition = e.GetPosition(ellipseContainer);
//RectangleGeometry visibleArea = new RectangleGeometry();
//visibleArea.Rect = new Rect(0, 0, 650, 100);
//path.Clip = visibleArea;
Storyboard animation = this.FindName("RipplePath") as Storyboard;
animation.Begin();
}
Here, the result :
Thanks a lot for your help :)
============== Try Justin XL's solution : result :
Thanks Justin for your Help, It seems the rectanglegeometry is animed too :
But nothing is over the grid.
the Xaml code is :
<UserControl
x:Class="UIComponents.POIButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UIComponents"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="650" Background="White">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Gray">
<Grid.Clip>
<RectangleGeometry Rect="0,0,650,100" />
</Grid.Clip> ...
and CS code is :
public POIButton()
{
this.InitializeComponent();
var visual = ElementCompositionPreview.GetElementVisual(this);
visual.Clip = visual.Compositor.CreateInsetClip();
}
================ XAML Result :
This XAML code not produce correct effect :
<UserControl
x:Class="UIComponents.POIButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UIComponents"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="100"
Width="650" Background="White">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Gray">
<Grid.Clip>
<RectangleGeometry Rect="0,0,650,100" />
</Grid.Clip>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<!--Animation Ellipse-->
<Grid x:Name="ellipseContainer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Grid.ColumnSpan="3">
<Path x:Name="path" Fill="Red" Stroke="Black" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Path.Data>
<EllipseGeometry x:Name="circleGeometry" Center="0,0" RadiusX="5" RadiusY="5" />
</Path.Data>
</Path>
</Grid>...
But CS Code works fine (with comment the line RectangleGeometry in XAML code) :
this.InitializeComponent();
var visual = ElementCompositionPreview.GetElementVisual(this);
visual.Clip = visual.Compositor.CreateInsetClip();
Produce this :
Thanks @Justin XL for it help :)
Upvotes: 3
Views: 1106
Reputation: 39006
You just need to clip the control from going outside of its boundaries. This can be achieved by either using
<Grid x:Name="Root" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Gray">
<Grid.Clip>
<RectangleGeometry Rect="0,0,200,80" />
</Grid.Clip>
in your XAML(200
is the width and 80
is the height), or
public POIButton()
{
InitializeComponent();
var visual = ElementCompositionPreview.GetElementVisual(this);
visual.Clip = visual.Compositor.CreateInsetClip();
}
in code-behind with the new Composition API. Note with the XAML approach you need to manually update the width and height by bindings or code-behind if the size of your control changes, where with Composition, you don't.
Also, I have noticed you used a path animation which runs on the UI thread(e.g. EnableDependentAnimation
). This can be replaced by a Ellipse
with a ScaleTransform
animation which is normally the recommended approach because of its much better performance.
XamlLight
Since you are developing for UWP, it's important to acknowledge what the platform can do, and the UWP way of achieving a similar effect, yet still honoring its own Fluent Design System.
I created the following FluentButton
control as shown below with the help of a new XamlLight
class introduced in 15063
. You will notice that the light follows your mouse cursor, and it ripples upon click/tap.
The second part is done by a customized XamlLight
which I called RippleXamlLight
, and this is how it's implemented -
First, create a class that inherits from XamlLight
.
public class RippleXamlLight : XamlLight
Then, in its OnConnected
override method, create a SpotLight
instance and a Vector3
animation that will be used to animate the light's Offset
. It will also take care of subscribing to pointer events such as PointerPressed
.
protected override void OnConnected(UIElement newElement)
{
_compositor = Window.Current.Compositor;
var spotLight = CreateSpotLight();
CompositionLight = spotLight;
_lightRippleOffsetAnimation = CreateLightRippleOffsetAnimation();
SubscribeToPointerEvents();
AddTargetElement(GetId(), newElement);
...
}
}
Finally, kick off the animation whenever the control is pressed. The Offset
value is provided by the pointer position and a _rippleOffsetZ
which is calculated based on the size of the control.
private void OnPointerPressed(object sender, PointerRoutedEventArgs e) =>
StartLightRippleOffsetAnimation(e.GetCurrentPoint((UIElement)sender).Position.ToVector2());
private void StartLightRippleOffsetAnimation(Vector2 position)
{
var startingPoisition = new Vector3(position, 0.0f);
_lightRippleOffsetAnimation?.InsertKeyFrame(0.0f, startingPoisition);
_lightRippleOffsetAnimation?.InsertKeyFrame(1.0f, new Vector3(position.X, position.Y, _rippleOffsetZ));
CompositionLight?.StartAnimation("Offset", _lightRippleOffsetAnimation);
}
In case I didn't explain it clearly enough, here's the full source for your reference. :)
Upvotes: 8