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();