WPF 应用 - 走马灯动画
1. 功能
- 做一个图片集合控件,在指定大小的区域内,以走马灯的动画效果呈现图片。
- 能根据图片的数量决定动画的方向。当图片集合的大小大于指定的大小,图片往反方向走,以便于能看到底部的图片;当图片集合的大小小于指定的大小时,图片往正方向走,以便于保证所有图片都一直在可视区域内。
- 根据图片的数量,决定动画的时间,保持速度不变。
2. 效果
3. 代码
3.1 内部动画流动控件 FlowImagesUserControl
<UserControl x:Class="WpfAppTemplate.FlowImagesUserControl"
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"
xmlns:local="clr-namespace:WpfAppTemplate"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" x:Name="selfUserControl">
<StackPanel Orientation="Horizontal">
<ItemsControl x:Name="itemsControl"
ItemsSource="{Binding}"
Loaded="itemsControl_Loaded"
MouseEnter="itemsControl_MouseEnter"
MouseLeave="itemsControl_MouseLeave">
<ItemsControl.RenderTransform>
<TransformGroup>
<TranslateTransform X="0" Y="0"></TranslateTransform>
</TransformGroup>
</ItemsControl.RenderTransform>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding PicPath}" ToolTip="{Binding Name}" x:Name="img"
Width="{Binding ImageWidth, ElementName=selfUserControl}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Resources>
<Storyboard x:Key="storyBoard">
<DoubleAnimation Storyboard.TargetName="itemsControl"
Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"
To="{Binding ToX, ElementName=selfUserControl}"
Duration="{Binding Duration, ElementName=selfUserControl}"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</ItemsControl.Resources>
</ItemsControl>
</StackPanel>
</UserControl>
public partial class FlowImagesUserControl : UserControl
{
/// <summary>
/// 图片大小
/// </summary>
public double ImageWidth
{
get { return (double)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(double), typeof(FlowImagesUserControl), new PropertyMetadata(60.0));
/// <summary>
/// 动画结束值
/// </summary>
public double ToX
{
get { return (double)GetValue(ToXProperty); }
set { SetValue(ToXProperty, value); }
}
public static readonly DependencyProperty ToXProperty =
DependencyProperty.Register("ToX", typeof(double), typeof(FlowImagesUserControl), new PropertyMetadata(500.0));
/// <summary>
/// 动画时间
/// </summary>
public Duration Duration
{
get { return (Duration)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(Duration), typeof(FlowImagesUserControl), new PropertyMetadata(new Duration(new System.TimeSpan(0,0,2))));
private Storyboard _storyboard;
public FlowItemsUserControl()
{
InitializeComponent();
_storyboard = itemsControl.FindResource("storyBoard") as Storyboard;
itemsControl.SizeChanged += (s, e) =>
{
if (this.IsLoaded)
{
InitParamsOfStoryboard();
}
};
}
private void itemsControl_Loaded(object sender, RoutedEventArgs e)
{
InitParamsOfStoryboard();
}
private void itemsControl_MouseEnter(object sender, MouseEventArgs e)
{
_storyboard.Pause();
}
private void itemsControl_MouseLeave(object sender, MouseEventArgs e)
{
_storyboard.Resume();
}
private void InitParamsOfStoryboard()
{
_storyboard.Stop();
// 当集合的大小发生改变时,根据集合的大小决定动画的位移
ToX = this.ActualWidth - itemsControl.ActualWidth;
// 根据位移来决定动画的总时间,以每秒走 25px 的速度,避免不同位移相同时间导致速度不可控
Duration = new Duration(new System.TimeSpan(0, 0, System.Math.Abs((int)ToX/25)));
_storyboard.Begin();
}
}
3.2 在窗口中添加 FlowImagesUserControl 控件
<local:FlowItemsUserControl DataContext="{Binding Cars}" ImageWidth="100"/>
<Button Grid.Row="1" Content="删除一辆车" Click="Button_Click"
Width="100" Background="DarkSalmon"/>
public partial class ContentWindow : Window
{
public ContentWindow()
{
InitializeComponent();
this.DataContext = new CarsViewModel();
}
private void Button_Click_DeleteACar(object sender, RoutedEventArgs e)
{
var carList = (this.DataContext as CarsViewModel).Cars;
if (carList != null && carList.Count > 0)
{
carList.RemoveAt(carList.Count - 1);
}
}
}
public class Car
{
public string Name { get; set; }
public string PicPath { get; set; }
}
public class NotifyPropertyChanged : System.ComponentModel.INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnProperty(string propertyName)
{
PropertyChangedEventHandler propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class CarsViewModel : NotifyPropertyChanged
{
private ObservableCollection<Car> _Cars;
public ObservableCollection<Car> Cars
{
get { return _Cars; }
set
{
_Cars = value;
OnProperty("Cars");
}
}
public CarsViewModel()
{
InitCars();
}
private void InitCars()
{
string path = AppDomain.CurrentDomain.BaseDirectory + "/Resources/Images/Cars";
if (!Directory.Exists(path))
{
return;
}
Cars = new ObservableCollection<Car>();
// 获取文件夹下的所有车辆图片
FileInfo[] fileArr = new DirectoryInfo(path).GetFiles();
for (int i = 0, l = fileArr.Count(); i < l; i++)
{
Cars.Add(new Car()
{
PicPath = fileArr[i].FullName,
Name = fileArr[i].Name.Split('.')[0] //fileArr[i].Name = 宝马.jpg
});
}
}
}
3.3 鼠标移入图片效果
鼠标移入图片时,动画会停止。这里再给图片添加一个鼠标移入效果,当鼠标移入时,图片变大,鼠标移出后,图片恢复大小。
<Style TargetType="Image" x:Key="MoveInBiggerImageStyle">
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform/>
</TransformGroup>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
To="1.1" Duration="0:0:0.2"/>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
To="1.1" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
To="1" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
To="1" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
...
<Image x:Name="img"
Source="{Binding PicPath}"
ToolTip="{Binding Name}"
Width="{Binding ImageWidth, ElementName=selfUserControl}"
Style="{StaticResource MoveInBiggerImageStyle}">
</Image>