首先说两件事:

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. python网络socket编程

    一.服务端 #!/usr/bin/python # -*- coding: UTF-8 -*- import socket import sys from thread import * HOST = ...

  2. 【搜索】Shuffle'm Up

    运用第i个s12和第i+1个s12中,每个位置具有的确定的映射关系: pos = pos * 2 + 1 (pos < c) pos = pos * 2 - c * 2 (pos >= c ...

  3. OPNET仿真软件资料合集

    1. OPEN中国代理商业 http://www.credit-top.com/page/Default.asp?pageID=105

  4. CSS-尺寸与边框

    1.基础选择器的优先级 权值:标识当前选择器的重要程度,权值越大优先级越高. 元素选择器 1 类选择器 10 伪类选择器 10 ID选择器 100 内联样式 1000 选择器的权值加到一起,大的优先 ...

  5. mysql数据库进阶篇

    一.连表操作 1)为何需要连表操作 .把所有数据都存放于一张表的弊端 .表的组织结构复杂不清晰 .浪费空间 .扩展性极差 2)表设计,分析表与表之间的关系 寻找表与表之间的关系的套路 举例:emp表 ...

  6. 2019.01.20 bzoj3784: 树上的路径(二分答案+点分治)

    传送门 点分治好题. 题意简述:给一棵带边权的树,问所有路径中前mmm大的.m≤300000m\le300000m≤300000 思路: 网上有题解写了可以通过什么点分治序转化成超级钢琴那道题的做法蒟 ...

  7. JAVA遇上HTML-----JSP 篇基本概念

    Java Web简介 1.什么是WEB应用程序: Web应用程序是一种可以通过Web访问的应用程序.Web应用程序的一个最大好处是用户很容易访问应用程序.用户只需要有浏览器即可,不需要再安装其他软件. ...

  8. FontAwesome 4.7.0 中完整的675个图标样式CSS参考

    FontAwesome 4.7.0 中完整的675个图标样式CSS参考 用法:首先引入CSS文件:<link href="https://maxcdn.bootstrapcdn.com ...

  9. c++中typedef、define、const、inline之间的区别

    1.typedef和#define的区别 typedef int* pInt; , b = ; const pInt p1 = &a; //p1是常量指针 pInt const p2 = &a ...

  10. s5-1 CPU调度

    基本概念 通过多道程序设计得到 CPU 的最高利用率 (CPU-- I/O 脉冲周期 - - 进程的执行包括进程在 CPU 上执行和等待 I/O ) 进程的执行以 CPU 脉冲开始,其后跟着 I/O ...