火柴
This commit is contained in:
parent
99f4bfe607
commit
3f343a5931
@ -1,10 +1,10 @@
|
|||||||
<UserControl x:Class="Wpf_AiSportsMicrospace.MyUserControl.CoverFlowControl1"
|
<UserControl x:Class="Wpf_AiSportsMicrospace.MyUserControl.CoverFlowControl1"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:_3DTools="clr-namespace:_3DTools;assembly=3DTools"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl">
|
||||||
Height="300" Width="600">
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||||
<local:ProgressToRectangleGeometryConverter x:Key="ProgressToRectangleGeometryConverter"/>
|
<local:ProgressToRectangleGeometryConverter x:Key="ProgressToRectangleGeometryConverter"/>
|
||||||
@ -30,13 +30,19 @@
|
|||||||
</TransformGroup>
|
</TransformGroup>
|
||||||
</Border.RenderTransform>
|
</Border.RenderTransform>
|
||||||
|
|
||||||
<Grid>
|
<Canvas Width="150" Height="200">
|
||||||
<Image Source="{Binding ImageUri}" Stretch="UniformToFill" Width="150" Height="200"/>
|
<Image Source="{Binding ImageUri}" Width="150" Height="200" Stretch="UniformToFill"/>
|
||||||
|
|
||||||
<!-- 矩形进度条 -->
|
<!-- 矩形进度条 -->
|
||||||
<Path Stroke="Red" StrokeThickness="4" Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}"
|
<Path Stroke="Gold" StrokeThickness="4"
|
||||||
Data="{Binding Progress, Converter={StaticResource ProgressToRectangleGeometryConverter}, ConverterParameter='150,200'}"/>
|
Data="{Binding Progress, Converter={StaticResource ProgressToRectangleGeometryConverter}, ConverterParameter='150,200'}"/>
|
||||||
</Grid>
|
<!-- 火花 -->
|
||||||
|
<Ellipse x:Name="Spark" Width="8" Height="8" Fill="Yellow" Visibility="Collapsed">
|
||||||
|
<Ellipse.Effect>
|
||||||
|
<DropShadowEffect Color="White" BlurRadius="10" ShadowDepth="0"/>
|
||||||
|
</Ellipse.Effect>
|
||||||
|
</Ellipse>
|
||||||
|
</Canvas>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
|
|||||||
@ -24,7 +24,6 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// CoverFlowControl1.xaml 的交互逻辑
|
/// CoverFlowControl1.xaml 的交互逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public partial class CoverFlowControl1 : UserControl
|
public partial class CoverFlowControl1 : UserControl
|
||||||
{
|
{
|
||||||
public ObservableCollection<CoverFlowItem> Images { get; set; } = new ObservableCollection<CoverFlowItem>();
|
public ObservableCollection<CoverFlowItem> Images { get; set; } = new ObservableCollection<CoverFlowItem>();
|
||||||
@ -36,18 +35,21 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (Images.Count == 0) return;
|
if (Images.Count == 0) return;
|
||||||
|
|
||||||
if (value < 0) _selectedIndex = Images.Count - 1;
|
if (value < 0) _selectedIndex = Images.Count - 1;
|
||||||
else if (value >= Images.Count) _selectedIndex = 0;
|
else if (value >= Images.Count) _selectedIndex = 0;
|
||||||
else _selectedIndex = value;
|
else _selectedIndex = value;
|
||||||
|
|
||||||
for (int i = 0; i < Images.Count; i++)
|
for (int i = 0; i < Images.Count; i++)
|
||||||
{
|
Images[i].IsSelected = i == _selectedIndex;
|
||||||
Images[i].IsSelected = (i == _selectedIndex);
|
|
||||||
if (i == _selectedIndex) Images[i].Progress = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateLayoutWithAnimation();
|
UpdateLayoutWithAnimation();
|
||||||
|
|
||||||
|
// 启动进度条动画
|
||||||
|
var current = Images[_selectedIndex];
|
||||||
|
StartProgress(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CoverFlowControl1()
|
public CoverFlowControl1()
|
||||||
@ -56,15 +58,95 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
Loaded += (s, e) => UpdateLayoutWithAnimation(true);
|
Loaded += (s, e) => UpdateLayoutWithAnimation(true);
|
||||||
|
|
||||||
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(30) };
|
//var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(5) };
|
||||||
timer.Tick += (s, e) =>
|
//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;
|
||||||
|
|
||||||
|
private void StartProgress(CoverFlowItem item)
|
||||||
{
|
{
|
||||||
if (Images.Count == 0) return;
|
StopProgress();
|
||||||
var current = Images[_selectedIndex];
|
|
||||||
if (current.Progress < 1)
|
_currentItem = item;
|
||||||
current.Progress += 0.01; // 调整速度
|
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.002; // 控制速度
|
||||||
|
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
|
||||||
|
{
|
||||||
|
// 停止动画
|
||||||
|
StopProgress();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
timer.Start();
|
|
||||||
|
CompositionTarget.Rendering += _renderHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopProgress()
|
||||||
|
{
|
||||||
|
if (_renderHandler != null)
|
||||||
|
{
|
||||||
|
CompositionTarget.Rendering -= _renderHandler;
|
||||||
|
_renderHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentItem != null)
|
||||||
|
{
|
||||||
|
_currentItem.Progress = 0;
|
||||||
|
_currentItem.IsSelected = false;
|
||||||
|
|
||||||
|
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)
|
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
@ -82,6 +164,7 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
|
|
||||||
private void UpdateLayoutWithAnimation(bool instant = false)
|
private void UpdateLayoutWithAnimation(bool instant = false)
|
||||||
{
|
{
|
||||||
|
|
||||||
double centerX = ActualWidth / 2;
|
double centerX = ActualWidth / 2;
|
||||||
double spacing = 180;
|
double spacing = 180;
|
||||||
double sideScale = 0.8;
|
double sideScale = 0.8;
|
||||||
@ -145,6 +228,19 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
border.BeginAnimation(Border.OpacityProperty,
|
border.BeginAnimation(Border.OpacityProperty,
|
||||||
new DoubleAnimation(targetOpacity, TimeSpan.FromMilliseconds(400)));
|
new DoubleAnimation(targetOpacity, TimeSpan.FromMilliseconds(400)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var item = Images[i];
|
||||||
|
|
||||||
|
if (i == SelectedIndex)
|
||||||
|
{
|
||||||
|
item.IsSelected = true;
|
||||||
|
StartProgress(item); // 开始绘制进度条 + 火花
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.IsSelected = false;
|
||||||
|
StopProgress(); // 停止并重置进度
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,5 +259,4 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
public void SlideLeft() => SelectedIndex++;
|
public void SlideLeft() => SelectedIndex++;
|
||||||
public void SlideRight() => SelectedIndex--;
|
public void SlideRight() => SelectedIndex--;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,25 +10,37 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
{
|
{
|
||||||
public class CoverFlowItem : INotifyPropertyChanged
|
public class CoverFlowItem : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private bool _isSelected;
|
|
||||||
private double _progress; // 0~1
|
|
||||||
|
|
||||||
public Uri ImageUri { get; set; }
|
public Uri ImageUri { get; set; }
|
||||||
|
|
||||||
public bool IsSelected
|
private double _progress;
|
||||||
{
|
|
||||||
get => _isSelected;
|
|
||||||
set { _isSelected = value; OnPropertyChanged(nameof(IsSelected)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public double Progress
|
public double Progress
|
||||||
{
|
{
|
||||||
get => _progress;
|
get => _progress;
|
||||||
set { _progress = value; OnPropertyChanged(nameof(Progress)); }
|
set
|
||||||
|
{
|
||||||
|
if (_progress != value)
|
||||||
|
{
|
||||||
|
_progress = value;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isSelected;
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isSelected != value)
|
||||||
|
{
|
||||||
|
_isSelected = value;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
protected void OnPropertyChanged(string name) =>
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,57 +12,60 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
|||||||
{
|
{
|
||||||
public class ProgressToRectangleGeometryConverter : IValueConverter
|
public class ProgressToRectangleGeometryConverter : IValueConverter
|
||||||
{
|
{
|
||||||
// parameter: "width,height"
|
// parameter: "width,height" (例如 "150,200")
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
double progress = (double)value;
|
if (!(value is double progress)) return Geometry.Empty;
|
||||||
string[] sizes = parameter.ToString().Split(',');
|
if (parameter == null) return Geometry.Empty;
|
||||||
double width = double.Parse(sizes[0]);
|
|
||||||
double height = double.Parse(sizes[1]);
|
var parts = parameter.ToString().Split(',');
|
||||||
|
if (parts.Length != 2) return Geometry.Empty;
|
||||||
|
if (!double.TryParse(parts[0], out double width)) return Geometry.Empty;
|
||||||
|
if (!double.TryParse(parts[1], out double height)) return Geometry.Empty;
|
||||||
|
|
||||||
|
double perimeter = 2 * (width + height);
|
||||||
|
double len = Math.Max(0, Math.Min(perimeter, progress * perimeter));
|
||||||
|
|
||||||
var geo = new StreamGeometry();
|
var geo = new StreamGeometry();
|
||||||
using (var ctx = geo.Open())
|
using (var ctx = geo.Open())
|
||||||
{
|
{
|
||||||
ctx.BeginFigure(new Point(0, 0), false, false);
|
ctx.BeginFigure(new Point(0, 0), false, false);
|
||||||
|
|
||||||
double total = 2 * (width + height);
|
// top edge
|
||||||
double len = progress * total;
|
|
||||||
|
|
||||||
// 上边
|
|
||||||
if (len <= width)
|
if (len <= width)
|
||||||
{
|
{
|
||||||
ctx.LineTo(new Point(len, 0), true, true);
|
ctx.LineTo(new Point(len, 0), true, false);
|
||||||
return geo;
|
return geo;
|
||||||
}
|
}
|
||||||
ctx.LineTo(new Point(width, 0), true, true);
|
ctx.LineTo(new Point(width, 0), true, false);
|
||||||
len -= width;
|
len -= width;
|
||||||
|
|
||||||
// 右边
|
// right edge
|
||||||
if (len <= height)
|
if (len <= height)
|
||||||
{
|
{
|
||||||
ctx.LineTo(new Point(width, len), true, true);
|
ctx.LineTo(new Point(width, len), true, false);
|
||||||
return geo;
|
return geo;
|
||||||
}
|
}
|
||||||
ctx.LineTo(new Point(width, height), true, true);
|
ctx.LineTo(new Point(width, height), true, false);
|
||||||
len -= height;
|
len -= height;
|
||||||
|
|
||||||
// 下边
|
// bottom edge
|
||||||
if (len <= width)
|
if (len <= width)
|
||||||
{
|
{
|
||||||
ctx.LineTo(new Point(width - len, height), true, true);
|
ctx.LineTo(new Point(width - len, height), true, false);
|
||||||
return geo;
|
return geo;
|
||||||
}
|
}
|
||||||
ctx.LineTo(new Point(0, height), true, true);
|
ctx.LineTo(new Point(0, height), true, false);
|
||||||
len -= width;
|
len -= width;
|
||||||
|
|
||||||
// 左边
|
// left edge
|
||||||
if (len <= height)
|
if (len <= height)
|
||||||
{
|
{
|
||||||
ctx.LineTo(new Point(0, height - len), true, true);
|
ctx.LineTo(new Point(0, height - len), true, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.LineTo(new Point(0, 0), true, true);
|
ctx.LineTo(new Point(0, 0), true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
geo.Freeze();
|
geo.Freeze();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user