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事件中获取。
FlipView flipView;
ScrollViewer scrollViewer;
Compositor compositor;
CompositionPropertySet scrollPropSet; public DependencyObject AssociatedObject { get; private set; } public void Attach(DependencyObject associatedObject)
{
AssociatedObject = associatedObject;
if (associatedObject is FlipView flip) flipView = flip;
else throw new ArgumentException("对象不是FlipView");
scrollViewer = Helper.FindVisualChild<ScrollViewer>(flipView, "ScrollingHost");
if (scrollViewer == null)
{
flipView.Loaded += FlipView_Loaded;
}
else InitCompositionResources(scrollViewer);
} private void FlipView_Loaded(object sender, RoutedEventArgs e)
{
flipView.Loaded -= FlipView_Loaded;
var scroll = Helper.FindVisualChild<ScrollViewer>(flipView, "ScrollingHost");
if (scroll == null) throw new ArgumentNullException("ScrollViewer为空");
else scrollViewer = scroll; InitCompositionResources(scrollViewer);
} void InitCompositionResources(ScrollViewer scroll)
{
if (compositor == null) compositor = ElementCompositionPreview.GetElementVisual(flipView).Compositor;
if (scroll == null) return; scrollPropSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
}
public static class Helper
{
public static T FindVisualChild<T>(DependencyObject obj, int Index = ) where T : DependencyObject
{
if (Index == -) return null;
int count = VisualTreeHelper.GetChildrenCount(obj);
int findedcount = ;
for (int i = ; i < count; i++)
{
DependencyObject child = Windows.UI.Xaml.Media.VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
{
if (findedcount == Index)
return (T)child;
else
{
findedcount++;
}
}
else
{
T childOfChild = FindVisualChild<T>(child, findedcount);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
public static T FindVisualChild<T>(DependencyObject obj, string name) where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(obj);
int findedcount = ;
for (int i = ; i < count; i++)
{
DependencyObject child = Windows.UI.Xaml.Media.VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
{
if ((child as FrameworkElement).Name == name)
return (T)child;
else
{
findedcount++;
}
}
else
{
T childOfChild = FindVisualChild<T>(child, findedcount);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
}
然后创建两个表达式动画,分别作用在中心点和缩放上。
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附加动画,虽然损失了部分性能,但是可以保证不出问题。
void InitAnimation()
{
if (compositor != null)
{
for (int i = ; i < flipView.Items.Count; i++)
{
var item = flipView.ContainerFromIndex(i);
if (item is UIElement ele)
{
var visual = ElementCompositionPreview.GetElementVisual(ele);
CenterPointAnimation.SetReferenceParameter("visual", visual);
visual.StartAnimation("CenterPoint", CenterPointAnimation);
visual.StopAnimation("Scale.X");
visual.StopAnimation("Scale.Y");
ScaleAnimation.SetReferenceParameter("visual", visual);
visual.StartAnimation("Scale.X", ScaleAnimation);
visual.StartAnimation("Scale.Y", ScaleAnimation);
}
}
}
}
最后在Loaded的最后也调用一次InitAnimation,大功告成。
UWP:使用Behavior实现FlipView简单缩放效果的更多相关文章
- Android 四种简单的动画(淡入淡出、旋转、移动、缩放效果)
最近在Android开发当中,用到的动画效果. public void onClick(View arg0) { // TODO 自动生成的方法存根 switch (arg0.getId()) { c ...
- [UWP]使用Picker实现一个简单的ColorPicker弹窗
在上一篇博文<[UWP]使用Popup构建UWP Picker>中我们简单讲述了一下使用Popup构建适用于MVVM框架下的弹窗层组件Picker的过程.但是没有应用实例的话可能体现不出P ...
- 《JavaScript 实战》:JavaScript 实现拖拽缩放效果
拖拉缩放效果,实现通过鼠标拖动来调整层的面积(宽高)大小,例如选框效果.这里的拖拉缩放比一般的选框复杂一点,能设置八个方位(方向)的固定触发点,能设置最小范围,最大范围和比例缩放. 跟拖放效果一样,程 ...
- web前端学习(三)css学习笔记部分(5)-- CSS动画--页面特效、HTML与CSS3简单页面效果实例
CSS动画--页面特效部分内容目前仅仅观看了解内容,记录简单笔记,之后工作了进行内容的补充 7. CSS动画--页面特效 7.1 2D.3D转换 7.1.1 通过CSS3转换,我们能够对元素进行 ...
- Query 一些简单的效果
Query 一些简单的效果 $(selector).hide(speed,callback); 隐藏 $(selector).show(speed,callback); 显示 $(selector). ...
- 原生JS封装简单动画效果
原生JS封装简单动画效果 一致使用各种插件,有时候对原生JS陌生了起来,所以决定封装一个简单动画效果,熟悉JS原生代码 function animate(obj, target,num){ if(ob ...
- iOS开发——实用技术OC篇&简单抽屉效果的实现
简单抽屉效果的实现 就目前大部分App来说基本上都有关于抽屉效果的实现,比如QQ/微信等.所以,今天我们就来简单的实现一下.当然如果你想你的效果更好或者是封装成一个到哪里都能用的工具类,那就还需要下一 ...
- HTML与CSS简单页面效果实例
本篇博客实现一个HTML与CSS简单页面效果实例 index.html <!DOCTYPE html> <html> <head> <meta charset ...
- iOS简单动画效果:闪烁、移动、旋转、路径、组合
#define kDegreesToRadian(x) (M_PI * (x) / 180.0) #define kRadianToDegrees(radian) (radian*180.0)/(M_ ...
随机推荐
- 原生封装ajax
01.声明一个全局变量 02.开始封装,判断参数 03.属性的var自定义 04.请求 01.请求行 02.请求头 03.请求发送 05.响应 01.事件监听onreadystatechange 02 ...
- HashMap源码深入研究
简介 HashMap是采用链表和位桶来来实现的,由于一个位桶存在元素太多会导致get效率低,因此在jdk1.8中采用的红黑树实现,当链表长度大于TREEIFY_THRESHOLD(值为8)时会转换为红 ...
- 关于Win7 内存变小处理方法
windows + R 输入msconfig 点击引导 点击高级选项 点击最大内存打钩,就好了,你重启,你的内存将恢复成原来的.
- 字典的定义和操作 (Python)
字典是由key and value 构成,无序结构(不想列表那样有固体位置): note:key 是唯一的,没有重复: 字典可以多级嵌套: 定义: dict1 = { # 由等式构成 dict = { ...
- Django--Uploaded Files以及Handlers
一.表示已经上传的文件(uploaded files)的类 表示已经上传的文件的类有下面几个: class UploadedFile 在文件上传的期间,实际的文件数据被存储在request.FILES ...
- 用Node.JS+MongoDB搭建个人博客(成品展示)
在博客里可以随意畅写和分享自己喜欢的技术,和网友分享知识也是一种提升.根据自己所发表的博客也能更加加深印象. 与此同时写博客也可以提高自己的写作能力(虽然不咋地),但我相信博客只会越写越有质量的. 博 ...
- thinkphp 3.2 发送邮件(Phpmailer)
1.在该模板下Conmon模块函数公共目录新建一个function.php <?php function Sendemail(){ vendor('PHPMailer.class#phpmail ...
- RxSwift 实战操作【注册登录】
前言 看了前面的文章,相信很多同学还不知道RxSwift该怎么使用,这篇文件将带领大家一起写一个 注册登录(ps:本例子采用MVVM)的例子进行实战.本篇文章是基于RxSwift3.0写的,采用的是C ...
- [js] 小谈 export (没总结完)
作用 导出变量/类 等等 用法 index.js 文件 export default name 仅导出一个变量 import name from './index.js' index.js 文件 ex ...
- Java并发编程——线程安全及解决机制简介
简介: 本文主要介绍了Java多线程环境下,可能会出现的问题(线程不安全)以及相应的解决措施.通过本文,你将学习到如下几块知识: 1. 为什么需要多线程(多线程的优势) 1. 多线程带来的问题—线程安 ...