WPF 图片轮播效果(2D轮转)
<UserControl x:Class="CustomControl.Carousel2DView" 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:CustomControl" mc:Ignorable="d" Width="800" Height="200" x:Name="CarouselBanner"> <Grid x:Name="GdRoot" Width="800" Height="200"> <Canvas x:Name="CvMain"> </Canvas> </Grid> </UserControl>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
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.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace CustomControl
{
/// <summary>
/// Carousel2DView.xaml 的交互逻辑
/// </summary>
public partial class Carousel2DView : UserControl,INotifyPropertyChanged
{
public List<string> FileItems;
int clickIndex = 0;
DispatcherTimer timer;
int timeInterval = 4;
public Carousel2DView()
{
InitializeComponent();
string sPath = AppDomain.CurrentDomain.BaseDirectory + "Resources/Banner";
if (!Directory.Exists(sPath))
Directory.CreateDirectory(sPath);
List<string> ltFiles = FolderHelper.GetAllFileFullName(sPath);
this.FileItems = ltFiles;
this.Loaded += Carousel2DView_Loaded;
this.Unloaded += Carousel2DView_Unloaded;
this.DataContext = this;
}
private void Carousel2DView_Unloaded(object sender, RoutedEventArgs e)
{
this.Unloaded -= Carousel2DView_Unloaded;
}
private void Carousel2DView_Loaded(object sender, RoutedEventArgs e)
{
this.Loaded -= Carousel2DView_Loaded;
this.CreateElements();
this.GdRoot.MouseLeftButtonDown += GdRoot_MouseLeftButtonDown;
this.MouseMove += Carousel2DView_MouseMove;
this.MouseUp += Carousel2DView_MouseUp;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(timeInterval);
timer.Tick += new EventHandler(RefreshLocation);
timer.Start();
}
public void ReStartTimer()
{
if (timer == null)
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(timeInterval);
timer.Tick += new EventHandler(RefreshLocation);
timer.Start();
}
else {
timer.Stop();
timer.Tick -= RefreshLocation;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(timeInterval);
timer.Tick += new EventHandler(RefreshLocation);
timer.Start();
}
}
private void RefreshLocation(object sender, EventArgs e)
{
this.Dispatcher.Invoke(new Action(() =>
{
if (clickIndex < this.ElementList.Count)
{
this.IsMouseDown = true;
this.CurNavItem = ElementList[clickIndex];
if (this.IsMouseDown && this.TotalMoveDegree < 50)
{
this.InertiaDegree = CenterDegree - this.CurNavItem.Degree;
this.CurNavItem = null;
this.IsMouseDown = false;
if (this.InertiaDegree != 0)
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
clickIndex++;
}
}
else
{
clickIndex = 0;
}
}));
}
#region Create Elements
private double VisualCount = 10d;
private List<ImageItem> ElementList;
private double CenterDegree = 180d;
private double TotalDegree = 0;
public double GridWidth
{
get { return (double)GetValue(GridWidthProperty); }
set { SetValue(GridWidthProperty, value); }
}
public static readonly DependencyProperty GridWidthProperty =
DependencyProperty.Register("GridWidth", typeof(double), typeof(Carousel2DView));
public double GridHeight
{
get { return (double)GetValue(GridHeightProperty); }
set { SetValue(GridHeightProperty, value); }
}
public static readonly DependencyProperty GridHeightProperty =
DependencyProperty.Register("GridHeight", typeof(double), typeof(Carousel2DView));
public double ElementWidth
{
get { return (double)GetValue(ElementWidthProperty); }
set { SetValue(ElementWidthProperty, value); }
}
public static readonly DependencyProperty ElementWidthProperty =
DependencyProperty.Register("ElementWidth", typeof(double), typeof(Carousel2DView));
public double ElementHeight
{
get { return (double)GetValue(ElementHeightProperty); }
set { SetValue(ElementHeightProperty, value); }
}
public static readonly DependencyProperty ElementHeightProperty =
DependencyProperty.Register("ElementHeight", typeof(double), typeof(Carousel2DView));
public double Radius
{
get { return (double)GetValue(RadiusProperty); }
set { SetValue(RadiusProperty, value); }
}
public static readonly DependencyProperty RadiusProperty =
DependencyProperty.Register("Radius", typeof(double), typeof(Carousel2DView));
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private double GetScaledSize(double degrees)
{
return GetCoefficient(degrees);
}
private double GetCoefficient(double degrees)
{
return 1.0 - Math.Cos(ConvertToRads(degrees)) / 2.0 - 0.5;
}
private double ConvertToRads(double degrees)
{
return degrees * Math.PI / 180.0;
}
private int GetZValue(double degrees)
{
return (int)((360 * GetCoefficient(degrees)) * 1000);
}
public void CreateElements()
{
double dAverageDegree = 360d / VisualCount;
this.TotalDegree = this.FileItems.Count * dAverageDegree;
this.ElementList = new List<ImageItem>();
for (int i = 0; i < this.FileItems.Count; i++)
{
string sFile = this.FileItems[i];
ImageItem oItem = new ImageItem(sFile);
oItem.MouseLeftButtonDown += OItem_MouseLeftButtonDown;
oItem.MouseLeftButtonUp += OItem_MouseLeftButtonUp;
oItem.Width = this.ElementWidth;
oItem.Height = this.ElementHeight;
oItem.Y = 0d;
oItem.Degree = i * dAverageDegree;
this.ElementList.Add(oItem);
}
this.UpdateLocation();
//this.IsMouseDown = false;
//this.CurNavItem = null;
double dIntervalDegree = this.InertiaDegree * 0.4;
for (int i = 0; i < this.ElementList.Count; i++)
{
ImageItem oItem = this.ElementList[i];
oItem.Degree += dIntervalDegree;
}
this.UpdateLocation();
//this.InertiaDegree -= dIntervalDegree;
}
private ImageItem CurNavItem;
private void OItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
timer.Stop();
timer.Tick -= RefreshLocation;
if (this.IsMouseDown && CurNavItem == sender && this.TotalMoveDegree < 50)
{
this.InertiaDegree = CenterDegree - this.CurNavItem.Degree;
this.CurNavItem = null;
this.IsMouseDown = false;
if (this.InertiaDegree != 0)
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
e.Handled = true;
}
}
private void OItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
CurNavItem = sender as ImageItem;
}
private void UpdateLocation()
{
for (int i = 0; i < this.ElementList.Count; i++)
{
ImageItem oItem = this.ElementList[i];
if (oItem.Degree - this.CenterDegree >= this.TotalDegree / 2d)
oItem.Degree -= this.TotalDegree;
else if (this.CenterDegree - oItem.Degree > this.TotalDegree / 2d)
oItem.Degree += this.TotalDegree;
if (oItem.Degree >= 90d && oItem.Degree < 270d) // Degree 在90-270之间的显示
this.SetElementVisiable(oItem);
else
this.SetElementInvisiable(oItem);
}
}
private void SetElementVisiable(ImageItem oItem)
{
if (oItem == null)
return;
if (!oItem.IsVisible)
{
if (!this.CvMain.Children.Contains(oItem))
{
oItem.IsVisible = true;
this.CvMain.Children.Add(oItem);
}
}
this.DoUpdateElementLocation(oItem);
}
private void SetElementInvisiable(ImageItem oItem)
{
if (oItem.IsVisible)
{
if (this.CvMain.Children.Contains(oItem))
{
this.CvMain.Children.Remove(oItem);
oItem.IsVisible = false;
}
}
}
public void DoUpdateElementLocation(ImageItem oItem)
{
double CenterX = this.GdRoot.Width / 2.0;
double dX = -Radius * Math.Sin(ConvertToRads(oItem.Degree));
oItem.X = (dX + CenterX - this.ElementWidth / 2d);
double dScale = GetScaledSize(oItem.Degree);
oItem.ScaleX = dScale;
oItem.ScaleY = dScale;
//oItem.Opacity = dScale;
int nZIndex = GetZValue(oItem.Degree);
Canvas.SetZIndex(oItem, nZIndex);
}
#endregion
#region Drag And Move
private bool IsMouseDown = false;
private double PreviousX = 0;
private double CurrentX = 0;
private double IntervalDegree = 0;
private double InertiaDegree = 0;
private double TotalMoveDegree = 0;
private void GdRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.IsMouseDown = true;
this.IntervalDegree = 0;
this.PreviousX = e.GetPosition(this).X;
this.TotalMoveDegree = 0;
CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
}
private void Carousel2DView_MouseMove(object sender, MouseEventArgs e)
{
if (this.IsMouseDown)
{
this.CurrentX = e.GetPosition(this).X;
this.IntervalDegree = this.CurrentX - this.PreviousX;
this.TotalMoveDegree += Math.Abs(this.IntervalDegree * 0.5d);
this.InertiaDegree = this.IntervalDegree * 5d;
for (int i = 0; i < this.ElementList.Count; i++)
{
ImageItem oItem = this.ElementList[i];
oItem.Degree += this.IntervalDegree;
}
this.UpdateLocation();
this.PreviousX = this.CurrentX;
}
}
private void Carousel2DView_MouseUp(object sender, MouseButtonEventArgs e)
{
if (this.IsMouseDown)
{
this.IsMouseDown = false;
this.CurNavItem = null;
if (this.InertiaDegree != 0)
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
}
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
double dIntervalDegree = this.InertiaDegree * 0.4;
for (int i = 0; i < this.ElementList.Count; i++)
{
ImageItem oItem = this.ElementList[i];
oItem.Degree += dIntervalDegree;
}
this.UpdateLocation();
this.InertiaDegree -= dIntervalDegree;
if (Math.Abs(this.InertiaDegree) < 0.1)
CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
}
public void RefreshBanner()
{
this.CvMain.Children.Clear();
CreateElements();
ReStartTimer();
}
#endregion
public event Action OnReturn;
//private void BdrReturn_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
//{
// this.BdrReturn.MouseLeftButtonDown -= BdrReturn_MouseLeftButtonDown;
// CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
// this.IsEnabled = false;
// //CvUtils.EffectAnimation(this, new FadeTransitionEffect(), false, null, null, 0.5d, 0d,
// // () => {
// if (this.OnReturn != null)
// this.OnReturn();
// //});
//}
}
}
ImageItem
<UserControl x:Class="CustomControl.ImageItem" 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:CustomControl" mc:Ignorable="d" Width="230" Height="200" RenderTransformOrigin="0.5 0.5" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="{Binding Path=ScaleX}" ScaleY="{Binding Path=ScaleY}"/> <TranslateTransform X="{Binding Path=X}" Y="{Binding Path=Y}"/> </TransformGroup> </UserControl.RenderTransform> <Grid> <Border CornerRadius="0" Margin="0,0,0,0" BorderThickness="0" BorderBrush="White"> <!--<Border.Background> <SolidColorBrush Color="White" Opacity="0.5"/> </Border.Background>--> </Border> <Image Margin="0,0" Stretch="Fill" x:Name="ImgMain"/> <!--<TextBlock VerticalAlignment="Bottom" Margin="30,30" Foreground="White" TextWrapping="Wrap" x:Name="TbkTitle" TextAlignment="Center" FontSize="20"/>--> </Grid> </UserControl>
using System;
using System.Collections.Generic;
using System.IO;
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.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CustomControl
{
/// <summary>
/// ImageItem.xaml 的交互逻辑
/// </summary>
public partial class ImageItem : UserControl
{
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(ImageItem), new UIPropertyMetadata(0.0));
public double Y
{
get { return (double)GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
public static readonly DependencyProperty YProperty =
DependencyProperty.Register("Y", typeof(double), typeof(ImageItem), new UIPropertyMetadata(0.0));
public double ScaleX
{
get { return (double)GetValue(ScaleXProperty); }
set { SetValue(ScaleXProperty, value); }
}
public static readonly DependencyProperty ScaleXProperty =
DependencyProperty.Register("ScaleX", typeof(double), typeof(ImageItem), new UIPropertyMetadata(1.0));
public double ScaleY
{
get { return (double)GetValue(ScaleYProperty); }
set { SetValue(ScaleYProperty, value); }
}
public static readonly DependencyProperty ScaleYProperty =
DependencyProperty.Register("ScaleY", typeof(double), typeof(ImageItem), new UIPropertyMetadata(1.0));
public double Degree;
private string FileSrc="";
private bool _IsVisible = false;
public new bool IsVisible
{
get { return _IsVisible; }
set
{
_IsVisible = value;
if (value)
this.LoadUiImmediate();
}
}
private bool IsUiLoaded = false;
public void LoadUiImmediate()
{
if (!IsUiLoaded)
{
IsUiLoaded = true;
try {
if (File.Exists(FileSrc))
this.ImgMain.Source = new BitmapImage(new Uri(FileSrc));
}
catch { }
}
}
public ImageItem(string sFile)
{
InitializeComponent();
this.FileSrc = sFile;
//string sFileName = System.IO.Path.GetFileNameWithoutExtension(sFile);
//this.TbkTitle.Text = sFileName;
this.Loaded += ImageItem_Loaded;
this.DataContext = this;
}
private void ImageItem_Loaded(object sender, RoutedEventArgs e)
{
this.Loaded -= ImageItem_Loaded;
AsynchUtils.AsynchSleepExecuteFunc(this.Dispatcher, LoadUiImmediate, 0.5);
}
public void Dispose()
{
this.ImgMain.Source = null;
}
}
}
引用:
<local:Carousel2DView x:Name="MainBanner" GridWidth="800" GridHeight="200" ElementWidth="280" ElementHeight="200" Radius="420" Visibility="Collapsed"/>
MainBanner.GridWidth = 850;
MainBanner.GridHeight = 200;
MainBanner.ElementWidth = 280;
MainBanner.ElementHeight = 180;
MainBanner.Radius = 420;
MainBanner.RefreshBanner();

浙公网安备 33010602011771号