diff --git a/Wpf_AiSportsMicrospace/Home.xaml b/Wpf_AiSportsMicrospace/Home.xaml index cc11610..3532627 100644 --- a/Wpf_AiSportsMicrospace/Home.xaml +++ b/Wpf_AiSportsMicrospace/Home.xaml @@ -4,6 +4,8 @@ xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl" Title="CoverFlow Demo" Height="400" Width="800"> - + + + diff --git a/Wpf_AiSportsMicrospace/Home.xaml.cs b/Wpf_AiSportsMicrospace/Home.xaml.cs index ccf3fc3..f56b0d9 100644 --- a/Wpf_AiSportsMicrospace/Home.xaml.cs +++ b/Wpf_AiSportsMicrospace/Home.xaml.cs @@ -33,8 +33,8 @@ namespace Wpf_AiSportsMicrospace new CoverImage( System.IO.Path.Combine(albumPath, "5.jpg"), System.IO.Path.Combine(projectRoot, "Resources/Img/Badge/5.jpg")), }; - // 设置 CoverFlow 图片 - coverFlow.SetImages(images, defaultSelectedIndex: 2); + // 设置 CoverFlow 图片s + //coverFlow.SetImages(images, defaultSelectedIndex: 2); } } } \ No newline at end of file diff --git a/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml b/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml new file mode 100644 index 0000000..0c9b038 --- /dev/null +++ b/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml @@ -0,0 +1,49 @@ + + + + + + + + + <_3DTools:Interactive3DDecorator> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs b/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs new file mode 100644 index 0000000..d169a80 --- /dev/null +++ b/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs @@ -0,0 +1,359 @@ +using _3DTools; +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.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Media.Media3D; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Wpf_AiSportsMicrospace.MyUserControl +{ + /// + /// CoverFlowControl1.xaml 的交互逻辑 + /// + public partial class CoverFlowControl1 : UserControl + { + public CoverFlowControl1() + { + InitializeComponent(); + InitControl(); + } + + #region 依赖属性注册 + public static readonly DependencyProperty CurrentMidIndexProperty = + DependencyProperty.Register( + "CurrentMidIndex", typeof(double), typeof(CoverFlowControl), + new FrameworkPropertyMetadata(new PropertyChangedCallback(CurrentMidIndexPropertyChangedCallback))); + + private static void CurrentMidIndexPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs arg) + { + var ctrl = sender as CoverFlowControl1; + ctrl?.ReLayoutInteractiveVisual3D(); + } + + public double CurrentMidIndex + { + get => (double)GetValue(CurrentMidIndexProperty); + set => SetValue(CurrentMidIndexProperty, value); + } + + public static readonly DependencyProperty ModelAngleProperty = + DependencyProperty.Register( + "ModelAngle", typeof(double), typeof(CoverFlowControl), + new FrameworkPropertyMetadata(70.0, new PropertyChangedCallback(ModelAnglePropertyChangedCallback))); + + private static void ModelAnglePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs arg) + { + var ctrl = sender as CoverFlowControl1; + ctrl?.ReLayoutInteractiveVisual3D(); + } + + public double ModelAngle + { + get => (double)GetValue(ModelAngleProperty); + set => SetValue(ModelAngleProperty, value); + } + + public static readonly DependencyProperty XDistanceBetweenModelsProperty = + DependencyProperty.Register( + "XDistanceBetweenModels", typeof(double), typeof(CoverFlowControl), + new FrameworkPropertyMetadata(0.5, XDistanceBetweenModelsPropertyChangedCallback)); + + private static void XDistanceBetweenModelsPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs arg) + { + var ctrl = sender as CoverFlowControl1; + ctrl?.ReLayoutInteractiveVisual3D(); + } + + public double XDistanceBetweenModels + { + get => (double)GetValue(XDistanceBetweenModelsProperty); + set => SetValue(XDistanceBetweenModelsProperty, value); + } + + public static readonly DependencyProperty ZDistanceBetweenModelsProperty = + DependencyProperty.Register( + "ZDistanceBetweenModels", typeof(double), typeof(CoverFlowControl), + new FrameworkPropertyMetadata(0.5, ZDistanceBetweenModelsPropertyChangedCallback)); + + private static void ZDistanceBetweenModelsPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs arg) + { + var ctrl = sender as CoverFlowControl1; + ctrl?.ReLayoutInteractiveVisual3D(); + } + + public double ZDistanceBetweenModels + { + get => (double)GetValue(ZDistanceBetweenModelsProperty); + set => SetValue(ZDistanceBetweenModelsProperty, value); + } + + public static readonly DependencyProperty MidModelDistanceProperty = + DependencyProperty.Register( + "MidModelDistance", typeof(double), typeof(CoverFlowControl), + new FrameworkPropertyMetadata(1.5, MidModelDistancePropertyChangedCallback)); + + private static void MidModelDistancePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs arg) + { + var ctrl = sender as CoverFlowControl1; + ctrl?.ReLayoutInteractiveVisual3D(); + } + + public double MidModelDistance + { + get => (double)GetValue(MidModelDistanceProperty); + set => SetValue(MidModelDistanceProperty, value); + } + #endregion + private void InitControl() + { + this.LoadImageToViewport3D(this.GetUserImages()); + + // 鼠标事件改为控件自身 + this.MouseDown += (s, e) => + { + if (e.LeftButton == MouseButtonState.Pressed) + this.CurrentMidIndex++; + else + this.CurrentMidIndex--; + }; + } + + /// + /// 获取当前用户的图片文件夹中的图片(不包含子文件夹) + /// + /// 返回图片路径列表 + private List GetUserImages() + { + List images = new List(); + + string path = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); + DirectoryInfo dir = new DirectoryInfo(path); + FileInfo[] files = dir.GetFiles("*.jpg", SearchOption.AllDirectories); + if (files != null) + { + foreach (FileInfo file in files) + { + images.Add(file.FullName); + } + } + + return images; + } + + /// + /// 添加图片到视口 + /// + /// + private void LoadImageToViewport3D(List images) + { + if (images == null) + { + return; + } + + for (int i = 0; i < images.Count; i++) + { + string imageFile = images[i]; + + InteractiveVisual3D iv3d = this.CreateInteractiveVisual3D(imageFile, i); + + this.viewport3D.Children.Add(iv3d); + } + + this.ReLayoutInteractiveVisual3D(); + } + private InteractiveVisual3D CreateInteractiveVisual3D(string imageFile, int index) + { + InteractiveVisual3D iv3d = new InteractiveVisual3D(); + iv3d.Visual = this.CreateVisual(imageFile, index); + iv3d.Geometry = this.CreateGeometry3D(); + iv3d.Transform = this.CreateEmptyTransform3DGroup(); + + return iv3d; + } + /// + /// 创建一个空的Transform3DGroup + /// + /// + private Transform3DGroup CreateEmptyTransform3DGroup() + { + Transform3DGroup group = new Transform3DGroup(); + group.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0))); + group.Children.Add(new TranslateTransform3D(new Vector3D())); + group.Children.Add(new ScaleTransform3D()); + + return group; + } + + /// + /// 创建3D图形 + /// + /// 创建的3D图形 + private Geometry3D CreateGeometry3D() + { + MeshGeometry3D geometry = new MeshGeometry3D(); + + geometry.Positions = new Point3DCollection(); + geometry.Positions.Add(new Point3D(-1, 1, 0)); + geometry.Positions.Add(new Point3D(-1, -1, 0)); + geometry.Positions.Add(new Point3D(1, -1, 0)); + geometry.Positions.Add(new Point3D(1, 1, 0)); + + geometry.TriangleIndices = new Int32Collection(); + geometry.TriangleIndices.Add(0); + geometry.TriangleIndices.Add(1); + geometry.TriangleIndices.Add(2); + geometry.TriangleIndices.Add(0); + geometry.TriangleIndices.Add(2); + geometry.TriangleIndices.Add(3); + + geometry.TextureCoordinates = new PointCollection(); + geometry.TextureCoordinates.Add(new Point(0, 0)); + geometry.TextureCoordinates.Add(new Point(0, 1)); + geometry.TextureCoordinates.Add(new Point(1, 1)); + geometry.TextureCoordinates.Add(new Point(1, 0)); + + return geometry; + } + + + /// + /// 由指定的图片路径创建一个可视对象 + /// + /// 图片路径 + /// 创建的可视对象 + private Visual CreateVisual(string imageFile, int index) + { + BitmapImage bmp = null; + + try + { + bmp = new BitmapImage(new Uri(imageFile)); + } + catch + { + } + + Image img = new Image(); + img.Width = 50; + img.Source = bmp; + + Border outBordre = new Border(); + outBordre.BorderBrush = Brushes.White; + outBordre.BorderThickness = new Thickness(0.5); + outBordre.Child = img; + + outBordre.MouseDown += delegate (object sender, MouseButtonEventArgs e) + { + this.CurrentMidIndex = index; + e.Handled = true; + }; + + return outBordre; + + } + + /// + /// 重新布局3D内容 + /// + private void ReLayoutInteractiveVisual3D() + { + int j = 0; + for (int i = 0; i < this.viewport3D.Children.Count; i++) + { + InteractiveVisual3D iv3d = this.viewport3D.Children[i] as InteractiveVisual3D; + if (iv3d != null) + { + double angle = 0; + double offsetX = 0; + double offsetZ = 0; + this.GetTransformOfInteractiveVisual3D(j++, this.CurrentMidIndex, out angle, out offsetX, out offsetZ); + + + NameScope.SetNameScope(this, new NameScope()); + this.RegisterName("iv3d", iv3d); + Duration time = new Duration(TimeSpan.FromSeconds(0.3)); + + DoubleAnimation angleAnimation = new DoubleAnimation(angle, time); + DoubleAnimation xAnimation = new DoubleAnimation(offsetX, time); + DoubleAnimation zAnimation = new DoubleAnimation(offsetZ, time); + + Storyboard story = new Storyboard(); + story.Children.Add(angleAnimation); + story.Children.Add(xAnimation); + story.Children.Add(zAnimation); + + Storyboard.SetTargetName(angleAnimation, "iv3d"); + Storyboard.SetTargetName(xAnimation, "iv3d"); + Storyboard.SetTargetName(zAnimation, "iv3d"); + + Storyboard.SetTargetProperty( + angleAnimation, + new PropertyPath("(ModelVisual3D.Transform).(Transform3DGroup.Children)[0].(RotateTransform3D.Rotation).(AxisAngleRotation3D.Angle)")); + + Storyboard.SetTargetProperty( + xAnimation, + new PropertyPath("(ModelVisual3D.Transform).(Transform3DGroup.Children)[1].(TranslateTransform3D.OffsetX)")); + Storyboard.SetTargetProperty( + zAnimation, + new PropertyPath("(ModelVisual3D.Transform).(Transform3DGroup.Children)[1].(TranslateTransform3D.OffsetZ)")); + + story.Begin(this); + + } + } + } + + /// + /// 依照InteractiveVisual3D在列表中的序号来变换其位置等 + /// + /// 在列表中的序号 + /// 列表中被作为中间项的序号 + private void GetTransformOfInteractiveVisual3D(int index, double midIndex, out double angle, out double offsetX, out double offsetZ) + { + double disToMidIndex = index - midIndex; + + + //旋转,两翼的图片各旋转一定的度数 + angle = 0; + if (disToMidIndex < 0) + { + angle = this.ModelAngle;//左边的旋转N度 + } + else if (disToMidIndex > 0) + { + angle = (-this.ModelAngle);//右边的旋转-N度 + } + + + + //平移,两翼的图片逐渐向X轴负和正两个方向展开 + offsetX = 0;//中间的不平移 + if (Math.Abs(disToMidIndex) <= 1) + { + offsetX = disToMidIndex * this.MidModelDistance; + } + else if (disToMidIndex != 0) + { + offsetX = disToMidIndex * this.XDistanceBetweenModels + (disToMidIndex > 0 ? this.MidModelDistance : -this.MidModelDistance); + } + + + //两翼的图片逐渐向Z轴负方向移动一点,造成中间突出(离观众较近的效果) + offsetZ = Math.Abs(disToMidIndex) * -this.ZDistanceBetweenModels; + + } + } +}