UWP:使用Behavior实现FlipView简单缩放效果

简介: 原文:UWP:使用Behavior实现FlipView简单缩放效果先上效果图 首先安装Behavior SDK:在Nuget中搜索安装 Microsoft.Xaml.Behaviors.Uwp.Managed 。
原文: UWP:使用Behavior实现FlipView简单缩放效果

先上效果图

首先安装Behavior SDK:在Nuget中搜索安装 Microsoft.Xaml.Behaviors.Uwp.Managed 。

然后新建类,AnimationFlipViewBehavior.cs,并继承DependencyObject和IBehavior接口:

namespace TestBehavior
{
    public class AnimationFlipViewBehavior: DependencyObject, IBehavior
    {
        public DependencyObject AssociatedObject { get; set; }
        public void Attach(DependencyObject associatedObject)
        {
            AssociatedObject  = associatedObject;
        }
        public void Detach()
        {

        }
    }
}

Attach是添加Behavior时被调用的方法,Detach是移除Behavior时被调用的方法。

这时在Attach中判断是否是FlipView,并且保存下来。然后按照老样子获取ScrollViewer,如果FlipView已经加载好了,就可以直接获取到ScrollViewer,否则要在FlipView的Loaded事件中获取。

 1 FlipView flipView;
 2 ScrollViewer scrollViewer;
 3 Compositor compositor;
 4 CompositionPropertySet scrollPropSet;
 5 
 6 public DependencyObject AssociatedObject { get; private set; }
 7 
 8 public void Attach(DependencyObject associatedObject)
 9 {
10     AssociatedObject = associatedObject;
11     if (associatedObject is FlipView flip) flipView = flip;
12     else throw new ArgumentException("对象不是FlipView");
13     scrollViewer = Helper.FindVisualChild<ScrollViewer>(flipView, "ScrollingHost");
14     if (scrollViewer == null)
15     {
16         flipView.Loaded += FlipView_Loaded;
17     }
18     else InitCompositionResources(scrollViewer);
19 }
20 
21 private void FlipView_Loaded(object sender, RoutedEventArgs e)
22 {
23     flipView.Loaded -= FlipView_Loaded;
24     var scroll = Helper.FindVisualChild<ScrollViewer>(flipView, "ScrollingHost");
25     if (scroll == null) throw new ArgumentNullException("ScrollViewer为空");
26     else scrollViewer = scroll;
27 
28     InitCompositionResources(scrollViewer);
29 }
30 
31 void InitCompositionResources(ScrollViewer scroll)
32 {
33     if (compositor == null) compositor = ElementCompositionPreview.GetElementVisual(flipView).Compositor;
34     if (scroll == null) return;
35 
36     scrollPropSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
37 }
View Code
 1 public static class Helper
 2 {
 3     public static T FindVisualChild<T>(DependencyObject obj, int Index = 0) where T : DependencyObject
 4     {
 5         if (Index == -1) return null;
 6         int count = VisualTreeHelper.GetChildrenCount(obj);
 7         int findedcount = 0;
 8         for (int i = 0; i < count; i++)
 9         {
10             DependencyObject child = Windows.UI.Xaml.Media.VisualTreeHelper.GetChild(obj, i);
11             if (child != null && child is T)
12             {
13                 if (findedcount == Index)
14                     return (T)child;
15                 else
16                 {
17                     findedcount++;
18                 }
19             }
20             else
21             {
22                 T childOfChild = FindVisualChild<T>(child, findedcount);
23                 if (childOfChild != null)
24                     return childOfChild;
25             }
26         }
27         return null;
28     }
29     public static T FindVisualChild<T>(DependencyObject obj, string name) where T : DependencyObject
30     {
31         int count = VisualTreeHelper.GetChildrenCount(obj);
32         int findedcount = 0;
33         for (int i = 0; i < count; i++)
34         {
35             DependencyObject child = Windows.UI.Xaml.Media.VisualTreeHelper.GetChild(obj, i);
36             if (child != null && child is T)
37             {
38                 if ((child as FrameworkElement).Name == name)
39                     return (T)child;
40                 else
41                 {
42                     findedcount++;
43                 }
44             }
45             else
46             {
47                 T childOfChild = FindVisualChild<T>(child, findedcount);
48                 if (childOfChild != null)
49                     return childOfChild;
50             }
51         }
52         return null;
53     }
54 }
View Code

然后创建两个表达式动画,分别作用在中心点和缩放上。

ExpressionAnimation CenterPointAnimation;
ExpressionAnimation ScaleAnimation;

void InitCompositionResources(ScrollViewer scroll)
{
    if (compositor == null) compositor = ElementCompositionPreview.GetElementVisual(flipView).Compositor;
    if (scroll == null) return;

    scrollPropSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
    if (CenterPointAnimation == null)
    {
        CenterPointAnimation = compositor.CreateExpressionAnimation("Vector3(visual.Size.X/2,visual.Size.Y/2,0)");
    }
    if (ScaleAnimation == null)
    {
        ScaleAnimation = compositor.CreateExpressionAnimation("Clamp(1- (visual.Offset.X + scroll.Translation.X) / visual.Size.X * 0.4, 0f, 1f)");
        ScaleAnimation.SetReferenceParameter("scroll", scrollPropSet);
    }
}

这里着重说一下ScaleAnimation。

表达式中的Clamp(value,min,max)是内置函数,当value在min和max之间的时候返回value,小于min则返回min,大于max则返回max。

FlipView中是一个ScrollViewer,横向滚动,ScrollViewer内的元素的Visual.Offset.X控制Visual的位置,而不是默认为0。所以只要判断visual.Offset.X和scroll.Translation.X的关系,就能做出动画来。

然后写一个方法,给所有Items的容器附加上这些动画。

因为默认的Items并不是Observable的,有两种解决方案,一是设置ItemsSource为一个ObservableCollection,然后注册CollectionChanged事件。这样做会让控件和页面后台代码耦合度提升。为了更干净的代码结构,这里用一个性能低一些的方法,注册FlipView的SelectionChanged事件,在这里调用InitAnimation方法。

如果每次只给SelectedItem和左右的Item附加动画,PC上测试很完美,但是手机上,或者说触摸操作的时候,会出现动画未加载的问题。这里涉及到一个FlipView和Pivot的大坑。

在键鼠操作和代码操作SelectedIndex切换页面的时候,是先触发SelectionChanged事件,再播放动画的。但是触摸操作的时候,只有当你滑屏再送手后,系统才知道到底应不应该切换页面。所以我们每次送手播放完动画和触发SelectionChanged并不同步,动画自然就不会附加到后面的Item上,所以每次我们都给所有的Item附加动画,虽然损失了部分性能,但是可以保证不出问题。

 1 void InitAnimation()
 2 {
 3     if (compositor != null)
 4     {
 5         for (int i = 0; i < flipView.Items.Count; i++)
 6         {
 7             var item = flipView.ContainerFromIndex(i);
 8             if (item is UIElement ele)
 9             {
10                 var visual = ElementCompositionPreview.GetElementVisual(ele);
11                 CenterPointAnimation.SetReferenceParameter("visual", visual);
12                 visual.StartAnimation("CenterPoint", CenterPointAnimation);
13                 visual.StopAnimation("Scale.X");
14                 visual.StopAnimation("Scale.Y");
15                 ScaleAnimation.SetReferenceParameter("visual", visual);
16                 visual.StartAnimation("Scale.X", ScaleAnimation);
17                 visual.StartAnimation("Scale.Y", ScaleAnimation);
18             }
19         }
20     }
21 }

 

最后在Loaded的最后也调用一次InitAnimation,大功告成。

源代码下载

目录
相关文章
|
Windows
Customize Acrylic Brush in UWP Applications(在UWP中自定义亚克力笔刷)
原文 Customize Acrylic Brush in UWP Applications(在UWP中自定义亚克力笔刷) Windows 10 Fall Creators Update(Build 16299)添加了acrylic brush,这是一个类似于Windows 7 Aero效果的UI画笔。
1435 0
|
API C# 异构计算
一些WPF中的滤镜特效——Effect Library
原文:一些WPF中的滤镜特效——Effect Library WPF支持类似PhotoShop的滤镜功能,称之为Effect。在.Net 4.0中,WPF就废弃了对BitMapEffect的支持,转向使用支持GPU加速的Effect类,例如,我们可以使用如下代码生成带有阴影效果的按钮。
1534 0
|
UED
Silverlight & Blend动画设计系列九:动画(Animation)与视图状态管理(Visual State Manager)
原文:Silverlight & Blend动画设计系列九:动画(Animation)与视图状态管理(Visual State Manager)   Silverlight中的动画(Animation)与视图状态管理(Visual State Manager) 结合使用是非常常见的,动画用于管理对象在某段事件段内执行的动画动作,视图状态管理则用于控制对象在多个不同的视觉状态之间切换、导航。
796 0
WPF 圆形Loading
原文:WPF 圆形Loading 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a771948524/article/details/9271933 ...
1179 0
Silverlight & Blend动画设计系列六:动画技巧(Animation Techniques)之对象与路径转化、波感特效
原文:Silverlight & Blend动画设计系列六:动画技巧(Animation Techniques)之对象与路径转化、波感特效   当我们在进行Silverlight & Blend进行动画设计的过程中,可能需要设计出很多效果不一的图形图像出来作为动画的基本组成元素。
1050 0
|
算法 程序员 C#
【HLSL学习笔记】WPF Shader Effect Library算法解读之[BandedSwirl]
原文:【HLSL学习笔记】WPF Shader Effect Library算法解读之[BandedSwirl] 因工作原因,需要在Silverlight中使用Pixel Shader技术,这对于我来说可算是相当有难度了,首先我是个Java Web开发程序员,从来没正经地学过微软的开发语言和工具;其次,对于算法这种东西,向来有种天生的排斥,一看便头疼。
1787 0
|
算法 C#
【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur]
原文:【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur] 方位模糊是一个按照指定角度循环位移并叠加纹理,最后平均颜色值并输出的一种特效。
815 0
|
C#
WPF和Expression Blend开发实例:Loading动画
原文:WPF和Expression Blend开发实例:Loading动画 今天来点实际的,项目中可以真实使用的,一个Loading的动画,最后封装成一个控件,可以直接使用在项目中,先上图: 整个设计比较简单,就是在界面上画18个Path,然后通过动画改变OpacityMask的值来实现一种动态的效果.
1216 0
|
C#
【C#/WPF】修改图像的DPI、Resolution
原文:【C#/WPF】修改图像的DPI、Resolution 问题: WPF中默认使用的图像的DPI是96。如果我们使用的图素的DPI不是96时(比如是72),那么WPF会把图片的DPI自动改为96,导致图像加载出来的实际大小Width和Height会比想要的大(原图显示大小会是实际图大小的72/96 = 3/4),比如图片会在Image控件内显示超框了。
1439 0
|
Windows
背水一战 Windows 10 (70) - 控件(控件基类): UIElement - Transform3D(3D变换), Projection(3D投影)
原文:背水一战 Windows 10 (70) - 控件(控件基类): UIElement - Transform3D(3D变换), Projection(3D投影) [源码下载] 背水一战 Windows 10 (70) - 控件(控件基类): UIElement - Transform3D(3...
1165 0