Merge branch 'dev' of http://8.153.108.90:3000/YD/Wpf_AiSportsMicrospace into dev
# Conflicts: # Wpf_AiSportsMicrospace/Views/JumpRope/MusicJumpRope.xaml
This commit is contained in:
commit
3bd2e0c5b4
@ -2,6 +2,8 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views"
|
||||
xmlns:conv="clr-namespace:Wpf_AiSportsMicrospace.Converter"
|
||||
|
||||
StartupUri="Views/Main.xaml">
|
||||
|
||||
<!--StartupUri="Views/JumpRope/GroupJumpRope.xaml">-->
|
||||
@ -10,6 +12,8 @@
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="./Resoures/Fonts/myFontFamily"/>
|
||||
</Style>
|
||||
<conv:DotColorConverter x:Key="DotColorConverter"/>
|
||||
<!--<conv:SpacingConverter x:Key="SpacingConverter"/>-->
|
||||
</Application.Resources>
|
||||
|
||||
</Application>
|
||||
|
||||
@ -155,7 +155,7 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
}
|
||||
|
||||
var duration = DateTime.Now - _raiseStartTime.Value;
|
||||
if (duration.TotalSeconds >= 3)
|
||||
if (duration.TotalSeconds >= 1)
|
||||
{
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
|
||||
24
Wpf_AiSportsMicrospace/Converter/DotColorConverter.cs
Normal file
24
Wpf_AiSportsMicrospace/Converter/DotColorConverter.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using Wpf_AiSportsMicrospace.MyUserControl;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.Converter
|
||||
{
|
||||
public class DotColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool b && b)
|
||||
return Brushes.Red;
|
||||
return Brushes.Green;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@ -50,6 +50,8 @@ namespace Dto
|
||||
}
|
||||
};
|
||||
|
||||
public Dictionary<int, List<double>> MusicBeatsDic;
|
||||
|
||||
public MusicJumpRopeContext()
|
||||
{
|
||||
CirclePositions = new List<(double XNorm, double YNorm)>
|
||||
@ -58,6 +60,8 @@ namespace Dto
|
||||
(0.50, 0.88),
|
||||
};
|
||||
|
||||
MusicBeatsDic = MusicBeats["1"].GroupBy(b => (int)Math.Ceiling(b) - 1).ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
UserList = new List<SportUserItem>();
|
||||
UserNumberList = new List<string>();
|
||||
Sports = new List<SportBase>();
|
||||
|
||||
29
Wpf_AiSportsMicrospace/MyUserControl/BeatScrollDots.xaml
Normal file
29
Wpf_AiSportsMicrospace/MyUserControl/BeatScrollDots.xaml
Normal file
@ -0,0 +1,29 @@
|
||||
<UserControl x:Class="Wpf_AiSportsMicrospace.MyUserControl.BeatScrollDots"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Converter"
|
||||
mc:Ignorable="d"
|
||||
Height="100" Width="400">
|
||||
<UserControl.Resources>
|
||||
<local:DotColorConverter x:Key="DotColorConverter"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<ScrollViewer x:Name="scrollViewer" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled" Opacity="0.5">
|
||||
<ItemsControl ItemsSource="{Binding Dots, ElementName=root}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Ellipse Width="20" Height="20"
|
||||
Margin="{Binding ElementName=root, Path=DotSpacing}"
|
||||
Fill="{Binding IsSelected, Converter={StaticResource DotColorConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
136
Wpf_AiSportsMicrospace/MyUserControl/BeatScrollDots.xaml.cs
Normal file
136
Wpf_AiSportsMicrospace/MyUserControl/BeatScrollDots.xaml.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using Emgu.CV.Flann;
|
||||
using HandyControl.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
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;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
{
|
||||
public partial class BeatScrollDots : UserControl
|
||||
{
|
||||
public ObservableCollection<DotItem> Dots { get; set; } = new ObservableCollection<DotItem>();
|
||||
|
||||
public BeatScrollDots()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
public int DotCount
|
||||
{
|
||||
get => (int)GetValue(DotCountProperty);
|
||||
set => SetValue(DotCountProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DotCountProperty =
|
||||
DependencyProperty.Register(nameof(DotCount), typeof(int), typeof(BeatScrollDots),
|
||||
new PropertyMetadata(0, OnDotCountChanged));
|
||||
|
||||
private static void OnDotCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is BeatScrollDots ctrl)
|
||||
ctrl.GenerateDots();
|
||||
}
|
||||
|
||||
private void GenerateDots()
|
||||
{
|
||||
Dots.Clear();
|
||||
for (int i = 0; i < DotCount; i++)
|
||||
Dots.Add(new DotItem { IsSelected = false });
|
||||
}
|
||||
|
||||
public void SetSelected(int index, bool selected)
|
||||
{
|
||||
if (index >= 0 && index < Dots.Count)
|
||||
Dots[index].IsSelected = selected;
|
||||
}
|
||||
|
||||
public Thickness DotSpacing
|
||||
{
|
||||
get => (Thickness)GetValue(DotSpacingProperty);
|
||||
set => SetValue(DotSpacingProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DotSpacingProperty =
|
||||
DependencyProperty.Register(nameof(DotSpacing), typeof(Thickness), typeof(BeatScrollDots), new PropertyMetadata(new Thickness(5, 0, 5, 0)));
|
||||
|
||||
public void ScrollToDotCenter(int index, bool mirror = false)
|
||||
{
|
||||
if (scrollViewer == null || Dots.Count == 0) return;
|
||||
|
||||
double step = 20 + DotSpacing.Left + DotSpacing.Right;
|
||||
double totalWidth = Dots.Count * step;
|
||||
double dotCenter = index * step + step / 2;
|
||||
|
||||
double targetOffset = !mirror
|
||||
? dotCenter - scrollViewer.ActualWidth / 2
|
||||
: totalWidth - dotCenter - scrollViewer.ActualWidth / 2;
|
||||
|
||||
targetOffset = System.Math.Max(0, System.Math.Min(targetOffset, scrollViewer.ScrollableWidth));
|
||||
scrollViewer.ScrollToHorizontalOffset(targetOffset);
|
||||
}
|
||||
|
||||
public void UpdateSpacingForCount(int count)
|
||||
{
|
||||
if (count <= 0) count = 1;
|
||||
double spacing = (ActualWidth / count) - 20;
|
||||
if (spacing < 2) spacing = 2;
|
||||
DotSpacing = new Thickness(spacing / 2, 0, spacing / 2, 0);
|
||||
}
|
||||
public Dictionary<int, List<double>> MusicBeatsDic
|
||||
{
|
||||
get => (Dictionary<int, List<double>>)GetValue(MusicBeatsDicProperty);
|
||||
set => SetValue(MusicBeatsDicProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MusicBeatsDicProperty =
|
||||
DependencyProperty.Register(nameof(MusicBeatsDic), typeof(Dictionary<int, List<double>>), typeof(BeatScrollDots),
|
||||
new PropertyMetadata(null, OnMusicBeatsDicChanged));
|
||||
|
||||
private static void OnMusicBeatsDicChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is BeatScrollDots ctrl)
|
||||
{
|
||||
// 可选:根据第一个 key 初始化 DotCount
|
||||
if (ctrl.MusicBeatsDic != null && ctrl.MusicBeatsDic.Any())
|
||||
{
|
||||
int firstKey = ctrl.MusicBeatsDic.Keys.Min();
|
||||
ctrl.DotCount = ctrl.MusicBeatsDic[firstKey].Count;
|
||||
ctrl.UpdateSpacingForCount(ctrl.DotCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DotItem : INotifyPropertyChanged
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -38,9 +38,9 @@
|
||||
</Grid>
|
||||
|
||||
<!-- 矩形进度条 Stroke="{Binding ProgressColor}" -->
|
||||
|
||||
<Path StrokeThickness="13" Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}" StrokeStartLineCap="Round" StrokeEndLineCap="Round"
|
||||
Data="{Binding Progress, Converter={StaticResource ProgressToRectangleGeometryConverter}, ConverterParameter='372,456,22,13'}">
|
||||
|
||||
<Path StrokeThickness="13" Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}" StrokeStartLineCap="Flat" StrokeEndLineCap="Flat"
|
||||
Data="{Binding Progress, Converter={StaticResource ProgressToRectangleGeometryConverter}, ConverterParameter='372,456,22,13'}">
|
||||
<Path.Stroke>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
|
||||
<GradientStop Offset="0" Color="{Binding ProgressColor1}"/>
|
||||
|
||||
@ -116,7 +116,7 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
{
|
||||
if (_currentItem.Progress < 1)
|
||||
{
|
||||
_currentItem.Progress += 0.00556; // 控制速度
|
||||
_currentItem.Progress += 0.01; // 控制速度
|
||||
if (spark != null)
|
||||
{
|
||||
var pos = GetSparkPosition(_currentItem.Progress, 150, 200);
|
||||
|
||||
25
Wpf_AiSportsMicrospace/MyUserControl/WxProgressBar.xaml
Normal file
25
Wpf_AiSportsMicrospace/MyUserControl/WxProgressBar.xaml
Normal file
@ -0,0 +1,25 @@
|
||||
<UserControl x:Class="Wpf_AiSportsMicrospace.MyUserControl.WxProgressBar"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
x:Name="root"
|
||||
Height="30" Width="600">
|
||||
<Canvas>
|
||||
<Border x:Name="LeftBar" Background="#95dfce" HorizontalAlignment="Left" Height="30"/>
|
||||
<Border x:Name="RightBar" Background="#e0585b" HorizontalAlignment="Right" Height="30"/>
|
||||
|
||||
<!-- 中间分割线图片 -->
|
||||
<Image x:Name="MiddleLine"
|
||||
Source="/Resources/Img/Album/vs.png"
|
||||
Width="30" Height="50" Panel.ZIndex="100"/>
|
||||
|
||||
<!-- 中间百分比文本 -->
|
||||
<TextBlock x:Name="ProgressTextBlock"
|
||||
Foreground="White"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Canvas>
|
||||
</UserControl>
|
||||
95
Wpf_AiSportsMicrospace/MyUserControl/WxProgressBar.xaml.cs
Normal file
95
Wpf_AiSportsMicrospace/MyUserControl/WxProgressBar.xaml.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
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.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// WxProgressBar.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class WxProgressBar : UserControl
|
||||
{
|
||||
public WxProgressBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.SizeChanged += WxProgressBar_SizeChanged;
|
||||
}
|
||||
|
||||
private void WxProgressBar_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateVisual();
|
||||
}
|
||||
|
||||
#region 左右进度属性
|
||||
private double _leftProgress;
|
||||
public double LeftProgress
|
||||
{
|
||||
get => _leftProgress;
|
||||
set
|
||||
{
|
||||
_leftProgress = Clamp(value, 0, 1);
|
||||
_rightProgress = 1 - _leftProgress;
|
||||
UpdateVisual();
|
||||
}
|
||||
}
|
||||
|
||||
private double _rightProgress;
|
||||
public double RightProgress
|
||||
{
|
||||
get => _rightProgress;
|
||||
set
|
||||
{
|
||||
_rightProgress = Clamp(value, 0, 1);
|
||||
_leftProgress = 1 - _rightProgress;
|
||||
UpdateVisual();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void UpdateVisual()
|
||||
{
|
||||
double totalWidth = ActualWidth;
|
||||
double totalHeight = ActualHeight;
|
||||
if (totalWidth <= 0 || totalHeight <= 0) return;
|
||||
|
||||
double barHeight = 30; // 进度条高度
|
||||
double middleLineHeight = MiddleLine.Height;
|
||||
|
||||
// 左侧红条
|
||||
LeftBar.Width = totalWidth * _leftProgress;
|
||||
LeftBar.Height = barHeight;
|
||||
Canvas.SetLeft(LeftBar, 0);
|
||||
Canvas.SetTop(LeftBar, (totalHeight - barHeight) / 2);
|
||||
|
||||
// 右侧绿条
|
||||
RightBar.Width = totalWidth * _rightProgress;
|
||||
RightBar.Height = barHeight;
|
||||
Canvas.SetLeft(RightBar, totalWidth - RightBar.Width);
|
||||
Canvas.SetTop(RightBar, (totalHeight - barHeight) / 2);
|
||||
|
||||
// 分割线居中在红绿交界点
|
||||
double middleX = LeftBar.Width - MiddleLine.Width / 2;
|
||||
if (middleX < 0) middleX = 0;
|
||||
if (middleX > totalWidth - MiddleLine.Width) middleX = totalWidth - MiddleLine.Width;
|
||||
|
||||
Canvas.SetLeft(MiddleLine, middleX);
|
||||
Canvas.SetTop(MiddleLine, (totalHeight - middleLineHeight) / 2);
|
||||
|
||||
// 百分比文本居中显示
|
||||
ProgressTextBlock.Text = $"{Math.Round(_leftProgress * 100)}% : {Math.Round(_rightProgress * 100)}%";
|
||||
Canvas.SetLeft(ProgressTextBlock, (totalWidth - ProgressTextBlock.ActualWidth) / 2);
|
||||
Canvas.SetTop(ProgressTextBlock, (totalHeight - ProgressTextBlock.ActualHeight) / 2);
|
||||
}
|
||||
|
||||
private double Clamp(double value, double min, double max) => value < min ? min : value > max ? max : value;
|
||||
}
|
||||
}
|
||||
BIN
Wpf_AiSportsMicrospace/Resources/Img/Album/vs.png
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Img/Album/vs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@ -216,7 +216,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
||||
}
|
||||
}
|
||||
|
||||
private int _currentCountdown = 3;
|
||||
private double _currentCountdown = 3600;
|
||||
private DateTime _lastUpdateTime = DateTime.Now;
|
||||
private void StartCountdown(int start = 3)
|
||||
{
|
||||
@ -233,10 +233,10 @@ namespace Wpf_AiSportsMicrospace.Views
|
||||
|
||||
private void UpdateCountdown()
|
||||
{
|
||||
if ((DateTime.Now - _lastUpdateTime).TotalSeconds >= 1)
|
||||
if ((DateTime.Now - _lastUpdateTime).TotalMilliseconds >= 600)
|
||||
{
|
||||
_lastUpdateTime = DateTime.Now;
|
||||
_currentCountdown--;
|
||||
_currentCountdown -= 600;
|
||||
|
||||
if (_currentCountdown > 0)
|
||||
{
|
||||
|
||||
@ -35,29 +35,9 @@
|
||||
TextAlignment="Center"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid x:Name="BottomBeatPanel" VerticalAlignment="Bottom" Height="200" Panel.ZIndex="100">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="600"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左边滚动条 -->
|
||||
<ScrollViewer x:Name="BeatScrollLeft" Width="660" Height="80"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
Grid.Column="0">
|
||||
<Canvas x:Name="beatCanvasLeft" Height="100"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- 右边滚动条 -->
|
||||
<ScrollViewer x:Name="BeatScrollRight" Width="660" Height="80"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
Grid.Column="2">
|
||||
<Canvas x:Name="beatCanvasRight" Height="100"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" >
|
||||
<local:WxProgressBar x:Name="PkBar"/>
|
||||
</StackPanel>
|
||||
|
||||
<!--<Grid Width="3" Height="140" Margin="00,600,580,0" Background="#fedd40" ClipToBounds="False"></Grid>
|
||||
<Grid Width="580" Height="87" Margin="00,600,0,0" Background="#25ffffff" ClipToBounds="False">
|
||||
|
||||
@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
@ -37,40 +38,13 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
private readonly object _updateLock = new object();
|
||||
private readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
|
||||
private MusicJumpRopeContext _musicJumpRopeContext;
|
||||
|
||||
private GameState _currentGameState = GameState.NotStarted;
|
||||
|
||||
List<RankItem> RankingItemList = new();
|
||||
private List<double> _musicBeats = new List<double>()
|
||||
{
|
||||
0.545,1.09,1.635,2.18,2.725,3.27,3.815,4.36,4.905,5.45,
|
||||
5.995,6.54,7.085,7.63,8.175,8.72,9.265,9.81,10.355,10.9,
|
||||
11.445,11.99,12.535,13.08,13.625,14.17,14.715,15.26,15.805,16.35,
|
||||
16.895,17.44,17.985,18.53,19.075,19.62,20.165,20.71,21.255,21.8,
|
||||
22.345,22.89,23.435,23.98,24.525,25.07,25.615,26.16,26.705,27.25,
|
||||
27.795,28.34,28.885,29.43,29.975,30.52,31.065,31.61,32.155,32.7,
|
||||
33.245,33.79,34.335,34.88,35.425,35.97,36.515,37.06,37.605,38.15,
|
||||
38.695,39.24,39.785,40.33,40.875,41.42,41.965,42.51,43.055,43.6,
|
||||
44.145,44.69,45.235,45.78,46.325,46.87,47.415,47.96,48.505,49.05,
|
||||
49.595,50.14,50.685,51.23,51.775,52.32,52.865,53.41,53.955,54.5,
|
||||
55.045,55.59,56.135,56.68,57.225,57.77,58.315,58.86,59.405,59.95,
|
||||
60.495,61.04,61.585,62.13,62.675,63.22,63.765,64.31,64.855,65.4,
|
||||
65.945,66.49,67.035,67.58,68.125,68.67,69.215,69.76,70.305,70.85,
|
||||
71.395,71.94,72.485,73.03,73.575,74.12,74.665,75.21,75.755,76.3,
|
||||
76.845,77.39,77.935,78.48,79.025,79.57,80.115,80.66,81.205,81.75,
|
||||
82.295,82.84,83.385,83.93,84.475,85.02,85.565,86.11,86.655,87.2,
|
||||
87.745,88.29,88.835,89.38,89.925,90.47,91.015,91.56,92.105,92.65,
|
||||
93.195,93.74,94.285,94.83,95.375,95.92,96.465,97.01,97.555,98.1,
|
||||
98.645,99.19,99.735,100.28,100.825,101.37,101.915,102.46,103.005,103.55,
|
||||
104.095,104.64,105.185,105.73,106.275,106.82,107.365,107.91,108.455
|
||||
|
||||
};
|
||||
|
||||
private List<TextBlock> _musicBeatTextBlock = new List<TextBlock>();
|
||||
|
||||
// 容忍时间(节拍误差)
|
||||
public double _beatTolerance = 0.15; // ±150ms
|
||||
|
||||
private int _totalDots = 0;
|
||||
// 滚动显示的节拍点集合
|
||||
public ObservableCollection<BeatItem> BeatDisplayLeft { get; set; } = new();
|
||||
public ObservableCollection<BeatItem> BeatDisplayRight { get; set; } = new();
|
||||
@ -81,9 +55,6 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
Loaded += UserControl_Loaded;
|
||||
Unloaded += UserControl_Unloaded;
|
||||
_musicJumpRopeContext = new MusicJumpRopeContext();
|
||||
|
||||
// 初始化节拍点数据
|
||||
InitBeatDots();
|
||||
}
|
||||
|
||||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
@ -91,6 +62,18 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
DrawCirclesWithText();
|
||||
// 播放音乐
|
||||
PlayMusic("raisehand.mp3");
|
||||
|
||||
Random rnd = new Random();
|
||||
|
||||
// 模拟 20 次随机更新
|
||||
for (int i = 0; i < 200; i++)
|
||||
{
|
||||
// 随机左右进度变化,保证总和=1
|
||||
double left = rnd.NextDouble(); // 0~1
|
||||
PkBar.LeftProgress = left; // RightProgress 自动 1-left
|
||||
|
||||
await Task.Delay(1000); // 200ms 间隔,不卡UI线程
|
||||
}
|
||||
}
|
||||
|
||||
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
@ -276,8 +259,6 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
// 播放背景音乐(循环)
|
||||
Utils.PlayBackgroundMusic("1.MP3", true);
|
||||
|
||||
//StartBeatScrollTimer();
|
||||
|
||||
for (int i = seconds; i >= 0; i--)
|
||||
{
|
||||
countdownText.Text = i.ToString();
|
||||
@ -491,30 +472,29 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
userItem.ImageState = "2";
|
||||
|
||||
var currentTime = Utils.GetMusicCurrentTime();
|
||||
var beats = _musicJumpRopeContext.MusicBeats["1"];
|
||||
int currentSecond = (int)Math.Floor(currentTime);
|
||||
|
||||
for (int j = 0; j < beats.Count; j++)
|
||||
// 判断是否命中节拍
|
||||
var beats = _musicJumpRopeContext.MusicBeats["1"];
|
||||
bool hit = beats.Any(b => Math.Abs(b - currentTime) <= _beatTolerance);
|
||||
if (hit)
|
||||
{
|
||||
int indexCopy2 = j; // 复制一份当前循环索引
|
||||
if (Math.Abs(beats[indexCopy2] - currentTime) <= _beatTolerance)
|
||||
{
|
||||
_musicJumpRopeContext.UserBeatSyncList[indexCopy]++;
|
||||
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
if (indexCopy == 0)
|
||||
{
|
||||
((Ellipse)beatCanvasLeft.Children[indexCopy2]).Fill = Brushes.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
((Ellipse)beatCanvasRight.Children[indexCopy2]).Fill = Brushes.Red;
|
||||
}
|
||||
_musicBeatTextBlock[indexCopy].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[indexCopy]}";
|
||||
});
|
||||
}
|
||||
_musicJumpRopeContext.UserBeatSyncList[indexCopy]++;
|
||||
_musicBeatTextBlock[indexCopy].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[indexCopy]}";
|
||||
|
||||
//if (indexCopy == 0)
|
||||
// LeftBeats.SetSelected(currentSecond, true);
|
||||
//else
|
||||
// RightBeats.SetSelected(currentSecond, true);
|
||||
|
||||
//Application.Current.Dispatcher.BeginInvoke(() =>
|
||||
//{
|
||||
// if (indexCopy == 0)
|
||||
// LeftBeats.SetSelected(currentSecond, true);
|
||||
// else
|
||||
// RightBeats.SetSelected(currentSecond, true);
|
||||
//});
|
||||
}
|
||||
// 滚动条跳跃到当前点附近
|
||||
UpdateBeatScrollSudden(currentTime);
|
||||
};
|
||||
|
||||
sport.Start();
|
||||
@ -522,30 +502,6 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBeatScrollSudden(double currentTime)
|
||||
{
|
||||
int maxVisible = 5; // 最多显示 5 个点
|
||||
var beats = _musicJumpRopeContext.MusicBeats["1"];
|
||||
|
||||
// 找到最近的点索引
|
||||
int currentIndex = beats.FindIndex(bt => bt >= currentTime);
|
||||
if (currentIndex == -1) currentIndex = beats.Count - 1;
|
||||
|
||||
// 取前后最多 5 个点
|
||||
int startIndex = Math.Max(0, currentIndex - maxVisible / 2);
|
||||
int endIndex = Math.Min(beats.Count - 1, startIndex + maxVisible - 1);
|
||||
|
||||
// 左侧 ScrollViewer
|
||||
double leftCenterX = BeatDisplayLeft[currentIndex].X - BeatScrollLeft.ViewportWidth / 2;
|
||||
leftCenterX = Math.Max(0, Math.Min(leftCenterX, beatCanvasLeft.Width - BeatScrollLeft.ViewportWidth));
|
||||
BeatScrollLeft.ScrollToHorizontalOffset(leftCenterX);
|
||||
|
||||
// 右侧 ScrollViewer
|
||||
double rightCenterX = BeatDisplayRight[currentIndex].X - BeatScrollRight.ViewportWidth / 2;
|
||||
rightCenterX = Math.Max(0, Math.Min(rightCenterX, beatCanvasRight.Width - BeatScrollRight.ViewportWidth));
|
||||
BeatScrollRight.ScrollToHorizontalOffset(rightCenterX);
|
||||
}
|
||||
|
||||
private void UpdateCircleCounts(List<Human> humans)
|
||||
{
|
||||
double radiusNormX = 0.07;
|
||||
@ -672,84 +628,5 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
|
||||
return userItem;
|
||||
}
|
||||
private List<TextBlock> leftDots = new();
|
||||
private List<TextBlock> rightDots = new();
|
||||
public double BeatPanelWidth { get; set; }
|
||||
private void InitBeatDots()
|
||||
{
|
||||
BeatDisplayLeft.Clear();
|
||||
BeatDisplayRight.Clear();
|
||||
beatCanvasLeft.Children.Clear();
|
||||
beatCanvasRight.Children.Clear();
|
||||
|
||||
var beats = _musicJumpRopeContext.MusicBeats["1"];
|
||||
double lastX = 0;
|
||||
double scale = 300; // 间隔放大系数,可调
|
||||
|
||||
for (int i = 0; i < beats.Count; i++)
|
||||
{
|
||||
double interval = i == 0 ? beats[0] : beats[i] - beats[i - 1];
|
||||
lastX += interval * scale;
|
||||
}
|
||||
double totalWidth = lastX + 50;
|
||||
|
||||
// 左侧点
|
||||
lastX = 0;
|
||||
for (int i = 0; i < beats.Count; i++)
|
||||
{
|
||||
double interval = i == 0 ? beats[0] : beats[i] - beats[i - 1];
|
||||
double xPos = lastX + interval * scale;
|
||||
lastX = xPos;
|
||||
|
||||
var leftItem = new BeatItem { X = xPos, Color = Brushes.Black };
|
||||
BeatDisplayLeft.Add(leftItem);
|
||||
|
||||
var leftEllipse = new Ellipse { Width = 8, Height = 8, Fill = leftItem.Color };
|
||||
Canvas.SetLeft(leftEllipse, leftItem.X);
|
||||
Canvas.SetTop(leftEllipse, 40);
|
||||
beatCanvasLeft.Children.Add(leftEllipse);
|
||||
}
|
||||
beatCanvasLeft.Width = totalWidth;
|
||||
|
||||
// 右侧点(倒序排列,但间隔顺序保持一致)
|
||||
lastX = 0;
|
||||
for (int i = 0; i < beats.Count; i++)
|
||||
{
|
||||
double interval = i == 0 ? beats[0] : beats[i] - beats[i - 1];
|
||||
double xPos = lastX + interval * scale;
|
||||
lastX = xPos;
|
||||
|
||||
// 倒序显示
|
||||
double rightX = totalWidth - xPos;
|
||||
var rightItem = new BeatItem { X = rightX, Color = Brushes.Black };
|
||||
BeatDisplayRight.Add(rightItem);
|
||||
|
||||
var rightEllipse = new Ellipse { Width = 8, Height = 8, Fill = rightItem.Color };
|
||||
Canvas.SetLeft(rightEllipse, rightItem.X);
|
||||
Canvas.SetTop(rightEllipse, 40);
|
||||
beatCanvasRight.Children.Add(rightEllipse);
|
||||
}
|
||||
beatCanvasRight.Width = totalWidth;
|
||||
}
|
||||
private DispatcherTimer _beatScrollTimer;
|
||||
private double totalTime = 108.455; // 音乐总时长
|
||||
private void StartBeatScrollTimer()
|
||||
{
|
||||
if (_beatScrollTimer != null) _beatScrollTimer.Stop();
|
||||
|
||||
_beatScrollTimer = new DispatcherTimer();
|
||||
_beatScrollTimer.Interval = TimeSpan.FromMilliseconds(1000);
|
||||
_beatScrollTimer.Tick += (s, e) =>
|
||||
{
|
||||
double currentTime = Utils.GetMusicCurrentTime();
|
||||
|
||||
double progress = currentTime / totalTime;
|
||||
double maxOffsetLeft = beatCanvasLeft.Width - BeatScrollLeft.ViewportWidth;
|
||||
double maxOffsetRight = beatCanvasRight.Width - BeatScrollRight.ViewportWidth;
|
||||
BeatScrollLeft.ScrollToHorizontalOffset(progress * (beatCanvasLeft.Width - BeatScrollLeft.ViewportWidth));
|
||||
BeatScrollRight.ScrollToHorizontalOffset((1 - progress) * (beatCanvasRight.Width - BeatScrollRight.ViewportWidth));
|
||||
};
|
||||
_beatScrollTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<None Remove="Resources\Img\Album\home_play.png" />
|
||||
<None Remove="Resources\Img\Album\home_test.png" />
|
||||
<None Remove="Resources\Img\Album\title.png" />
|
||||
<None Remove="Resources\Img\Album\vs.png" />
|
||||
<None Remove="Resources\Img\Badge\1.jpg" />
|
||||
<None Remove="Resources\Img\Badge\2.jpg" />
|
||||
<None Remove="Resources\Img\Badge\3.jpg" />
|
||||
@ -208,6 +209,9 @@
|
||||
<Resource Include="Resources\Img\Album\title.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Img\Album\vs.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Img\Badge\1.jpg">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user