2025-09-28 14:08:36 +08:00

285 lines
9.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
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.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace Wpf_AiSportsMicrospace.MyUserControl
{
/// <summary>
/// CoverFlowControl.xaml 的交互逻辑
/// </summary>
public partial class CoverFlowControl : UserControl
{// 新增事件:进度条完成
public event Action<CoverFlowItem> ProgressCompleted;
public ObservableCollection<CoverFlowItem> Images { get; set; } = new ObservableCollection<CoverFlowItem>();
private int _selectedIndex = 0;
public int SelectedIndex
{
get => _selectedIndex;
set
{
if (Images.Count == 0) return;
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;
UpdateLayoutWithAnimation();
// 启动进度条动画
//var current = Images[_selectedIndex];
//StartProgress(current);
}
}
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();
}
private EventHandler _renderHandler;
private CoverFlowItem _currentItem;
/// <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)
{
StopProgress();
_currentItem = item;
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;
_renderHandler = (s, e) =>
{
if (_currentItem.Progress < 1)
{
_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);
}
}
else
{
ProgressCompleted?.Invoke(_currentItem);
// 停止动画
StopProgress();
}
};
CompositionTarget.Rendering += _renderHandler;
}
private void StopProgress()
{
if (_renderHandler != null)
{
CompositionTarget.Rendering -= _renderHandler;
_renderHandler = null;
}
if (_currentItem != null)
{
_currentItem.Progress = 0;
//_currentItem.IsSelected = true;
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;
}
_currentItem = null;
}
}
private Point GetSparkPosition(double progress, double width, double height)
{
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); // 左边
}
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Border border)
{
var container = ItemsHost.ItemContainerGenerator.ContainerFromItem(border.DataContext) as ContentPresenter;
if (container != null)
{
int index = ItemsHost.ItemContainerGenerator.IndexFromContainer(container);
SelectedIndex = index;
}
}
}
private void UpdateLayoutWithAnimation(bool instant = false)
{
double centerX = ActualWidth / 2;
double spacing = 550;
double sideScale = 0.93;
double centerScale = 1.43;
for (int i = 0; i < ItemsHost.Items.Count; i++)
{
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;
var scale = transformGroup.Children[0] as ScaleTransform;
var translate = transformGroup.Children[1] as TranslateTransform;
double targetX;
double targetScale;
double targetOpacity;
if (i == SelectedIndex)
{
targetX = centerX - 75;
targetScale = centerScale;
targetOpacity = 1.0;
}
else if (i == SelectedIndex - 1 || (SelectedIndex == 0 && i == Images.Count - 1))
{
targetX = centerX - spacing - 75;
targetScale = sideScale;
targetOpacity = 1.0;
}
else if (i == SelectedIndex + 1 || (SelectedIndex == Images.Count - 1 && i == 0))
{
targetX = centerX + spacing - 75;
targetScale = sideScale;
targetOpacity = 1.0;
}
else
{
targetX = centerX - 75;
targetScale = sideScale;
targetOpacity = 0.0;
}
if (instant)
{
translate.X = targetX;
scale.ScaleX = scale.ScaleY = targetScale;
border.Opacity = targetOpacity;
}
else
{
translate.BeginAnimation(TranslateTransform.XProperty,
new DoubleAnimation(targetX, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
scale.BeginAnimation(ScaleTransform.ScaleXProperty,
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)));
}
}
}
private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
if (obj == null) return null;
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;
}
public void SlideLeft()
{
StopProgress();
SelectedIndex++;
}
public void SlideRight()
{
StopProgress();
SelectedIndex--;
}
}
}