285 lines
9.9 KiB
C#
Raw Normal View History

2025-09-22 16:49:49 +08:00
using System;
2025-09-22 15:22:53 +08:00
using System.Collections.Generic;
2025-09-22 17:02:19 +08:00
using System.Collections.ObjectModel;
using System.ComponentModel;
2025-09-22 15:22:53 +08:00
using System.Linq;
2025-09-23 10:15:06 +08:00
using System.Runtime.CompilerServices;
2025-09-22 15:22:53 +08:00
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;
2025-09-23 10:15:06 +08:00
using System.Windows.Media.Animation;
2025-09-22 15:22:53 +08:00
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
2025-09-26 16:46:22 +08:00
using System.Windows.Threading;
2025-09-22 15:22:53 +08:00
namespace Wpf_AiSportsMicrospace.MyUserControl
{
2025-09-22 16:49:49 +08:00
/// <summary>
/// CoverFlowControl.xaml 的交互逻辑
/// </summary>
2025-09-22 15:22:53 +08:00
public partial class CoverFlowControl : UserControl
2025-09-28 09:17:44 +08:00
{// 新增事件:进度条完成
2025-09-26 16:46:22 +08:00
public event Action<CoverFlowItem> ProgressCompleted;
2025-09-28 09:17:44 +08:00
public ObservableCollection<CoverFlowItem> Images { get; set; } = new ObservableCollection<CoverFlowItem>();
2025-09-23 10:15:06 +08:00
private int _selectedIndex = 0;
2025-09-28 09:17:44 +08:00
2025-09-23 10:15:06 +08:00
public int SelectedIndex
2025-09-22 15:22:53 +08:00
{
2025-09-23 10:15:06 +08:00
get => _selectedIndex;
set
{
2025-09-23 10:18:28 +08:00
if (Images.Count == 0) return;
2025-09-26 16:46:22 +08:00
if (value < 0) _selectedIndex = Images.Count - 1;
else if (value >= Images.Count) _selectedIndex = 0;
else _selectedIndex = value;
for (int i = 0; i < Images.Count; i++)
Images[i].IsSelected = i == _selectedIndex;
2025-09-23 10:18:28 +08:00
2025-09-23 10:15:06 +08:00
UpdateLayoutWithAnimation();
2025-09-26 16:46:22 +08:00
// 启动进度条动画
//var current = Images[_selectedIndex];
//StartProgress(current);
}
2025-09-28 09:17:44 +08:00
2025-09-26 16:46:22 +08:00
}
2025-09-28 09:17:44 +08:00
public CoverFlowControl()
{
InitializeComponent();
DataContext = this;
Loaded += (s, e) => UpdateLayoutWithAnimation(true);
//var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(5) };
//timer.Tick += (s, e) =>
//{
// if (Images.Count == 0) return;
// var current = Images[_selectedIndex];
// if (current.Progress < 1)
// current.Progress += 0.01; // 调整速度
//};
//timer.Start();
}
2025-09-26 16:46:22 +08:00
private EventHandler _renderHandler;
private CoverFlowItem _currentItem;
2025-09-28 09:17:44 +08:00
/// <summary>
/// 针对当前选中项启动进度条动画
/// </summary>
public void StartSelectedProgress()
{
if (SelectedIndex >= 0 && SelectedIndex < Images.Count)
{
var current = Images[SelectedIndex];
if (current.Progress >= 1) return; // 已完成,直接返回,不再启动
StartProgress(current);
}
}
public void CancelSelectedProgress()
{
if (SelectedIndex >= 0 && SelectedIndex < Images.Count)
{
var current = Images[SelectedIndex];
// 停止动画/计时
StopProgress();
// 清零当前的进度值
current.Progress = 0;
}
}
public void StartProgress(CoverFlowItem item)
2025-09-26 16:46:22 +08:00
{
StopProgress();
_currentItem = item;
2025-09-28 09:17:44 +08:00
item.Progress = 0;
var container = ItemsHost.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
if (container == null) return;
var canvas = FindVisualChild<Canvas>(container);
var spark = FindVisualChild<Ellipse>(canvas);
if (spark != null)
spark.Visibility = Visibility.Visible;
2025-09-26 16:46:22 +08:00
_renderHandler = (s, e) =>
{
if (_currentItem.Progress < 1)
{
2025-09-28 09:17:44 +08:00
_currentItem.Progress += 0.00556; // 控制速度
if (spark != null)
{
var pos = GetSparkPosition(_currentItem.Progress, 150, 200);
Canvas.SetLeft(spark, pos.X - spark.Width / 2);
Canvas.SetTop(spark, pos.Y - spark.Height / 2);
}
2025-09-26 16:46:22 +08:00
}
else
{
2025-09-28 09:17:44 +08:00
ProgressCompleted?.Invoke(_currentItem);
// 停止动画
2025-09-26 16:46:22 +08:00
StopProgress();
}
};
CompositionTarget.Rendering += _renderHandler;
}
private void StopProgress()
2025-09-28 14:08:36 +08:00
2025-09-26 16:46:22 +08:00
{
if (_renderHandler != null)
{
CompositionTarget.Rendering -= _renderHandler;
_renderHandler = null;
}
2025-09-28 09:17:44 +08:00
if (_currentItem != null)
2025-09-26 16:46:22 +08:00
{
2025-09-28 09:17:44 +08:00
_currentItem.Progress = 0;
//_currentItem.IsSelected = true;
2025-09-26 16:46:22 +08:00
2025-09-28 09:17:44 +08:00
var container = ItemsHost.ItemContainerGenerator.ContainerFromItem(_currentItem) as ContentPresenter;
if (container != null)
{
var canvas = FindVisualChild<Canvas>(container);
var spark = FindVisualChild<Ellipse>(canvas);
if (spark != null)
spark.Visibility = Visibility.Collapsed;
}
2025-09-26 16:46:22 +08:00
2025-09-28 09:17:44 +08:00
_currentItem = null;
2025-09-23 10:15:06 +08:00
}
2025-09-22 17:02:19 +08:00
}
2025-09-28 09:17:44 +08:00
private Point GetSparkPosition(double progress, double width, double height)
2025-09-22 17:02:19 +08:00
{
2025-09-28 09:17:44 +08:00
double perimeter = 2 * (width + height);
double len = progress * perimeter;
if (len <= width) return new Point(len, 0); // 上边
len -= width;
if (len <= height) return new Point(width, len); // 右边
len -= height;
if (len <= width) return new Point(width - len, height); // 下边
len -= width;
return new Point(0, height - len); // 左边
2025-09-22 15:22:53 +08:00
}
2025-09-22 17:02:19 +08:00
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
2025-09-23 10:15:06 +08:00
if (sender is Border border)
2025-09-22 17:02:19 +08:00
{
2025-09-23 10:15:06 +08:00
var container = ItemsHost.ItemContainerGenerator.ContainerFromItem(border.DataContext) as ContentPresenter;
if (container != null)
2025-09-22 17:02:19 +08:00
{
2025-09-23 10:15:06 +08:00
int index = ItemsHost.ItemContainerGenerator.IndexFromContainer(container);
SelectedIndex = index;
2025-09-22 17:02:19 +08:00
}
}
}
2025-09-23 10:15:06 +08:00
private void UpdateLayoutWithAnimation(bool instant = false)
2025-09-22 17:02:19 +08:00
{
2025-09-23 10:15:06 +08:00
double centerX = ActualWidth / 2;
2025-10-11 14:40:01 +08:00
double spacing = 530;
double sideScale = 0.98;
double centerScale = 1.33;
2025-09-23 10:15:06 +08:00
for (int i = 0; i < ItemsHost.Items.Count; i++)
2025-09-22 17:02:19 +08:00
{
2025-09-23 10:15:06 +08:00
var container = ItemsHost.ItemContainerGenerator.ContainerFromIndex(i) as ContentPresenter;
if (container == null) continue;
var border = FindVisualChild<Border>(container);
if (border == null) continue;
var transformGroup = border.RenderTransform as TransformGroup;
2025-09-28 09:17:44 +08:00
var scale = transformGroup.Children[0] as ScaleTransform;
2025-09-23 10:15:06 +08:00
var translate = transformGroup.Children[1] as TranslateTransform;
double targetX;
double targetScale;
double targetOpacity;
if (i == SelectedIndex)
2025-09-22 17:02:19 +08:00
{
2025-10-11 14:40:01 +08:00
targetX = centerX - 175;
2025-09-23 10:15:06 +08:00
targetScale = centerScale;
targetOpacity = 1.0;
}
2025-09-23 10:18:28 +08:00
else if (i == SelectedIndex - 1 || (SelectedIndex == 0 && i == Images.Count - 1))
2025-09-23 10:15:06 +08:00
{
2025-10-11 14:40:01 +08:00
targetX = centerX - spacing -175;
2025-09-23 10:15:06 +08:00
targetScale = sideScale;
targetOpacity = 1.0;
}
2025-09-23 10:18:28 +08:00
else if (i == SelectedIndex + 1 || (SelectedIndex == Images.Count - 1 && i == 0))
2025-09-23 10:15:06 +08:00
{
2025-10-11 14:40:01 +08:00
targetX = centerX + spacing - 175;
2025-09-23 10:15:06 +08:00
targetScale = sideScale;
targetOpacity = 1.0;
}
else
{
2025-10-11 14:40:01 +08:00
targetX = centerX - 175;
2025-09-23 10:15:06 +08:00
targetScale = sideScale;
targetOpacity = 0.0;
2025-09-22 17:02:19 +08:00
}
2025-09-23 10:15:06 +08:00
if (instant)
{
2025-09-28 09:17:44 +08:00
translate.X = targetX;
scale.ScaleX = scale.ScaleY = targetScale;
2025-09-23 10:15:06 +08:00
border.Opacity = targetOpacity;
}
else
{
2025-09-28 09:17:44 +08:00
translate.BeginAnimation(TranslateTransform.XProperty,
2025-09-23 10:15:06 +08:00
new DoubleAnimation(targetX, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
2025-09-28 09:17:44 +08:00
scale.BeginAnimation(ScaleTransform.ScaleXProperty,
2025-09-23 10:15:06 +08:00
new DoubleAnimation(targetScale, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
scale.BeginAnimation(ScaleTransform.ScaleYProperty,
new DoubleAnimation(targetScale, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
border.BeginAnimation(Border.OpacityProperty,
new DoubleAnimation(targetOpacity, TimeSpan.FromMilliseconds(400)));
}
}
2025-09-22 17:02:19 +08:00
}
2025-09-23 10:15:06 +08:00
private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
2025-09-22 17:02:19 +08:00
{
2025-09-28 09:17:44 +08:00
if (obj == null) return null;
2025-09-23 10:15:06 +08:00
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is T target) return target;
var result = FindVisualChild<T>(child);
if (result != null) return result;
}
return null;
2025-09-22 17:02:19 +08:00
}
2025-09-28 09:17:44 +08:00
2025-09-23 16:04:47 +08:00
public void SlideLeft()
{
2025-09-28 09:17:44 +08:00
StopProgress();
2025-09-23 16:04:47 +08:00
SelectedIndex++;
}
public void SlideRight()
{
2025-09-28 09:17:44 +08:00
StopProgress();
2025-09-23 16:04:47 +08:00
SelectedIndex--;
}
2025-09-22 15:22:53 +08:00
}
2025-09-28 09:17:44 +08:00
}