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_ ...
随机推荐
- 【转】iOS 9 Storyboard 教程(一上)
转自:http://blog.csdn.net/yangmeng13930719363/article/details/49886547 Storyboard是在iOS5之后新增的一个令人兴奋的功能, ...
- Spring AOP的注解实现
适用场景: 记录接口方法的执行情况,记录相关状态到日志中. 注解类:LogMark.java package com.lichmama.spring.demo.annotation; import j ...
- Angular--ui-router的使用
先引用Angular然后引用ui-router 路由清单:我们依赖的ui.router中提供了一个服务$state,此时可以用config来配置这个服务.用$stateProvider的state方法 ...
- Java中的二进制及基本的位运算
Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二 ...
- MongoDB数据库基础操作
前面的话 为了保存网站的用户数据和业务数据,通常需要一个数据库.MongoDB和Node.js特别般配,因为Mongodb是基于文档的非关系型数据库,文档是按BSON(JSON的轻量化二进制格式)存储 ...
- layui中使用autocomplete.js
前言 在网站找了一大圈都是问题没有答案,记录记录谨防踩坑 layui版本:layui-v1.0.9_rls a(https://github.com/devbridge/jQuery-Autocomp ...
- 学习笔记TF030:实现AlexNet
ILSVRC(ImageNet Large Scale Visual Recognition Challenge)分类比赛.AlexNet 2012年冠军(top-5错误率16.4%,额外数据15.3 ...
- InnoDB关键特性之change buffer
一.关于IOT:索引组织表 表在存储的时候按照主键排序进行存储,同时在主键上建立一棵树,这样就形成了一个索引组织表,一个表的存储方式以索引的方式来组织存储的. 所以,MySQL表一定要加上主键,通过主 ...
- eclipse连接hadoop问题
1,首先可以测试:hafs dfsadmin -safemode leave2,如果出现下面的问题Error:Permission denied: user= ,access=READ_EXECUTE ...
- Objective-c 多线程操作 自定义NSOperation 模拟下载
写在前面 使用多线程下载图片,使用内存缓存和磁盘缓存. 这里只为理解NSOperation及其派生类 真要应用到APP中 请下载成熟的第三方库 效果 下载多张图片时可控制线程并发数 分析 自定义NSO ...