Merge branch 'dev' of http://8.153.108.90:3000/YD/Wpf_AiSportsMicrospace into dev
This commit is contained in:
commit
eef77b8995
@ -69,6 +69,7 @@ namespace Dto
|
|||||||
public int Rank { get; set; }
|
public int Rank { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int Number { get; set; }
|
public int Number { get; set; }
|
||||||
|
public double Score { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Shapes;
|
||||||
using Wpf_AiSportsMicrospace.MyUserControl;
|
using Wpf_AiSportsMicrospace.MyUserControl;
|
||||||
using Yztob.AiSports.Postures.Sports;
|
using Yztob.AiSports.Postures.Sports;
|
||||||
|
|
||||||
@ -14,7 +16,7 @@ namespace Dto
|
|||||||
public List<(double XNorm, double YNorm)> CirclePositions { get; private set; }
|
public List<(double XNorm, double YNorm)> CirclePositions { get; private set; }
|
||||||
public List<SportUserItem> UserList { get; private set; }
|
public List<SportUserItem> UserList { get; private set; }
|
||||||
public List<string> UserNumberList { get; private set; }
|
public List<string> UserNumberList { get; private set; }
|
||||||
public List<string> UserScoreList { get; private set; }
|
public List<double> UserScoreList { get; private set; }
|
||||||
public List<SportBase> Sports { get; private set; }
|
public List<SportBase> Sports { get; private set; }
|
||||||
public List<string> UseNameList { get; private set; } = new() { "一号位", "二号位" };
|
public List<string> UseNameList { get; private set; } = new() { "一号位", "二号位" };
|
||||||
public List<int> UserBeatSyncList = new List<int>();
|
public List<int> UserBeatSyncList = new List<int>();
|
||||||
@ -53,13 +55,12 @@ namespace Dto
|
|||||||
CirclePositions = new List<(double XNorm, double YNorm)>
|
CirclePositions = new List<(double XNorm, double YNorm)>
|
||||||
{
|
{
|
||||||
(0.21, 0.88),
|
(0.21, 0.88),
|
||||||
(0.78, 0.88),
|
(0.50, 0.88),
|
||||||
};
|
};
|
||||||
|
|
||||||
UserList = new List<SportUserItem>();
|
UserList = new List<SportUserItem>();
|
||||||
UserNumberList = new List<string>();
|
UserNumberList = new List<string>();
|
||||||
Sports = new List<SportBase>();
|
Sports = new List<SportBase>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新排行榜方法
|
// 更新排行榜方法
|
||||||
@ -83,5 +84,47 @@ namespace Dto
|
|||||||
|
|
||||||
return rankList;
|
return rankList;
|
||||||
}
|
}
|
||||||
|
public List<RankItem> UpdateScoreRankList()
|
||||||
|
{
|
||||||
|
int totalBeats = MusicBeats.Count; // 总节拍数
|
||||||
|
const double maxScore = 100.0; // 满分 100
|
||||||
|
|
||||||
|
var rankList = UserBeatSyncList
|
||||||
|
.Select((beatCount, index) => new
|
||||||
|
{
|
||||||
|
Index = index,
|
||||||
|
Name = UseNameList[index],
|
||||||
|
BeatCount = beatCount,
|
||||||
|
// 按照 100 分制计算得分
|
||||||
|
Score = Math.Round((beatCount / (double)totalBeats) * maxScore, 1)
|
||||||
|
})
|
||||||
|
.OrderByDescending(x => x.Score)
|
||||||
|
.Select((x, rank) => new RankItem
|
||||||
|
{
|
||||||
|
Rank = rank + 1,
|
||||||
|
Name = x.Name,
|
||||||
|
Number = x.BeatCount, // 卡点个数
|
||||||
|
Score = x.Score // 总分(百分制)
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 同步更新 UserScoreList
|
||||||
|
UserScoreList = rankList.Select(x => x.Score).ToList();
|
||||||
|
return rankList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class UserBeatBar
|
||||||
|
{
|
||||||
|
public Canvas Canvas { get; set; } // 用于显示节拍条的 Canvas
|
||||||
|
public List<Ellipse> BeatPoints { get; set; } // 每个节拍点
|
||||||
|
public List<double> BeatTimes { get; set; }
|
||||||
|
public bool IsLeftToRight { get; set; } // 滑动方向
|
||||||
|
public double BarWidth { get; set; } // 横杠宽度
|
||||||
|
public double BarHeight { get; set; } // 横杠高度
|
||||||
|
|
||||||
|
public UserBeatBar()
|
||||||
|
{
|
||||||
|
BeatPoints = new List<Ellipse>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
Wpf_AiSportsMicrospace/Views/JumpRope/BeatDot.cs
Normal file
25
Wpf_AiSportsMicrospace/Views/JumpRope/BeatDot.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace Views.JumpRope
|
||||||
|
{
|
||||||
|
public class BeatItem : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private Brush _color = Brushes.Black;
|
||||||
|
public double X { get; set; } // 横坐标
|
||||||
|
public Brush Color
|
||||||
|
{
|
||||||
|
get => _color;
|
||||||
|
set { _color = value; OnPropertyChanged(nameof(Color)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
26
Wpf_AiSportsMicrospace/Views/JumpRope/BeatMarginConverter.cs
Normal file
26
Wpf_AiSportsMicrospace/Views/JumpRope/BeatMarginConverter.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace Views.JumpRope
|
||||||
|
{
|
||||||
|
public class BeatMarginConverter : IMultiValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (values[0] is double leftMargin)
|
||||||
|
{
|
||||||
|
return new Thickness(leftMargin, 0, 0, 0);
|
||||||
|
}
|
||||||
|
return new Thickness(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,9 @@
|
|||||||
Width="615"
|
Width="615"
|
||||||
Margin="0,0,0,0"
|
Margin="0,0,0,0"
|
||||||
/>
|
/>
|
||||||
<Grid Height="1080" Width="1920" x:Name="userBox"/>
|
<Grid Height="1080" Width="1920" x:Name="userBox">
|
||||||
|
<Canvas x:Name="beatCanvas" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Grid Width="220" Height="130" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="00,40,60,0" Visibility="Hidden" x:Name="countdownGrid">
|
<Grid Width="220" Height="130" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="00,40,60,0" Visibility="Hidden" x:Name="countdownGrid">
|
||||||
<Border Background="#005fff" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="30" />
|
<Border Background="#005fff" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="30" />
|
||||||
@ -33,5 +35,28 @@
|
|||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</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>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
using Enum;
|
using Enum;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@ -15,6 +17,7 @@ using System.Windows.Media.Imaging;
|
|||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using Views.JumpRope;
|
||||||
using Wpf_AiSportsMicrospace.Common;
|
using Wpf_AiSportsMicrospace.Common;
|
||||||
using Wpf_AiSportsMicrospace.Enum;
|
using Wpf_AiSportsMicrospace.Enum;
|
||||||
using Wpf_AiSportsMicrospace.MyUserControl;
|
using Wpf_AiSportsMicrospace.MyUserControl;
|
||||||
@ -64,15 +67,23 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
};
|
};
|
||||||
|
|
||||||
private List<TextBlock> _musicBeatTextBlock = new List<TextBlock>();
|
private List<TextBlock> _musicBeatTextBlock = new List<TextBlock>();
|
||||||
|
|
||||||
// 容忍时间(节拍误差)
|
// 容忍时间(节拍误差)
|
||||||
public double _beatTolerance = 0.15; // ±150ms
|
public double _beatTolerance = 0.15; // ±150ms
|
||||||
|
|
||||||
|
// 滚动显示的节拍点集合
|
||||||
|
public ObservableCollection<BeatItem> BeatDisplayLeft { get; set; } = new();
|
||||||
|
public ObservableCollection<BeatItem> BeatDisplayRight { get; set; } = new();
|
||||||
|
|
||||||
public MusicJumpRope()
|
public MusicJumpRope()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Loaded += UserControl_Loaded;
|
Loaded += UserControl_Loaded;
|
||||||
Unloaded += UserControl_Unloaded;
|
Unloaded += UserControl_Unloaded;
|
||||||
_musicJumpRopeContext = new MusicJumpRopeContext();
|
_musicJumpRopeContext = new MusicJumpRopeContext();
|
||||||
|
|
||||||
|
// 初始化节拍点数据
|
||||||
|
InitBeatDots();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||||
@ -252,11 +263,8 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
{
|
{
|
||||||
countdownText.Text = "GO!";
|
countdownText.Text = "GO!";
|
||||||
countdownGrid.Visibility = Visibility.Hidden;
|
countdownGrid.Visibility = Visibility.Hidden;
|
||||||
|
|
||||||
// 播放背景音乐(循环)
|
|
||||||
Utils.PlayBackgroundMusic("1.MP3", true);
|
|
||||||
// 启动60秒倒计时(独立任务)
|
// 启动60秒倒计时(独立任务)
|
||||||
StartGameCountdown(108);
|
StartGameCountdown(105);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void StartGameCountdown(int seconds)
|
private async void StartGameCountdown(int seconds)
|
||||||
@ -265,29 +273,42 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
|
|
||||||
countdownGrid.Visibility = Visibility.Visible;
|
countdownGrid.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
// 播放背景音乐(循环)
|
||||||
|
Utils.PlayBackgroundMusic("1.MP3", true);
|
||||||
|
|
||||||
|
//StartBeatScrollTimer();
|
||||||
|
|
||||||
for (int i = seconds; i >= 0; i--)
|
for (int i = seconds; i >= 0; i--)
|
||||||
{
|
{
|
||||||
countdownText.Text = i.ToString();
|
countdownText.Text = i.ToString();
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 停止音乐
|
||||||
|
Utils.StopBackgroundMusic();
|
||||||
|
// 停止实时检测和视频流
|
||||||
|
|
||||||
countdownGrid.Visibility = Visibility.Hidden;
|
countdownGrid.Visibility = Visibility.Hidden;
|
||||||
|
|
||||||
_musicJumpRopeContext.UserList.ForEach(x =>
|
|
||||||
{
|
|
||||||
x.ImageState = "1";
|
|
||||||
});
|
|
||||||
|
|
||||||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||||||
_mainWin.WebcamClient.StopExtract();
|
_mainWin.WebcamClient.StopExtract();
|
||||||
|
|
||||||
RankingItemList = _musicJumpRopeContext.UpdateRankList();
|
// 计算排名
|
||||||
//ShowRankingBoard(RankingItemList);
|
var rankList = _musicJumpRopeContext.UpdateRankList();
|
||||||
Utils.StopBackgroundMusic();
|
var scoreList = _musicJumpRopeContext.UpdateScoreRankList();
|
||||||
|
|
||||||
|
|
||||||
|
// 更新游戏状态
|
||||||
_currentGameState = GameState.Finished;
|
_currentGameState = GameState.Finished;
|
||||||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
|
||||||
_mainWin.WebcamClient.StartExtract();
|
// 恢复视频流
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||||||
|
_mainWin.WebcamClient.StartExtract();
|
||||||
|
|
||||||
|
// 更新 UI(排行榜或分数板)
|
||||||
|
// ShowRankingBoard(rankList);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime? _raiseStartTime;
|
private DateTime? _raiseStartTime;
|
||||||
@ -435,6 +456,8 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
_musicJumpRopeContext.Sports.Clear();
|
_musicJumpRopeContext.Sports.Clear();
|
||||||
_musicJumpRopeContext.UserList.Clear();
|
_musicJumpRopeContext.UserList.Clear();
|
||||||
_musicJumpRopeContext.UserNumberList.Clear();
|
_musicJumpRopeContext.UserNumberList.Clear();
|
||||||
|
_musicJumpRopeContext.UserBeatSyncList.Clear();
|
||||||
|
_musicBeatTextBlock.Clear();
|
||||||
|
|
||||||
double imgWidth = userBox.ActualWidth;
|
double imgWidth = userBox.ActualWidth;
|
||||||
double imgHeight = userBox.ActualHeight;
|
double imgHeight = userBox.ActualHeight;
|
||||||
@ -467,29 +490,62 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
if (userItem.ImageState != "2")
|
if (userItem.ImageState != "2")
|
||||||
userItem.ImageState = "2";
|
userItem.ImageState = "2";
|
||||||
|
|
||||||
var _currentTime = Utils.GetMusicCurrentTime();
|
var currentTime = Utils.GetMusicCurrentTime();
|
||||||
|
var beats = _musicJumpRopeContext.MusicBeats["1"];
|
||||||
|
|
||||||
bool isOnBeat = _musicBeats.Any(bt => Math.Abs(bt - _currentTime) <= _beatTolerance);
|
for (int j = 0; j < beats.Count; j++)
|
||||||
if (isOnBeat)
|
|
||||||
{
|
{
|
||||||
_musicJumpRopeContext.UserBeatSyncList[i]++;
|
int indexCopy2 = j; // 复制一份当前循环索引
|
||||||
|
if (Math.Abs(beats[indexCopy2] - currentTime) <= _beatTolerance)
|
||||||
if (_musicJumpRopeContext.UserBeatSyncList[i] < count)
|
|
||||||
{
|
{
|
||||||
_musicBeatTextBlock[i].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[i]}";
|
_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]}";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Application.Current.Dispatcher.BeginInvoke(() =>
|
|
||||||
//{
|
|
||||||
// _musicBeatTextBlock[i].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[i]}";
|
|
||||||
//});
|
|
||||||
}
|
}
|
||||||
|
// 滚动条跳跃到当前点附近
|
||||||
|
UpdateBeatScrollSudden(currentTime);
|
||||||
};
|
};
|
||||||
|
|
||||||
sport.Start();
|
sport.Start();
|
||||||
_musicJumpRopeContext.Sports.Add(sport);
|
_musicJumpRopeContext.Sports.Add(sport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private void UpdateCircleCounts(List<Human> humans)
|
||||||
{
|
{
|
||||||
double radiusNormX = 0.07;
|
double radiusNormX = 0.07;
|
||||||
@ -536,10 +592,8 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 推送计数
|
// 推送计数
|
||||||
if (hasHuman)
|
//if (hasHuman)
|
||||||
_musicJumpRopeContext.Sports[i].Pushing(humanInCircle);
|
_musicJumpRopeContext.Sports[i].Pushing(humanInCircle);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private string GetJumpState(int circleIndex, bool humanInCircle)
|
private string GetJumpState(int circleIndex, bool humanInCircle)
|
||||||
@ -587,7 +641,7 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
userItem.VerticalAlignment = VerticalAlignment.Top;
|
userItem.VerticalAlignment = VerticalAlignment.Top;
|
||||||
userItem.HorizontalAlignment = HorizontalAlignment.Left;
|
userItem.HorizontalAlignment = HorizontalAlignment.Left;
|
||||||
userItem.ImageState = "1";
|
userItem.ImageState = "1";
|
||||||
userItem.Margin = new Thickness(centerX - 120, centerY - 700, 0, 0);
|
userItem.Margin = new Thickness(centerX + 120, centerY - 700, 0, 0);
|
||||||
|
|
||||||
// ----------- 创建上方 TextBlock ------------
|
// ----------- 创建上方 TextBlock ------------
|
||||||
var textBlock = new TextBlock
|
var textBlock = new TextBlock
|
||||||
@ -618,5 +672,84 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
|
|
||||||
return userItem;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user