原文:
C# WPF 低仿网易云音乐(PC)Banner动画控件
由于技术有限没能做到一模一样的动画,只是粗略地做了一下。动画有点生硬,还有就是没做出网易云音乐的立体感。代码非常简单粗暴,而且我也写有很多注释,这里就不多啰嗦了,直接贴代码。
算了,啰嗦几句。原理是这样的,在自定义用户控件内添加3个border(左、中、右,以下分别简称为:b1、b2、b3),对border进行缩放和移动动画。往右切换时b1放大平移到b2的位置,b2缩小平移到b3的位置,b3平移到b1的位置,动画结束后重新记录3个border的左、中、右位置,然后如此循环。一次滚动有三个动画,分别写了3个方法来定义。
可加上透明动画、阴影,应该会更加好看些。
没有实现调用的方法,只是个纯动画项目,图片也是写死在前台代码中,需要在项目中使用时自行简单地修改即可。
低仿效果
网易云音乐原版
代码
后台
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace 网易云音乐Banner动画.Controls { /// <summary> /// CloudMusicBanner.xaml 的交互逻辑 /// </summary> public partial class CloudMusicBanner : UserControl { //代码所用涉及时间单位均是:秒 #region 一些变量 //左、中、右三张banner的位置 double leftlocation = 0, centerlocation = 0, rightlocation = 0; //每张banner的动画执行时间 double AnimationTime = 0.4; //非中间banner的遮盖层透明度 double bopacity = 0.65; //没有交互时动画自动执行间隔时间 double timeranimation_time = 4; //动画播放状态(当前动画是否在执行) bool isplay = false; //三个banner border变量,用于暂时保存 Border b_left, b_center, b_right; //通用缓动函数,提升动画流畅感 EasingFunctionBase easeFunction; //banner集合,用于定位和记录border的位置(左,中,右) Dictionary<Location, Border> Banners = new Dictionary<Location, Border>(); DispatcherTimer timeranimation; #endregion public CloudMusicBanner() { InitializeComponent(); //将三张banner(border)添加到banner集合 Banners.Add(Location.Left, left); Banners.Add(Location.Center, center); Banners.Add(Location.Right, right); //控件加载完成后 Loaded += (e, c) => { //首次启动设置三张banner的位置、大小信息 SetLocation(Location.Left, left); SetLocation(Location.Center, center); SetLocation(Location.Right, right); //启动定时器,用于自动播放滚动动画 TimerAnimationStart(); }; //初始化缓动函数 //quadraticease的easeout mode是从快到慢 //参考了博客:http://www.cnblogs.com/xwlyun/archive/2012/09/11/2680579.html easeFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut }; //初始化定时器 timeranimation = new DispatcherTimer(); //设置定时器的间隔时间 timeranimation.Interval = TimeSpan.FromSeconds(timeranimation_time); } #region 交互事件 private void UserControl_MouseEnter(object sender, MouseEventArgs e) { //鼠标移入控件时显示两个“左/右”图标按钮 toleftbtn.Opacity = 1; } private void UserControl_MouseLeave(object sender, MouseEventArgs e) { //鼠标移出控件时隐藏两个“左/右”图标按钮 toleftbtn.Opacity = 0; } private void toleftbtn_Click(object sender, RoutedEventArgs e) { //向左图标按钮点击 LeftAnimation(); } private void torightbtn_Click(object sender, RoutedEventArgs e) { RightAnimation(); } #endregion //左切换动画时三张banner向右滚动 /* * 即中间的border移动至:右 * 右边的border移动至:左 * 左边的border移动至:中 */ #region 左切换动画 public void LeftAnimation() { //启动动画时停止定时器 timeranimation.Stop(); //设置动画播放状态为真 isplay = true; //启动相应的动画 LefttoCenterAnimation(); CentertoRightAnimation(); RighttoLeftAnimation(); } //【仅注释此方法,以下代码均大多重复故不再注释】 #region 左切换动画-中向右动画 public void CentertoRightAnimation() { //记录动画结束后的位置,即当动画结束后中间的BORDER移动到右变成了右,代码:Banners[Location.Right] = b_center; b_center = Banners[Location.Center]; //设置一下border的显示层级 Grid.SetZIndex(Banners[Location.Center], 2); //获取透明遮盖图层,设置透明度 /* * <Grid Background="Black" Panel.ZIndex="2"></Grid> * 透明遮盖图层在设计代码中的每个border内 * */ GetOpacityGrid(b_center).Opacity = bopacity; //定义一个缩放转换对象(用于banner从大到小的动画) ScaleTransform scale = new ScaleTransform(); //需要设置中心点y坐标为控件的高度,不设置的话border是靠在顶部执行动画的。 scale.CenterY = this.ActualHeight; //定义一个水平移动转换对象 TranslateTransform ts = new TranslateTransform(); //定义一个转换集合 TransformGroup group = new TransformGroup(); //将上面的缩放、平移转换对象添加到集合 group.Children.Add(scale); group.Children.Add(ts); //将转换集合赋予给中心banner Banners[Location.Center].RenderTransform = group; //定义一个缩放动画 DoubleAnimation scaleAnimation = new DoubleAnimation() { //从1(100%即默认比例) From = 1, //到0.95(95%即从默认比例缩小到95%的比例大小) To = 0.95, //设置缓动函数 EasingFunction = easeFunction, //动画执行所需时间 Duration = TimeSpan.FromSeconds(AnimationTime) }; //定义一个移动动画(用于banner从左到右.....移动动画等) DoubleAnimation moveAnimation = new DoubleAnimation() { //从中心banner位置 From = centerlocation, //移动到右banner位置 To = rightlocation, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; //启动缩放动画 scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); //启动平移动画 ts.BeginAnimation(TranslateTransform.XProperty, moveAnimation); } #endregion #region 左切换动画-右向左动画 public void RighttoLeftAnimation() { b_right = Banners[Location.Right]; Grid.SetZIndex(Banners[Location.Right], 1); GetOpacityGrid(b_right).Opacity = bopacity; ScaleTransform scale = new ScaleTransform(); //缩放 scale.CenterY = this.ActualHeight; TranslateTransform ts = new TranslateTransform();//平移 TransformGroup group = new TransformGroup(); group.Children.Add(scale); group.Children.Add(ts); Banners[Location.Right].RenderTransform = group; DoubleAnimation scaleAnimation = new DoubleAnimation() { From = 0.85, To = 0.95, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; DoubleAnimation moveAnimation = new DoubleAnimation() { From = rightlocation, To = leftlocation, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; //scaleAnimation.Completed += new EventHandler(scaleAnimation_Completed); // AnimationClock clock = scaleAnimation.CreateClock(); scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); //启动平移动画 ts.BeginAnimation(TranslateTransform.XProperty, moveAnimation); } #endregion #region 左切换动画-左向中动画 public void LefttoCenterAnimation() { b_left = Banners[Location.Left]; Grid.SetZIndex(Banners[Location.Left], 3); GetOpacityGrid(b_left).Opacity = 0; ScaleTransform scale = new ScaleTransform(); //缩放 //scale.CenterX = 0; scale.CenterY = this.ActualHeight; TranslateTransform ts = new TranslateTransform();//平移 TransformGroup group = new TransformGroup(); group.Children.Add(scale); group.Children.Add(ts); Banners[Location.Left].RenderTransform = group; DoubleAnimation scaleAnimation = new DoubleAnimation() { From = 0.95, To = 1, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; DoubleAnimation moveAnimation = new DoubleAnimation() { From = leftlocation, To = centerlocation, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; scaleAnimation.Completed += new EventHandler(LeftAnimation_Completed); // AnimationClock clock = scaleAnimation.CreateClock(); scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); //启动平移动画 ts.BeginAnimation(TranslateTransform.XProperty, moveAnimation); } #endregion #region 动画结束 private void LeftAnimation_Completed(object sender, EventArgs e) { //动画结束后将banner集合的位置重新设置 Banners[Location.Left] = b_right; Banners[Location.Center] = b_left; Banners[Location.Right] = b_center; //此时动画结束 isplay = false; //启动定时器 TimerAnimationStart(); } #endregion #endregion #region 右切换动画 public void RightAnimation() { timeranimation.Stop(); isplay = true; LefttoRightAnimation(); CentertoLeftAnimation(); RighttoCenterAnimation(); } #region 右切换动画-左向右动画 public void LefttoRightAnimation() { b_left = Banners[Location.Left]; Grid.SetZIndex(Banners[Location.Left], 1); GetOpacityGrid(b_left).Opacity = bopacity; ScaleTransform scale = new ScaleTransform(); //缩放 scale.CenterY = this.ActualHeight; TranslateTransform ts = new TranslateTransform();//平移 TransformGroup group = new TransformGroup(); group.Children.Add(scale); group.Children.Add(ts); Banners[Location.Left].RenderTransform = group; DoubleAnimation scaleAnimation = new DoubleAnimation() { From = 0.85, To = 0.95, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; DoubleAnimation moveAnimation = new DoubleAnimation() { From = leftlocation, To = rightlocation, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); //启动平移动画 ts.BeginAnimation(TranslateTransform.XProperty, moveAnimation); } #endregion #region 右切换动画-中向左动画 public void CentertoLeftAnimation() { b_center = Banners[Location.Center]; Grid.SetZIndex(Banners[Location.Center], 2); GetOpacityGrid(b_center).Opacity = bopacity; ScaleTransform scale = new ScaleTransform(); //缩放 scale.CenterY = this.ActualHeight; TranslateTransform ts = new TranslateTransform();//平移 TransformGroup group = new TransformGroup(); group.Children.Add(scale); group.Children.Add(ts); Banners[Location.Center].RenderTransform = group; DoubleAnimation scaleAnimation = new DoubleAnimation() { From = 1, To = 0.95, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; DoubleAnimation moveAnimation = new DoubleAnimation() { From = centerlocation, To = leftlocation, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; //scaleAnimation.Completed += new EventHandler(scaleAnimation_Completed); // AnimationClock clock = scaleAnimation.CreateClock(); scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); //启动平移动画 ts.BeginAnimation(TranslateTransform.XProperty, moveAnimation); } #endregion #region 右切换动画-右向中动画 public void RighttoCenterAnimation() { b_right = Banners[Location.Right]; //SetZindex(b_right); Grid.SetZIndex(Banners[Location.Right], 3); GetOpacityGrid(b_right).Opacity = 0; ScaleTransform scale = new ScaleTransform(); //缩放 //scale.CenterX = 0; scale.CenterY = this.ActualHeight; TranslateTransform ts = new TranslateTransform();//平移 TransformGroup group = new TransformGroup(); group.Children.Add(scale); group.Children.Add(ts); Banners[Location.Right].RenderTransform = group; DoubleAnimation scaleAnimation = new DoubleAnimation() { To = 1, From = 0.95, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; DoubleAnimation moveAnimation = new DoubleAnimation() { From = rightlocation, To = centerlocation, EasingFunction = easeFunction, Duration = TimeSpan.FromSeconds(AnimationTime) }; scaleAnimation.Completed += new EventHandler(RightAnimation_Completed); // AnimationClock clock = scaleAnimation.CreateClock(); scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); //启动平移动画 ts.BeginAnimation(TranslateTransform.XProperty, moveAnimation); } #endregion #region 动画结束 private void RightAnimation_Completed(object sender, EventArgs e) { Banners[Location.Left] = b_center; Banners[Location.Center] = b_right; Banners[Location.Right] = b_left; isplay = false; TimerAnimationStart(); } #endregion #endregion #region 初始化设置控件位置 //定义一个枚举类用于标识左中右三个banner(border),方便操作时获取到。 public enum Location { Left, Center, Right } //设置三个border的大小和位置 public void SetLocation(Location l, Border g) { //banner的大小 g.Width = 540; g.Height = 200; //给除中间banner外的border缩小一些 ScaleTransform scaleTransform = new ScaleTransform(); scaleTransform.ScaleX = 0.95; scaleTransform.ScaleY = 0.95; scaleTransform.CenterY = this.ActualHeight; //获取设置遮盖层的透明度 Grid opacity_grid = GetOpacityGrid(g); opacity_grid.Opacity = bopacity; switch (l) { case Location.Left: TranslateTransform tt_left = new TranslateTransform() { X = 0 }; TransformGroup group_left = new TransformGroup(); group_left.Children.Add(tt_left); group_left.Children.Add(scaleTransform); g.RenderTransform = group_left; break; case Location.Center: opacity_grid.Opacity = 0; TransformGroup group_center = new TransformGroup(); //计算中心banner的x位置 centerlocation = (this.ActualWidth - g.ActualWidth) / 2; TranslateTransform tt_center = new TranslateTransform() { X = centerlocation }; group_center.Children.Add(tt_center); g.RenderTransform = group_center; Grid.SetZIndex(g, 3); break; case Location.Right: //Grid.SetZIndex(g, 3); //计算右banner的X位置 rightlocation = (this.ActualWidth - Banners[Location.Left].ActualWidth * 0.95); TranslateTransform tt_right = new TranslateTransform() { X = rightlocation }; TransformGroup group_right = new TransformGroup(); //这里要注意先后顺序,否则位置会有偏差,必须先缩放再设置X坐标。 group_right.Children.Add(scaleTransform); group_right.Children.Add(tt_right); g.RenderTransform = group_right; break; } } #endregion //获取透明覆盖层 //代码来自:https://www.cnblogs.com/udoless/p/3381411.html #region 获取透明覆盖层 public Grid GetOpacityGrid(DependencyObject g) { Grid opacity_grid = GetChild<Grid>(g, typeof(Grid)); opacity_grid = GetChild<Grid>(opacity_grid, typeof(Grid)); return opacity_grid; } public T GetChild<T>(DependencyObject obj, Type typename) where T : FrameworkElement { DependencyObject child = null; List<T> childList = new List<T>(); for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++) { child = VisualTreeHelper.GetChild(obj, i); if (child is T && (((T)child).GetType() == typename)) { return ((T)child); } } return null; } #endregion #region 定时唤醒动画 public void TimerAnimationStart() { if (timeranimation.IsEnabled == false) { timeranimation.Start(); } timeranimation.Tick += (e, c) => { timeranimation.Stop(); if (isplay == false) { RightAnimation(); } }; } #endregion } }
前台
<UserControl x:Class="网易云音乐Banner动画.Controls.CloudMusicBanner" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="200" d:DesignWidth="762" MouseEnter="UserControl_MouseEnter" MouseLeave="UserControl_MouseLeave" > <Grid> <!--两个图标按钮,鼠标进入时显示--> <Button Name="toleftbtn" Opacity="0" Click="toleftbtn_Click" BorderThickness="0" HorizontalAlignment="Left" Background="Transparent" Panel.ZIndex="99" Width="50" Height="50" Cursor="Hand"> <Image Source="/网易云音乐Banner动画;component/Res/左.png" Width="16" Height="16"></Image> </Button> <Button Name="torightbtn" Opacity="{Binding ElementName=toleftbtn,Path=Opacity}" Click="torightbtn_Click" BorderThickness="0" HorizontalAlignment="Right" Background="Transparent" Panel.ZIndex="99" Width="50" Height="50" Cursor="Hand"> <Image Source="/网易云音乐Banner动画;component/Res/右.png" Width="16" Height="16"></Image> </Button> <Grid HorizontalAlignment="Left"> <!--左边的banner--> <Border BorderBrush="#000000" BorderThickness="0" Width="540" Name="left" Background="Black"> <Grid> <!--透明遮盖层--> <Grid Background="Black" Panel.ZIndex="2"></Grid> <!--banner图片--> <Image Source="/网易云音乐Banner动画;component/Res/banner1.jpg" Stretch="Fill"></Image> </Grid> </Border> <!--中间的banner--> <Border BorderBrush="#96b0b3" BorderThickness="0" Width="540" Name="center" Background="Yellow" > <Grid> <Grid Background="Black" Panel.ZIndex="2"></Grid> <Image Source="/网易云音乐Banner动画;component/Res/banner2.jpg" Stretch="Fill"></Image> </Grid> </Border> <!--右边的banner--> <Border BorderBrush="#ab1491" BorderThickness="1" Name="right" Background="Red"> <Grid> <Grid Background="Black" Panel.ZIndex="2"></Grid> <Image Source="/网易云音乐Banner动画;component/Res/banner3.jpg" Stretch="Fill"></Image> </Grid> </Border> </Grid> </Grid> </UserControl>
被你发现了
项目下载在这