首先说两件事:

1、大爆炸我还记着呢,先欠着吧。。。

2、博客搬家啦,新地址:https://blog.ultrabluefire.cn/

==========下面是正文==========

前些日子看到Xaml Controls Gallery的ToggleTheme过渡非常心水,大概是这样的:

在17134 SDK里写法如下:

 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.BackgroundTransition>
<BrushTransition Duration="0:0:0.4" />
</Grid.BackgroundTransition>
</Grid>

这和我原本的思路完全不同。
我原本的思路是定义一个静态的笔刷资源,然后动画修改他的Color,但是这样就不能和系统的笔刷资源很好的融合了。怎么办呢?
前天半梦半醒间,突然灵光一现,感觉可以用一个附加属性作为中间层,给Background赋临时的笔刷实现过渡。
闲话不多说,开干。
首先我们需要一个画刷,这个画刷要实现以下功能:

  • 拥有一个Color属性。
  • 对Color属性赋值时会播放动画。
  • 动画播放结束触发事件。
  • 可以从外部清理事件。

这个可以使用Storyboard,CompositionAnimation手动Start或者ImplicitAnimation实现,在这里我选择了我最顺手的Composition实现。
下面贴代码:

 public class FluentSolidColorBrush : XamlCompositionBrushBase
{
Compositor Compositor => Window.Current.Compositor;
ColorKeyFrameAnimation ColorAnimation;
bool IsConnected; //被设置到控件属性时触发,例RootGrid.Background=new FluentSolidColorBrush();
protected override void OnConnected()
{
if (CompositionBrush == null)
{
IsConnected = true; ColorAnimation = Compositor.CreateColorKeyFrameAnimation(); //进度为0的关键帧,表达式为起始颜色。
ColorAnimation.InsertExpressionKeyFrame(0f, "this.StartingValue"); //进度为0的关键帧,表达式为参数名为Color的参数。
ColorAnimation.InsertExpressionKeyFrame(1f, "Color"); //创建颜色笔刷
CompositionBrush = Compositor.CreateColorBrush(Color);
}
} //从属性中移除时触发,例RootGrid.Background=null;
protected override void OnDisconnected()
{
if (CompositionBrush != null)
{
IsConnected = false; ColorAnimation.Dispose();
ColorAnimation = null;
CompositionBrush.Dispose();
CompositionBrush = null; //清除已注册的事件。
ColorChanged = null;
}
} public TimeSpan Duration
{
get { return (TimeSpan)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
} public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(TimeSpan), typeof(FluentSolidColorBrush), new PropertyMetadata(TimeSpan.FromSeconds(0.4d), (s, a) =>
{
if (a.NewValue != a.OldValue)
{
if (s is FluentSolidColorBrush sender)
{
if (sender.ColorAnimation != null)
{
sender.ColorAnimation.Duration = (TimeSpan)a.NewValue;
}
}
}
})); public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
} public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(Color), typeof(FluentSolidColorBrush), new PropertyMetadata(default(Color), (s, a) =>
{
if (a.NewValue != a.OldValue)
{
if (s is FluentSolidColorBrush sender)
{
if (sender.IsConnected)
{
//给ColorAnimation,进度为1的帧的参数Color赋值
sender.ColorAnimation.SetColorParameter("Color", (Color)a.NewValue); //创建一个动画批,CompositionAnimation使用批控制动画完成。
var batch = sender.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); //批内所有动画完成事件,完成时如果画刷没有Disconnected,则触发ColorChanged
batch.Completed += (s1, a1) =>
{
if (sender.IsConnected)
{
sender.OnColorChanged((Color)a.OldValue, (Color)a.NewValue);
}
};
sender.CompositionBrush.StartAnimation("Color", sender.ColorAnimation);
batch.End();
}
}
}
})); public event ColorChangedEventHandler ColorChanged;
private void OnColorChanged(Color oldColor, Color newColor)
{
ColorChanged?.Invoke(this, new ColorChangedEventArgs()
{
OldColor = oldColor,
NewColor = newColor
});
}
} public delegate void ColorChangedEventHandler(object sender, ColorChangedEventArgs args);
public class ColorChangedEventArgs : EventArgs
{
public Color OldColor { get; internal set; }
public Color NewColor { get; internal set; }
}

这样这个笔刷在每次修改Color的时候就能自动触发动画了,这完成了我思路的第一步,接下来我们需要一个Background属性设置时的中间层,用来给两个颜色之间添加过渡,这个使用附加属性和Behavior都可以实现。

我开始选择了Behavior,优点是可以在VisualState的Storyboard节点中赋值,而且由于每个Behavior都是独立的属性,可以存储更多的非公共属性、状态等;但是缺点也非常明显,使用Behavior要引入"Microsoft.Xaml.Behaviors.Uwp.Managed"这个包,使用的时候也要使用至少三行代码。
而附加属性呢,优点是原生和短,缺点是不能存储过多状态,也不能在Storyboard里使用,只能用Setter控制。
不过对于我们的需求呢,只需要Background和Duration两个属性,综上所述,最终我选择了附加属性实现。
闲话不多说,继续贴代码:

 public class TransitionsHelper : DependencyObject
{
public static Brush GetBackground(FrameworkElement obj)
{
return (Brush)obj.GetValue(BackgroundProperty);
} public static void SetBackground(FrameworkElement obj, Brush value)
{
obj.SetValue(BackgroundProperty, value);
} public static TimeSpan GetDuration(FrameworkElement obj)
{
return (TimeSpan)obj.GetValue(DurationProperty);
} public static void SetDuration(FrameworkElement obj, TimeSpan value)
{
obj.SetValue(DurationProperty, value);
} public static readonly DependencyProperty BackgroundProperty =
DependencyProperty.RegisterAttached("Background", typeof(Brush), typeof(TransitionsHelper), new PropertyMetadata(null, BackgroundPropertyChanged)); public static readonly DependencyProperty DurationProperty =
DependencyProperty.RegisterAttached("Duration", typeof(TimeSpan), typeof(TransitionsHelper), new PropertyMetadata(TimeSpan.FromSeconds(0.6d))); private static void BackgroundPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
if (d is FrameworkElement sender)
{
//拿到New和Old的Brush,因为Brush可能不是SolidColorBrush,这里不能使用强制类型转换。
var NewBrush = e.NewValue as SolidColorBrush;
var OldBrush = e.OldValue as SolidColorBrush; //下面分别获取不同控件的Background依赖属性。
DependencyProperty BackgroundProperty = null;
if (sender is Panel)
{
BackgroundProperty = Panel.BackgroundProperty;
}
else if (sender is Control)
{
BackgroundProperty = Control.BackgroundProperty;
}
else if (sender is Shape)
{
BackgroundProperty = Shape.FillProperty;
} if (BackgroundProperty == null) return; //如果当前笔刷是FluentSolidColorBrush,就将当前笔刷设置成旧笔刷,触发FluentSolidColorBrush的OnDisconnected,
//OnDisconnected中会清理掉ColorChanged上注册的事件,防止笔刷在卸载之后,动画完成时触发事件,导致运行不正常。
if (sender.GetValue(BackgroundProperty) is FluentSolidColorBrush tmp_fluent)
{
sender.SetValue(BackgroundProperty, OldBrush);
} //如果OldBrush或者NewBrush中有一个为空,就不播放动画,直接赋值
if (OldBrush == null || NewBrush == null)
{
sender.SetValue(BackgroundProperty, NewBrush);
return;
} var FluentBrush = new FluentSolidColorBrush()
{
Duration = GetDuration(sender),
Color = OldBrush.Color,
};
FluentBrush.ColorChanged += (s, a) =>
{
sender.SetValue(BackgroundProperty, NewBrush);
};
sender.SetValue(BackgroundProperty, FluentBrush);
FluentBrush.Color = NewBrush.Color;
}
}
}
}

调用的时候就不能直接设置Background了:

 <Grid helper:TransitionsHelper.Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="ToggleTheme" Click="ToggleTheme_Click">ToggleTheme</Button>
</Grid>

在Style里调用方法也类似:

 <!-- Element中 -->
<Grid x:Name="RootGrid" helper:TransitionsHelper.Background="{TemplateBinding Background}">
...
</Grid> <!-- VisualState中 -->
<VisualState x:Name="TestState">
<VisualState.Setter>
<Setter Target="RootGrid.(helper:TransitionsHelper.Background)" Value="{Binding RelativeSource={RelativeSource TemplatedParent},Path=SecondBackground}" />
</VisualState.Setter>
</VisualState>

这里还有个点要注意,在VisualState中,不管是Storyboard还是Setter,如果要修改模板绑定,直接写Value="{TemplateBinding XXX}"会报错,正确的写法是Value="{Binding RelativeSource={RelativeSource TemplatedParent},Path=SecondBackground}"。

最后附一张效果图:

原文地址:https://blog.ultrabluefire.cn/archives/13.html

UWP Background过渡动画的更多相关文章

  1. transtion:过渡动画

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px Monaco; color: #4f5d66 } p.p2 { margin: 0.0px 0 ...

  2. 用js触发CSS3-transition过渡动画

    用js触发CSS3-transition过渡动画 经过这几天的工作,让我进一步的了解到CSS3的强大,原本许多需要js才能实现的动画效果,现在通过CSS3就能轻易实现了,但是CSS3也有自身的不足,例 ...

  3. CSS基础篇之背景、过渡动画

    background-origin(背景原点) 设置元素背景图片的原始起始位置.必须保证背景是background-repeat为no-repeat属性才能生效. background-origin: ...

  4. CSS3初学篇章_5(背景样式/列表样式/过渡动画)

    背景样式 1.背景颜色语法:background-color : transparent | color body { background-color:#CCCCCC;} 2.渐变色彩语法:back ...

  5. Css中的变形及过渡动画

    在css3的标准中新增加了变形样式,这些样式使得网页中各元素的位置形状的变换变得更加容易.其语法如下: transform:none | <transform-function>+ 其中对 ...

  6. 遇到的一个移动端从下往上过渡的弹框,在Android下过渡动画的优化问题。

    优化之前: /* 分享弹框样式 */ .popUpDiv { width: 100vw; height: 100vh; transition: all 0.5s ease; position: fix ...

  7. 属性动画和Activity、Fragment过渡动画等

    主题是关于动画的,但是不是什么动画的内容都包括.先泛泛的介绍一下,然后详细的介绍一下翻代码找见的一个好玩的动画的使用.以下的内容包括Android 3和Android 3.1等引入的API,在使用中请 ...

  8. react过渡动画效果的实现,react-transition-group

    本文介绍react相关的过渡动画效果的实现 有点类似vue的transition组件,主要用于组件mount和unmount之前切换时应用动画效果 安装 cnpm install react-tran ...

  9. css3-12 transition+css或transform实现过渡动画

    css3-12 transition+css或transform实现过渡动画 一.总结 一句话总结:首先要设置hover后的效果,然后在transition里面指定执行哪些样式和执行时间为多长. 1. ...

随机推荐

  1. Two Sum IV - Input is a BST LT653

    Given a Binary Search Tree and a target number, return true if there exist two elements in the BST s ...

  2. 使用policheck 检测

    Policheck is a profing and testing tool for sensitive terminology and helps in ensuring thattrustwor ...

  3. 微信小程序开发工具常用快捷键

    格式调整 Ctrl+S:保存文件 Ctrl+[, Ctrl+]:代码行缩进 Ctrl+Shift+[, Ctrl+Shift+]:折叠打开代码块 Ctrl+C Ctrl+V:复制粘贴,如果没有选中任何 ...

  4. ManageEngine的EventLog Analyzer许可信息

  5. 别人的Linux私房菜(2)Linux简介

    同一操作系统无法在不同硬件平台上运行.架构. Bell实验室和麻省理工学院MIT和通用电气公司GE发起了Multics计划,分时兼容系统,300以上多终端连接主机. Unics 由Multics中的人 ...

  6. 鲍姆-韦尔奇算法求解HMM参数

    1. HMM模型参数求解概述 HMM模型参数求解根据已知的条件可以分为两种情况. 第一种情况较为简单,就是我们已知DD个长度为TT的观测序列和对应的隐藏状态序列,即{(O1,I1),(O2,I2),. ...

  7. SQL MAP 注入测试

    SQL MAP是一款测试系统是否有SQL漏洞的工具 下载地址: http://sqlmap.org/ sqlmap 是一款使用python编写的工具,所以需要安装python,需要安装python 为 ...

  8. DOM10-1节点层次

    DOM(问的那个对象模型)是针对HTML和XML文档的API.DOM描绘了一个层次化的节点树,允许开发人员添加.移除和修改页面的一部分. 每个节点都拥有各自的特点.数据和方法,另外和其他节点也存在某种 ...

  9. ubuntu 设置DNS

    sudo vi /etc/resolv.conf #加入nameserver 114.114.114.114

  10. 快速创建一个 Servlet 项目(2)

    1. 新建一个 webapp 参考 http://www.cnblogs.com/zno2/p/5909019.html 2.调整jdk版本 修改 pom.xml 文件,将jdk 调整为适当的版本,比 ...