倒计时

This commit is contained in:
tanglong 2025-10-13 14:36:18 +08:00
parent 64bdaa3e05
commit 7db24230e0
7 changed files with 275 additions and 390 deletions

View File

@ -11,7 +11,7 @@
<local:ImageStateToVisibilityConverter x:Key="ImageStateToVisibilityConverter"/>
</UserControl.Resources>
<Grid>
<!--<Border Background="#60000000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>-->
<Image Source="/Resources/Img/test_img/one_rope/top_bg.png" Width="224"
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0"/>
@ -32,7 +32,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Top"/>
<Image Source="/Resources/Img/test_img/one_rope/out_user.png" Width="250" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,131,0,0"
<!--<Image Source="/Resources/Img/test_img/one_rope/out_user.png" Width="250" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,131,0,0"
Visibility="{Binding ImageState, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource ImageStateToVisibilityConverter}, ConverterParameter=3}" />
<Image Source="/Resources/Img/test_img/one_rope/pre_user.png" Width="270" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,131,0,0"
Visibility="{Binding ImageState, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource ImageStateToVisibilityConverter}, ConverterParameter=1}" />
@ -44,6 +44,23 @@
VerticalAlignment="Top"
Width="370"
Margin="0,131,0,0"
Visibility="{Binding ImageState, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource ImageStateToVisibilityConverter}, ConverterParameter=2}" />
Visibility="{Binding ImageState, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource ImageStateToVisibilityConverter}, ConverterParameter=2}" />-->
<Image Source="/Resources/Img/test_img/one_rope/out_user.png" Width="250" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,131,0,0"
x:Name="image3" Visibility="Hidden"/>
<Image Source="/Resources/Img/test_img/one_rope/pre_user.png" Width="270" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,131,0,0"
x:Name="image1" Visibility="Collapsed" />
<!--<Image Source="/Resources/Img/test_img/one_rope/nopeople.png" Width="270" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,131,0,0"
x:Name="image0" Visibility="Collapsed"/>-->
<Image gif:ImageBehavior.AnimatedSource="/Resources/Img/test_img/one_rope/jump_rope.gif"
x:Name="image2"
gif:ImageBehavior.AutoStart="True"
gif:ImageBehavior.RepeatBehavior="Forever"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Width="370"
Margin="0,131,0,0"
/>
</Grid>
</UserControl>
</UserControl>

View File

@ -1,4 +1,5 @@
using System;
using HandyControl.Controls;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
@ -17,6 +18,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using WpfAnimatedGif;
namespace Wpf_AiSportsMicrospace.MyUserControl
{
@ -29,6 +31,7 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
public SportUserItem()
{
InitializeComponent();
//InitGif();
}
public string DisplayText
@ -43,20 +46,99 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
public string NumberText
{
get => (string)GetValue(NumberTextProperty);
set => SetValue(DisplayTextProperty, value);
set => SetValue(NumberTextProperty, value);
}
public static readonly DependencyProperty NumberTextProperty =
DependencyProperty.Register(nameof(NumberText), typeof(string), typeof(SportUserItem), new PropertyMetadata("??"));
DependencyProperty.Register(nameof(NumberText), typeof(string), typeof(SportUserItem), new PropertyMetadata("0"));
public string ImageState
//0 没有人 1 准备中 2 运动中 3 出圈了
//0 没有人 1 准备中 2 运动中 3 出圈了
{
get => (string)GetValue(ImageStateProperty);
set => SetValue(ImageStateProperty, value);
}
public static readonly DependencyProperty ImageStateProperty =
DependencyProperty.Register(nameof(ImageState), typeof(string), typeof(SportUserItem), new PropertyMetadata("0"));
DependencyProperty.Register(nameof(ImageState), typeof(string), typeof(SportUserItem), new PropertyMetadata("0", OnImageStateChanged));
private static void OnImageStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (SportUserItem)d;
control.ShowImage((string)e.NewValue);
}
private void ShowImage(string imageIndex)
{
// 隐藏所有图片
image1.Visibility = Visibility.Hidden;
image2.Visibility = Visibility.Hidden;
image3.Visibility = Visibility.Hidden;
// 显示指定的图片
switch (imageIndex)
{
case "1":
//image1.Visibility = Visibility.Visible;
//image1.Visibility = Visibility.Visible;
image1.Visibility = Visibility.Visible;
//Pause_Click();
break;
case "2":
//image2.Visibility = Visibility.Visible;
image2.Visibility = Visibility.Visible;
//Play_Click();
break;
case "3":
image3.Visibility = Visibility.Visible;
break;
}
}
private BitmapImage _gifSource;
private BitmapFrame _firstFrame; // <- 改为 BitmapFrame
private void InitGif()
{
var uri = new Uri("pack://application:,,,/Resources/Img/test_img/one_rope/jump_rope.gif");
// 1) 加载用于播放的 BitmapImage(注意 OnLoad 防止被锁定)
_gifSource = new BitmapImage();
_gifSource.BeginInit();
_gifSource.UriSource = uri;
_gifSource.CacheOption = BitmapCacheOption.OnLoad;
_gifSource.EndInit();
// 2) 提取第一帧(使用 GifBitmapDecoder)
var sri = Application.GetResourceStream(uri);
if (sri != null)
{
using (var stream = sri.Stream)
{
var decoder = new GifBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
if (decoder.Frames.Count > 0)
_firstFrame = decoder.Frames[0];
}
}
// 3) 默认显示第一帧(静态)
image2.Source = _firstFrame;
}
private void Play_Click()
{
// 重新设置动画源并播放
//ImageBehavior.SetAnimatedSource(image2, _gifSource);
//ImageBehavior.GetAnimationController(image2)?.Play();
var controller = ImageBehavior.GetAnimationController(image2);
controller?.Play();
}
private void Pause_Click()
{
var controller = ImageBehavior.GetAnimationController(image2);
if (controller != null)
{
controller.Pause(); // 暂停动画
//controller.GotoFrame(0); // 回到第一帧
}
}
}
}

View File

@ -33,7 +33,6 @@ namespace Wpf_AiSportsMicrospace.Views
/// </summary>
public partial class CenterHome : UserControl
{
private SportOperate _sportOperate;
private Main _mainWin => Application.Current.MainWindow as Main;
public String _nowSelect = "test"; //测试吧:test 游戏吧:play
@ -53,11 +52,6 @@ namespace Wpf_AiSportsMicrospace.Views
public CenterHome()
{
InitializeComponent();
//string imgPath = Path.Combine(projectRoot, "Resources", "Img" , _nowSelect == "test" ? "test_img" : "play_img");
// 默认选中第1张
coverFlow.SelectedIndex = 0;
InitImg();
Loaded += Window_Loaded;
@ -68,8 +62,6 @@ namespace Wpf_AiSportsMicrospace.Views
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
//Utils.PlayBackgroundMusic("homeprojectselected.mp3", true);
}
private void CenterHome_Unloaded(object sender, RoutedEventArgs e)
@ -115,7 +107,6 @@ namespace Wpf_AiSportsMicrospace.Views
private void CoverFlow_ProgressCompleted(CoverFlowItem item)
{
// 停止抽帧线程/释放资源
try
{
RouterGoNew();
@ -124,28 +115,6 @@ namespace Wpf_AiSportsMicrospace.Views
{
Debug.WriteLine($"停止抽帧异常: {ex.Message}");
}
// 解绑事件,防止重复触发
//coverFlow.ProgressCompleted -= CoverFlow_ProgressCompleted;
// 根据图片跳转新窗口
string uri = item.ImageUri.ToString();
//if (uri.EndsWith("1.jpg"))
// newWindow = new GroupJumpRope();
//else if (uri.EndsWith("2.jpg"))
// newWindow = new GroupJumpRope();
//else if (uri.EndsWith("3.jpg"))
// newWindow = new GroupJumpRope();
// 找到主窗口,切换内容到 GroupJumpRopePage
//var mainWin = Application.Current.MainWindow as Main;
//if (mainWin != null)
//{
// Utils.PlayBackgroundMusic("musicjumprope1.mp3");
// mainWin.SwitchPage(new GroupJumpRope(), true);
//}
}
public void InitImg()
{
@ -165,7 +134,7 @@ namespace Wpf_AiSportsMicrospace.Views
coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(imgPath, "play_jump.png")), ProgressColor1 = "#e73d42", ProgressColor2 = "#fd8212" });
coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(imgPath, "play_music.png")), ProgressColor1 = "#e73d42", ProgressColor2 = "#215bc7" });
}
coverFlow.SelectedIndex = 0;
}
//跳转二级页面

View File

@ -55,7 +55,10 @@ namespace Wpf_AiSportsMicrospace
Utils.PlayBackgroundMusic("homeprojectselected.mp3", true);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
}
private void Home_Unloaded(object sender, RoutedEventArgs e)
{
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
@ -98,10 +101,6 @@ namespace Wpf_AiSportsMicrospace
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
}
private void CoverFlow_ProgressCompleted(CoverFlowItem item)
{
// 停止抽帧线程/释放资源

View File

@ -5,24 +5,22 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
mc:Ignorable="d" Height="1080" Width="1920" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded">
<Grid >
<Grid>
<Grid.Background>
<ImageBrush ImageSource="/Resources/Img/test_img/test_home_bg.png" Stretch="UniformToFill"/>
</Grid.Background>
<Grid Height="1080" Width="1920" x:Name="userBox">
<Image
<Image
Source="/Resources/Img/test_img/one_rope/title.png"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Width="615"
Margin="0,0,0,0"
/>
<!--<local:SportUserItem DisplayText="一号位" NumberText="100000" ImageState="0" Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" />-->
<!-- title -->
<Canvas x:Name="overlayCanvas" IsHitTestVisible="False" Height="1080" Width="1920" Margin="0,10,0,-10" />
<Grid Width="220" Height="130" VerticalAlignment="Top" Margin="0,150,0,0" Visibility="Collapsed" x:Name="countdownGrid">
<Border Background="#005fff" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="30" />
<TextBlock x:Name="countdownText"
<Grid Height="1080" Width="1920" x:Name="userBox"/>
<Grid Width="220" Height="130" VerticalAlignment="Top" Margin="0,150,0,0" Visibility="Hidden" x:Name="countdownGrid">
<Border Background="#005fff" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="30" />
<TextBlock x:Name="countdownText"
Text="35"
FontSize="120"
FontWeight="Bold"
@ -33,7 +31,6 @@
Margin="0,10,0,0"
TextAlignment="Center"
/>
</Grid>
</Grid>
</Grid>
</UserControl>

View File

@ -43,12 +43,14 @@ namespace Wpf_AiSportsMicrospace.Views
private List<SportBase> sports = new();
private List<TextBlock> circleTexts = new();
private List<SportUserItem> userList = new();
private List<string> userNumberList = ["0", "0", "0", "0", "0", "0", "0"];
private double[] circlePositionsX = { 0.07, 0.21, 0.36, 0.50, 0.64, 0.78, 0.92 };
private Main _mainWin => Application.Current.MainWindow as Main;
private List<(double XNorm, double YNorm)> circlePositions = new();
private MediaPlayer _mediaPlayer = new MediaPlayer();
private bool IsGameStarted = false;
List<string> _useName = ["四号位", "一号位", "五号位", "二号位", "六号位", "三号位", "七号位",];
public GroupJumpRope()
{
InitializeComponent();
@ -59,6 +61,7 @@ namespace Wpf_AiSportsMicrospace.Views
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DrawCirclesWithText();
// 播放音乐
PlayMusic("raisehand.mp3");
}
@ -67,56 +70,6 @@ namespace Wpf_AiSportsMicrospace.Views
{
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
}
private void ShowCenterTip(string imagePath, TimeSpan duration)
{
var tipImage = new Image
{
Source = new BitmapImage(new Uri(imagePath, UriKind.Absolute)),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Opacity = 0
};
// 增加图片的大小,调整比例
tipImage.Width = overlayCanvas.ActualWidth * 0.9; // 宽度为 Canvas 宽度的 90%
tipImage.Height = overlayCanvas.ActualHeight * 0.6; // 高度为 Canvas 高度的 60%
// 将图片添加到 Overlay Canvas
overlayCanvas.Children.Add(tipImage);
Canvas.SetLeft(tipImage, (overlayCanvas.ActualWidth - tipImage.Width) / 2); // 居中
Canvas.SetTop(tipImage, (overlayCanvas.ActualHeight - tipImage.Height) / 2); // 居中
// 渐变出现动画
var fadeInAnimation = new DoubleAnimation
{
From = 0,
To = 1,
Duration = TimeSpan.FromSeconds(1.5)
};
tipImage.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
// 定时移除,并且渐变消失
Task.Delay(duration).ContinueWith(_ =>
{
Dispatcher.Invoke(() =>
{
// 渐变消失动画
var fadeOutAnimation = new DoubleAnimation
{
From = 1,
To = 0,
Duration = TimeSpan.FromSeconds(1.5)
};
tipImage.BeginAnimation(UIElement.OpacityProperty, fadeOutAnimation);
// 完成后移除图片
fadeOutAnimation.Completed += (s, e) =>
{
overlayCanvas.Children.Remove(tipImage);
};
});
});
}
private void PlayMusic(string musicFileName)
{
@ -147,17 +100,16 @@ namespace Wpf_AiSportsMicrospace.Views
Source = new BitmapImage(new Uri(imagePath, UriKind.Absolute)),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Opacity = 0
Opacity = 0,
Margin = new Thickness(0, -100, 0, 0),
};
// 增加图片的大小,调整比例
tipImage.Width = overlayCanvas.ActualWidth * 0.9; // 宽度为 Canvas 宽度的 90%
tipImage.Height = overlayCanvas.ActualHeight * 0.6; // 高度为 Canvas 高度的 60%
tipImage.Width = 1920 * 0.9; // 宽度为 Canvas 宽度的 90%
tipImage.Height = 1080 * 0.6; // 高度为 Canvas 高度的 60%
// 将图片添加到 Overlay Canvas
overlayCanvas.Children.Add(tipImage);
Canvas.SetLeft(tipImage, (overlayCanvas.ActualWidth - tipImage.Width) / 2); // 居中
Canvas.SetTop(tipImage, (overlayCanvas.ActualHeight - tipImage.Height) / 2); // 居中
userBox.Children.Add(tipImage);
// 渐变出现动画
var fadeInAnimation = new DoubleAnimation
@ -185,7 +137,7 @@ namespace Wpf_AiSportsMicrospace.Views
// 完成后移除图片
fadeOutAnimation.Completed += (s, e) =>
{
overlayCanvas.Children.Remove(tipImage);
userBox.Children.Remove(tipImage);
};
});
});
@ -233,8 +185,7 @@ namespace Wpf_AiSportsMicrospace.Views
// 没检测到动作,重置倒计时显示
Utils.StopBackgroundMusic();
countdownText.Text = "3";
countdownText.Visibility = Visibility.Collapsed;
countdownGrid.Visibility = Visibility.Collapsed;
countdownGrid.Visibility = Visibility.Hidden;
break;
}
}
@ -255,7 +206,6 @@ namespace Wpf_AiSportsMicrospace.Views
{
_currentCountdown = start;
countdownText.Text = _currentCountdown.ToString();
countdownText.Visibility = Visibility.Visible;
countdownGrid.Visibility = Visibility.Visible;
_lastUpdateTime = DateTime.Now;
@ -282,15 +232,16 @@ namespace Wpf_AiSportsMicrospace.Views
private async void FinishCountdown()
{
foreach (var item in userList)
{
item.ImageState = "2";
}
//foreach (var item in userList)
//{
// item.ImageState = "2";
//}
// 举手完成,显示 ✔
countdownText.Text = "GO!";
countdownText.Visibility = Visibility.Collapsed;
countdownGrid.Visibility = Visibility.Collapsed;
countdownGrid.Visibility = Visibility.Hidden;
IsGameStarted = true;
// 播放背景音乐(循环)
Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true);
@ -299,25 +250,18 @@ namespace Wpf_AiSportsMicrospace.Views
}
private async void StartGameCountdown(int seconds)
{
countdownText.Visibility = Visibility.Visible;
countdownGrid.Visibility = Visibility.Visible;
for (int i = seconds; i >= 0; i--)
{
countdownText.Text = i.ToString();
await Task.Delay(1000); // 不阻塞主线程,计数逻辑继续执行
await Task.Delay(1000);
}
countdownText.Visibility = Visibility.Collapsed;
countdownGrid.Visibility = Visibility.Collapsed;
countdownGrid.Visibility = Visibility.Hidden;
IsGameStarted = false;
// 倒计时完成后可以触发其他逻辑,例如停止音乐
foreach (var item in userList)
{
item.ImageState = "1";
}
Utils.StopBackgroundMusic();
}
@ -331,64 +275,64 @@ namespace Wpf_AiSportsMicrospace.Views
var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist");
var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow");
if (rightWrist == null || rightElbow == null)
return (int)WavingAction.None;
const double raiseThreshold = 60; // 举手阈值
const double holdDuration = 2; // 持续 2 秒触发倒计时
const int countdownSeconds = 3; // 倒计时长度
double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕高于肘部 → 正值
const double raiseThreshold = 60; // 举手阈值,可调整
bool handRaised = false;
if (verticalRise >= raiseThreshold)
if (rightWrist != null && rightElbow != null)
{
// 初始化
_raiseStartTime ??= DateTime.Now;
_wristStartTime ??= DateTime.Now;
_countdownStartTime ??= DateTime.Now;
var wristDuration = DateTime.Now - _wristStartTime.Value;
// 第一次举手触发
if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1)
{
_firstHandTriggered = true;
_lastCountdownSecond = 3;
_countdownStartTime = DateTime.Now;
return (int)WavingAction.FirstHand;
}
// 倒计时逻辑
if (_firstHandTriggered)
{
var elapsed = DateTime.Now - _countdownStartTime.Value;
int currentSecond = 3 - (int)Math.Floor(elapsed.TotalSeconds);
if (currentSecond != _lastCountdownSecond && currentSecond > 0)
{
_lastCountdownSecond = currentSecond;
// 你可以在这里触发倒计时提示(比如语音播报或 UI 更新)
Console.WriteLine($"倒计时:{currentSecond}");
return (int)WavingAction.Raising; // 可选:倒计时状态
}
// 举手完成
if (elapsed.TotalSeconds >= 3)
{
ResetRaiseState();
return (int)WavingAction.RaiseHand; // 举手完成
}
return (int)WavingAction.Raising; // 举手中
}
return (int)WavingAction.Raising;
double verticalRise = rightElbow.Y - rightWrist.Y;
handRaised = verticalRise >= raiseThreshold;
}
else
// ---------- 第一步:持续举手触发倒计时 ----------
if (!_firstHandTriggered)
{
if (handRaised)
{
_raiseStartTime ??= DateTime.Now;
var holdElapsed = (DateTime.Now - _raiseStartTime.Value).TotalSeconds;
if (holdElapsed >= holdDuration)
{
// 举手达到 2 秒,启动倒计时
_firstHandTriggered = true;
_countdownStartTime = DateTime.Now;
_lastCountdownSecond = countdownSeconds;
return (int)WavingAction.FirstHand;
}
}
else
{
// 手放下,重置举手开始时间
_raiseStartTime = DateTime.Now;
}
return (int)WavingAction.None; // 倒计时未开始
}
// ---------- 第二步:倒计时逻辑(独立于手状态) ----------
var countdownElapsed = (DateTime.Now - _countdownStartTime.Value).TotalSeconds;
int currentSecond = countdownSeconds - (int)Math.Floor(countdownElapsed);
if (currentSecond > 0 && currentSecond != _lastCountdownSecond)
{
_lastCountdownSecond = currentSecond;
Console.WriteLine($"倒计时:{currentSecond}");
return (int)WavingAction.Raising; // 倒计时中
}
if (countdownElapsed >= countdownSeconds)
{
// 手放下重置状态
ResetRaiseState();
return (int)WavingAction.None;
return (int)WavingAction.RaiseHand; // 举手完成
}
return (int)WavingAction.Raising; // 倒计时中
}
private void ResetRaiseState()
{
_raiseStartTime = null;
@ -398,28 +342,31 @@ namespace Wpf_AiSportsMicrospace.Views
_lastCountdownSecond = 3;
}
private Dictionary<int, DateTime> _lastJumpUpdateTime = new Dictionary<int, DateTime>();
public Human LocateHuman(List<Human> humans, double begin, double end, double frameWidth, double frameHeight, int circleIndex)
{
if (humans == null || humans.Count == 0)
{
return null;
}
double circleX = circlePositions[circleIndex].XNorm;
double circleY = circlePositions[circleIndex].YNorm;
double radiusNormX = 0.07; // X方向半径
double radiusNormY = 0.1; // Y方向半径
double radiusNormX = 0.073;
double radiusNormY = 0.135;
Human inCircleHuman = null;
bool isOut = false;
foreach (var hu in humans)
{
var rightFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
var leftFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
if (rightFoot == null || leftFoot == null)
continue;
double xRightNorm = rightFoot.X / frameWidth;
double xLeftNorm = leftFoot.X / frameWidth;
// 前后出圈检测(Y方向)
double yRightNorm = rightFoot.Y / frameHeight;
double yLeftNorm = leftFoot.Y / frameHeight;
@ -431,32 +378,61 @@ namespace Wpf_AiSportsMicrospace.Views
if (outOfCircle)
{
Application.Current.Dispatcher.BeginInvoke(() =>
{
circleTexts[circleIndex].Text = "您已出圈!";
userList[circleIndex].ImageState = "3";
});
continue;
isOut = true;
}
// X 范围匹配 → 未出圈的人
if (xRightNorm >= (circleX - radiusNormX) && xRightNorm <= (circleX + radiusNormX))
else if (inCircleHuman == null)
{
return hu; // 返回给计数逻辑
inCircleHuman = hu;
}
}
return null;
if (isOut)
userList[circleIndex].ImageState = "3";
else
{
if (userList[circleIndex].ImageState == "3")
{
userList[circleIndex].ImageState = "1";
}
if (userNumberList[circleIndex] == userList[circleIndex].NumberText)
{
// 获取上次触发时间
if (!_lastJumpUpdateTime.TryGetValue(circleIndex, out var lastTime))
{
lastTime = DateTime.MinValue;
}
// 判断是否超过 2 秒
if ((DateTime.Now - lastTime).TotalSeconds >= 1)
{
userList[circleIndex].ImageState = "1";
_lastJumpUpdateTime[circleIndex] = DateTime.Now;
}
}
else
{
userNumberList[circleIndex] = userList[circleIndex].NumberText;
}
}
//else if
// (inCircleHuman != null) userList[circleIndex].ImageState = "1";
//else
// userList[circleIndex].ImageState = "1";
return inCircleHuman;
}
private void DrawCirclesWithText()
{
overlayCanvas.Children.Clear();
userBox.Children.Clear();
sports.Clear();
circleTexts.Clear();
userList.Clear(); // 清空用户控件列表
double imgWidth = overlayCanvas.ActualWidth;
double imgHeight = overlayCanvas.ActualHeight;
double imgWidth = userBox.ActualWidth;
double imgHeight = userBox.ActualHeight;
double radius = 100;
// 每个圆的位置:X 和 Y 都归一化 0~1
@ -479,37 +455,35 @@ namespace Wpf_AiSportsMicrospace.Views
// 绘制发光圆
//AddGlowEllipse(x, y, overlayCanvas);
AddUserItem(x, y, i);
// 创建文本控件
var text = new TextBlock
{
Text = "0",
Foreground = Brushes.Red,
FontWeight = FontWeights.Bold,
FontSize = 50,
TextAlignment = TextAlignment.Center,
Width = radius * 2
};
Canvas.SetLeft(text, x - radius);
Canvas.SetTop(text, y - radius - 25);
overlayCanvas.Children.Add(text);
circleTexts.Add(text);
var userItem = AddUserItem(x, y, i);
// 绑定运动对象
var sport = SportBase.Create("rope-skipping");
int index = circleTexts.Count - 1;
int indexCopy = i;
var currentItem = userItem;
// 订阅事件
sport.OnTicked += (count, times) =>
{
Application.Current.Dispatcher.BeginInvoke(() =>
{
circleTexts[index].Text = count.ToString();
userList[index].NumberText = count.ToString();
});
//if (currentItem.NumberText == count.ToString())
//{
// currentItem.ImageState = "1";
//}
//else
//{
// currentItem.ImageState = "2";
// currentItem.NumberText = count.ToString();
//}
currentItem.ImageState = "2";
currentItem.NumberText = count.ToString();
//Application.Current.Dispatcher.BeginInvoke(() =>
//{
// currentItem.NumberText = count.ToString();
//});
};
sport.Start();
sports.Add(sport);
}
@ -523,7 +497,7 @@ namespace Wpf_AiSportsMicrospace.Views
double begin = center - range;
double end = center + range;
var human = LocateHuman(humans, begin, end, overlayCanvas.ActualWidth, overlayCanvas.ActualHeight, i);
var human = LocateHuman(humans, begin, end, userBox.ActualWidth, userBox.ActualHeight, i);
if (human != null)
{
sports[i].Pushing(human);
@ -535,31 +509,17 @@ namespace Wpf_AiSportsMicrospace.Views
/// </summary>
private SportUserItem AddUserItem(double centerX, double centerY, int index)
{
List<String> trueName = ["一号位", "二号位", "三号位", "四号位", "五号位", "六号位", "七号位", ];
List<String> useName = ["四号位", "一号位", "五号位", "二号位", "六号位", "三号位", "七号位", ];
var userItem = new SportUserItem();
userItem.Width = 270;
userItem.Height = 560;
userItem.DisplayText = useName[index];
userItem.DisplayText = _useName[index];
userItem.VerticalAlignment = VerticalAlignment.Top;
userItem.HorizontalAlignment = HorizontalAlignment.Left;
if(index == 3)
{
userItem.ImageState = "1";
}
//设置位置---------------------------------
userItem.Margin = new Thickness(centerX - 135, centerY - 540, 0, 0);
userItem.ImageState = "1";
userItem.Margin = new Thickness(centerX - (index == 0 ? 80 : index == 6 ? 190 : 135), centerY - 540, 0, 0);
userBox.Children.Add(userItem);
userList.Add(userItem);
return userItem; //
}
public bool IsInsideCircle(double px, double py, double cx, double cy, double r)
{
double dx = px - cx;
double dy = py - cy;
return dx * dx + dy * dy <= r * r;
}
}
}

View File

@ -36,33 +36,29 @@ namespace Wpf_AiSportsMicrospace.Views
{
InitializeComponent();
//var options = new InferenceOptions()
//{
// GpuEnabled = true
//};
//Yztob.AiSports.Common.SportAppSettingService.Set("inferences", options);
var options = new InferenceOptions()
{
GpuEnabled = true
};
Yztob.AiSports.Common.SportAppSettingService.Set("inferences", options);
_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiLow);
_humanGraphicsRenderer = new HumanGraphicsRenderer();
_humanGraphicsRenderer.DrawLabel = false;
SportOperate = new SportOperate();
WebcamClient = SportOperate.CreateRTSP();
// 开始抽帧线程
StartFrameProcessing();
//WebcamClient.OnExtractFrame += this.ProcessFrame;
//WebcamClient.StartExtract();//开始抽帧
// 默认显示首页
MainContent.Content = new Home();
}
private void StartFrameProcessing()
{
SportOperate = new SportOperate();
WebcamClient = SportOperate.CreateRTSP();
WebcamClient.OnExtractFrame += frame =>
{
//if (frame != null)
_frameQueue.Enqueue(frame);
};
@ -79,16 +75,6 @@ namespace Wpf_AiSportsMicrospace.Views
else
{
// 避免 CPU 占满
//Thread.Sleep(5);
//if (frame == null)
//{
// WebcamClient.OnExtractFrame += frame =>
// {
// //if (frame != null)
// _frameQueue.Enqueue(frame);
// };
//}
//else
Thread.Sleep(5);
}
}
@ -120,48 +106,6 @@ namespace Wpf_AiSportsMicrospace.Views
}
public event EventHandler<List<Human>> HumanFrameUpdated;
//public void SwitchPage(UserControl newPage, bool fromRight)
//{
// if (MainContent.Content == newPage) return;
// var oldPage = MainContent.Content as UserControl;
// if (oldPage != null)
// {
// // 旧页面动画
// var oldTransformGroup = new TransformGroup();
// var oldScale = new ScaleTransform(1, 1);
// var oldSkew = new SkewTransform(0, 0);
// var oldTranslate = new TranslateTransform(0, 0);
// oldTransformGroup.Children.Add(oldScale);
// oldTransformGroup.Children.Add(oldSkew);
// oldTransformGroup.Children.Add(oldTranslate);
// oldPage.RenderTransformOrigin = new Point(fromRight ? 1 : 0, 0.5); // 左右翻页中心
// oldPage.RenderTransform = oldTransformGroup;
// var scaleAnim = new DoubleAnimation(1, 0.7, TimeSpan.FromMilliseconds(600)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } };
// var skewAnim = new DoubleAnimation(0, fromRight ? 45 : -45, TimeSpan.FromMilliseconds(600)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } };
// var fadeAnim = new DoubleAnimation(1, 0.2, TimeSpan.FromMilliseconds(600));
// var translateAnim = new DoubleAnimation(0, fromRight ? -ActualWidth : ActualWidth, TimeSpan.FromMilliseconds(600)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } };
// oldScale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnim);
// oldScale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnim);
// oldSkew.BeginAnimation(SkewTransform.AngleYProperty, skewAnim);
// oldTranslate.BeginAnimation(TranslateTransform.XProperty, translateAnim);
// oldPage.BeginAnimation(OpacityProperty, fadeAnim);
// fadeAnim.Completed += (s, e) =>
// {
// MainContent.Content = newPage;
// AnimateNewPageEnhanced(newPage, fromRight);
// };
// }
// else
// {
// MainContent.Content = newPage;
// AnimateNewPageEnhanced(newPage, fromRight);
// }
//}
public void SwitchPage(UserControl newPage, bool fromRight)
{
@ -183,89 +127,6 @@ namespace Wpf_AiSportsMicrospace.Views
// 清理资源
if (oldPage is IDisposable disposable)
disposable.Dispose();
//container.Children.Clear();
//if (oldPage != null)
//{
// MainContent.Content = null;
// // 创建容器同时显示新旧页面
// var container = new Grid();
// container.Children.Add(newPage); // 新页面在底层
// container.Children.Add(oldPage); // 旧页面在顶层
// MainContent.Content = container;
// // 设置新页面初始状态 - 轻微缩放和偏移
// var newTransformGroup = new TransformGroup();
// var newScale = new ScaleTransform(0.95, 0.95);
// var newTranslate = new TranslateTransform(fromRight ? 80 : -80, 0);
// newTransformGroup.Children.Add(newScale);
// newTransformGroup.Children.Add(newTranslate);
// newPage.RenderTransformOrigin = new Point(0.5, 0.5);
// newPage.RenderTransform = newTransformGroup;
// newPage.Opacity = 1;
// // 旧页面动画 - 飞出效果
// var oldTransformGroup = new TransformGroup();
// var oldScale = new ScaleTransform(1, 1);
// var oldSkew = new SkewTransform(0, 0);
// var oldTranslate = new TranslateTransform(0, 0);
// oldTransformGroup.Children.Add(oldScale);
// oldTransformGroup.Children.Add(oldSkew);
// oldTransformGroup.Children.Add(oldTranslate);
// oldPage.RenderTransformOrigin = new Point(fromRight ? 1 : 0, 0.5);
// oldPage.RenderTransform = oldTransformGroup;
// oldPage.Opacity = 1;
// // 动画参数
// var duration = TimeSpan.FromMilliseconds(1200);
// var easing = new CubicEase { EasingMode = EasingMode.EaseInOut };
// // 旧页面飞出动画
// var oldScaleAnim = new DoubleAnimation(1, 1.1, duration) { EasingFunction = easing };
// var oldSkewAnim = new DoubleAnimation(0, fromRight ? 15 : -15, duration) { EasingFunction = easing };
// var oldTranslateAnim = new DoubleAnimation(0, fromRight ? ActualWidth * 1.1 : -ActualWidth * 1.1, duration)
// {
// EasingFunction = easing
// };
// // 新页面进入动画
// var newScaleAnim = new DoubleAnimation(0.8, 1, duration) { EasingFunction = easing };
// var newTranslateAnim = new DoubleAnimation(fromRight ? -80 : -80, 0, duration) { EasingFunction = easing };
// // 开始动画
// oldScale.BeginAnimation(ScaleTransform.ScaleXProperty, oldScaleAnim);
// oldScale.BeginAnimation(ScaleTransform.ScaleYProperty, oldScaleAnim);
// oldSkew.BeginAnimation(SkewTransform.AngleYProperty, oldSkewAnim);
// oldTranslate.BeginAnimation(TranslateTransform.XProperty, oldTranslateAnim);
// newScale.BeginAnimation(ScaleTransform.ScaleXProperty, newScaleAnim);
// newScale.BeginAnimation(ScaleTransform.ScaleYProperty, newScaleAnim);
// newTranslate.BeginAnimation(TranslateTransform.XProperty, newTranslateAnim);
// // 动画完成后清理
// oldTranslateAnim.Completed += (s, e) =>
// {
// // 确保最终状态
// newPage.RenderTransform = null;
// newPage.Opacity = 1;
// // 切换到只有新页面
// MainContent.Content = newPage;
// // 清理资源
// if (oldPage is IDisposable disposable)
// disposable.Dispose();
// container.Children.Clear();
// };
//}
//else
//{
// MainContent.Content = newPage;
// newPage.Opacity = 1;
//}
}
private void PreloadPageResources(UserControl page)