285 lines
9.9 KiB
C#
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--;
|
|
}
|
|
}
|
|
} |