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画笔。
1427 0
|
前端开发 容器 开发工具
[uwp]自定义Behavior之随意拖动
原文:[uwp]自定义Behavior之随意拖动   由于最近有需求,所以自定义了一个随意拖动元素的Behavior.   当然在使用这个自定义的Behavior时,有个小假设:拖动元素必须是Canvas容器的子元素。
826 0
|
API C# Windows
[UWP]用Shape做动画
原文:[UWP]用Shape做动画 相对于WPF/Silverlight,UWP的动画系统可以说有大幅提高,不过本文无意深入讨论这些动画API,本文将介绍使用Shape做一些进度、等待方面的动画,除此之外也会介绍一些相关技巧。
778 0
|
UED
Silverlight & Blend动画设计系列九:动画(Animation)与视图状态管理(Visual State Manager)
原文:Silverlight & Blend动画设计系列九:动画(Animation)与视图状态管理(Visual State Manager)   Silverlight中的动画(Animation)与视图状态管理(Visual State Manager) 结合使用是非常常见的,动画用于管理对象在某段事件段内执行的动画动作,视图状态管理则用于控制对象在多个不同的视觉状态之间切换、导航。
793 0
|
C#
在Microsoft Expression Blend 2 中绘制圆角矩形按钮
原文:在Microsoft Expression Blend 2 中绘制圆角矩形按钮 /* 声明:转载请保留此信息:http://www.BrawDraw.com, http://www.ZPXP.com 版权所有:a3news(AT)hotmail.com */(1)打开Blend 2,新建一个工程名称为:LinearGradientButton。
1177 0
Silverlight & Blend动画设计系列六:动画技巧(Animation Techniques)之对象与路径转化、波感特效
原文:Silverlight & Blend动画设计系列六:动画技巧(Animation Techniques)之对象与路径转化、波感特效   当我们在进行Silverlight & Blend进行动画设计的过程中,可能需要设计出很多效果不一的图形图像出来作为动画的基本组成元素。
1042 0
|
XML Android开发 数据格式
Material Design系列 - 自定义Behavior实现伸缩标题栏
引言 CoordinatorLayout+CollapsingToolbarLayout+Behavior真是一个好东西,很多复杂的UI交互效果都可以通过Behavior来实现,用了Behavior之后腰也不疼了,再也不会对设计师说这个实现不了了,只要给我时间我就实现给你看!今天带来第一个自定义Behavior:实现一个伸缩的标题栏。
1978 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控件内显示超框了。
1427 0
|
定位技术
openlayers4实现拖动Feature效果
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gisdoer/article/details/80227307 拖动矢量图层feature,获取拖动后feature http://www.
1200 0
|
Windows
背水一战 Windows 10 (71) - 控件(控件基类): UIElement - RenderTransform(2D变换), Clip(剪裁)
原文:背水一战 Windows 10 (71) - 控件(控件基类): UIElement - RenderTransform(2D变换), Clip(剪裁) [源码下载] 背水一战 Windows 10 (71) - 控件(控件基类): UIElement - RenderTransform(2...
1249 0