我有兴趣转储所有使用的动画表达式值。甚至有可能吗?
假设offsetExpression
的值是position
的值是什么?
MainPage.xaml
<ListView x:Name="ListView" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView.Header>
<RelativePanel x:Name="ScrollHeader" Background="#2D2D30">
<Border
RelativePanel.AlignLeftWithPanel="True"
x:Name="HeaderLogo"
Width="100"
Height="100"
BorderThickness="10" >
<Ellipse Fill="{StaticResource HeaderLogoGradient}" />
</Border>
<StackPanel RelativePanel.AlignVerticalCenterWithPanel="True" RelativePanel.RightOf="HeaderLogo">
<TextBlock x:Name="HeaderText" FontSize="25" Text="Sticky Header" Foreground="#FFFFFF"/>
</StackPanel>
</RelativePanel>
</ListView.Header>
<ListView.Items>
<Rectangle Fill="#DAECF4" Height="200" />
<Rectangle Fill="#91C5DD" Height="200" />
<Rectangle Fill="#DAECF4" Height="200" />
<Rectangle Fill="#91C5DD" Height="200" />
<Rectangle Fill="#DAECF4" Height="200" />
<Rectangle Fill="#91C5DD" Height="200" />
<Rectangle Fill="#DAECF4" Height="200" />
<Rectangle Fill="#91C5DD" Height="200" />
<Rectangle Fill="#DAECF4" Height="200" />
<Rectangle Fill="#91C5DD" Height="200" />
</ListView.Items>
</ListView>
MainPage.xaml.cs
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Retrieve the ScrollViewer that the GridView is using internally
var scrollViewer = ListView.GetFirstDescendantOfType<ScrollViewer>();
CompositionPropertySet scrollerPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
Compositor compositor = scrollerPropertySet.Compositor;
// Get the visual that represents our HeaderTextBlock
// And define the progress animation string
var headerVisual = ElementCompositionPreview.GetElementVisual(ScrollHeader);
String progress = "Clamp(Abs(scroller.Translation.Y) / 100.0, 0.0, 1.0)";
// Create the expression and add in our progress string.
var textExpression = compositor.CreateExpressionAnimation("Lerp(1.5, 1, " + progress + ")");
textExpression.SetReferenceParameter("scroller", scrollerPropertySet);
// Shift the header by 50 pixels when scrolling down
var offsetExpression = compositor.CreateExpressionAnimation($"(-scroller.Translation.Y - {progress} * 50)");
offsetExpression.SetReferenceParameter("scroller", scrollerPropertySet);
headerVisual.StartAnimation("Offset.Y", offsetExpression);
// Logo scale and transform
var logoHeaderScaleAnimation = compositor.CreateExpressionAnimation("Lerp(Vector2(1,1), Vector2(0.5, 0.5), " + progress + ")");
logoHeaderScaleAnimation.SetReferenceParameter("scroller", scrollerPropertySet);
var logoVisual = ElementCompositionPreview.GetElementVisual(HeaderLogo);
logoVisual.StartAnimation("Scale.xy", logoHeaderScaleAnimation);
var logoVisualOffsetAnimation = compositor.CreateExpressionAnimation($"Lerp(0, 50, {progress})");
logoVisualOffsetAnimation.SetReferenceParameter("scroller", scrollerPropertySet);
logoVisual.StartAnimation("Offset.Y", logoVisualOffsetAnimation);
// Offset the header title
Visual textVisual = ElementCompositionPreview.GetElementVisual(HeaderText);
Vector3 finalOffset = new Vector3(-45, 22, 0);
var headerOffsetAnimation = compositor.CreateExpressionAnimation($"Lerp(Vector3(0,0,0), finalOffset, {progress})");
headerOffsetAnimation.SetReferenceParameter("scroller", scrollerPropertySet);
headerOffsetAnimation.SetVector3Parameter("finalOffset", finalOffset);
textVisual.StartAnimation(nameof(Visual.Offset), headerOffsetAnimation);
}
该示例是对stickyheader的略微修改。我试图消除不必要的动画(或执行其他动画,例如放大),如果您在开始时就拉下ListView
会发生这种情况。在此之前,我试图通过转储表达式的值来了解动画的工作方式。
public static class VisualTreeHelperExtensions
{
public static T GetFirstDescendantOfType<T>(this DependencyObject start) where T : DependencyObject
{
return start.GetDescendantsOfType<T>().FirstOrDefault();
}
public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject
{
return start.GetDescendants().OfType<T>();
}
public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start)
{
var queue = new Queue<DependencyObject>();
var count = VisualTreeHelper.GetChildrenCount(start);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(start, i);
yield return child;
queue.Enqueue(child);
}
while (queue.Count > 0)
{
var parent = queue.Dequeue();
var count2 = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count2; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
yield return child;
queue.Enqueue(child);
}
}
}
public static T GetFirstAncestorOfType<T>(this DependencyObject start) where T : DependencyObject
{
return start.GetAncestorsOfType<T>().FirstOrDefault();
}
public static IEnumerable<T> GetAncestorsOfType<T>(this DependencyObject start) where T : DependencyObject
{
return start.GetAncestors().OfType<T>();
}
public static IEnumerable<DependencyObject> GetAncestors(this DependencyObject start)
{
var parent = VisualTreeHelper.GetParent(start);
while (parent != null)
{
yield return parent;
parent = VisualTreeHelper.GetParent(parent);
}
}
public static bool IsInVisualTree(this DependencyObject dob)
{
return Window.Current.Content != null && dob.GetAncestors().Contains(Window.Current.Content);
}
public static Rect GetBoundingRect(this FrameworkElement dob, FrameworkElement relativeTo = null)
{
if (relativeTo == null)
{
relativeTo = Window.Current.Content as FrameworkElement;
}
if (relativeTo == null)
{
throw new InvalidOperationException("Element not in visual tree.");
}
if (dob == relativeTo)
return new Rect(0, 0, relativeTo.ActualWidth, relativeTo.ActualHeight);
var ancestors = dob.GetAncestors().ToArray();
if (!ancestors.Contains(relativeTo))
{
throw new InvalidOperationException("Element not in visual tree.");
}
var pos =
dob
.TransformToVisual(relativeTo)
.TransformPoint(new Point());
var pos2 =
dob
.TransformToVisual(relativeTo)
.TransformPoint(
new Point(
dob.ActualWidth,
dob.ActualHeight));
return new Rect(pos, pos2);
}
}
}