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

原文地址:https://www.cnblogs.com/candyzhmm/p/12713762.html