Merge branch 'dev' of http://8.153.108.90:3000/YD/Wpf_AiSportsMicrospace into dev
# Conflicts: # Wpf_AiSportsMicrospace/Views/Home.xaml # Wpf_AiSportsMicrospace/Views/Home.xaml.cs
This commit is contained in:
commit
bf2027035c
@ -1,8 +1,8 @@
|
||||
<Application x:Class="Wpf_AiSportsMicrospace.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace"
|
||||
StartupUri="Home.xaml">
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views"
|
||||
StartupUri="Views/Main.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
|
||||
@ -34,6 +34,9 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
private Point? _lastRightWrist = null;
|
||||
private DateTime? _raiseStartTime = null;
|
||||
private DateTime? _wristStartTime = null;
|
||||
private double _leftWristDeltaX = 0;
|
||||
private double _rightWristDeltaX = 0;
|
||||
private bool _firstHandTriggered = false;
|
||||
|
||||
|
||||
public SportOperate()
|
||||
@ -43,10 +46,10 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
_leftElbow = PostureCalculate.CreatePointTracker("left_elbow", 0);
|
||||
_rightElbow = PostureCalculate.CreatePointTracker("right_elbow", 0);
|
||||
|
||||
//_leftTracker.Amplitude = 0.05f;
|
||||
//_rightTracker.Amplitude = 0.05f;
|
||||
//_leftElbow.Amplitude = 0.05f;
|
||||
//_rightElbow.Amplitude = 0.05f;
|
||||
//_leftTracker.Amplitude = 0.03f;
|
||||
//_rightTracker.Amplitude = 0.03f;
|
||||
//_leftElbow.Amplitude = 0.03f;
|
||||
//_rightElbow.Amplitude = 0.03f;
|
||||
}
|
||||
|
||||
public WebcamClient CreateRTSP()
|
||||
@ -62,119 +65,98 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
var rightWrist = human.Keypoints.FirstOrDefault(x => x.Name == "right_wrist");
|
||||
var rightElbow = human.Keypoints.FirstOrDefault(x => x.Name == "right_elbow");
|
||||
|
||||
// 左手逻辑
|
||||
// --- 左手挥手 ---
|
||||
if (leftWrist != null && leftElbow != null)
|
||||
{
|
||||
var result = RecognizeLeftHandGesture(
|
||||
int leftResult = DetectHorizontalWave(
|
||||
new Point(leftWrist.X, leftWrist.Y),
|
||||
new Point(leftElbow.X, leftElbow.Y));
|
||||
if (result != 0) return result;
|
||||
new Point(leftElbow.X, leftElbow.Y),
|
||||
ref _lastLeftWrist,
|
||||
ref _leftWristDeltaX,
|
||||
true);
|
||||
if (leftResult != (int)WavingAction.None) return leftResult;
|
||||
}
|
||||
|
||||
// 右手逻辑
|
||||
// --- 右手挥手或举手 ---
|
||||
if (rightWrist != null && rightElbow != null)
|
||||
{
|
||||
var result = RecognizeRightHandGesture(
|
||||
int rightWaveResult = DetectHorizontalWave(
|
||||
new Point(rightWrist.X, rightWrist.Y),
|
||||
new Point(rightElbow.X, rightElbow.Y));
|
||||
if (result != 0) return result;
|
||||
}
|
||||
new Point(rightElbow.X, rightElbow.Y),
|
||||
ref _lastRightWrist,
|
||||
ref _rightWristDeltaX,
|
||||
false);
|
||||
if (rightWaveResult != (int)WavingAction.None) return rightWaveResult;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统一的水平挥手检测
|
||||
/// </summary>
|
||||
private int DetectHorizontalWave(Point wrist, Point elbow, Point? lastWrist, bool isLeft)
|
||||
{
|
||||
if (lastWrist != null)
|
||||
{
|
||||
double dx = wrist.X - lastWrist.Value.X;
|
||||
double dy = Math.Abs(wrist.Y - lastWrist.Value.Y);
|
||||
|
||||
// 挥手:水平位移明显,垂直位移小,且接近肘部水平
|
||||
if (Math.Abs(dx) > 30 && dy < 40 && Math.Abs(wrist.Y - elbow.Y) < 100)
|
||||
// --- 举手逻辑 ---
|
||||
double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕在肘上方 → 正值
|
||||
if (verticalRise >= 60) // 举手阈值
|
||||
{
|
||||
if (CheckCooldown())
|
||||
// 初始化计时
|
||||
if (_raiseStartTime == null)
|
||||
_raiseStartTime = DateTime.Now;
|
||||
|
||||
if (_wristStartTime == null)
|
||||
_wristStartTime = DateTime.Now;
|
||||
|
||||
var wristDuration = DateTime.Now - _wristStartTime.Value;
|
||||
|
||||
// 保持 >1 秒才触发一次 FirstHand
|
||||
if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1)
|
||||
{
|
||||
if (isLeft && dx > 0)
|
||||
return (int)WavingAction.LeftWave; // 左手往右挥
|
||||
if (!isLeft && dx < 0)
|
||||
return (int)WavingAction.RightWave; // 右手往左挥
|
||||
_firstHandTriggered = true;
|
||||
return (int)WavingAction.FirstHand; // 举手开始,只触发一次
|
||||
}
|
||||
|
||||
// 判断是否完成3秒举手
|
||||
var duration = DateTime.Now - _raiseStartTime.Value;
|
||||
if (duration.TotalSeconds >= 3)
|
||||
{
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
_firstHandTriggered = false; // 重置状态
|
||||
return (int)WavingAction.RaiseHand; // 举手完成
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)WavingAction.Raising; // 举手中
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 手放下,重置计时和状态
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
_firstHandTriggered = false;
|
||||
}
|
||||
}
|
||||
return (int)WavingAction.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 识别左手动作
|
||||
/// 统一的水平挥手检测
|
||||
/// </summary>
|
||||
public int RecognizeLeftHandGesture(Point wrist, Point elbow)
|
||||
private int DetectHorizontalWave(Point wrist, Point elbow, ref Point? lastWrist, ref double deltaX, bool isLeft)
|
||||
{
|
||||
int result = DetectHorizontalWave(wrist, elbow, _lastLeftWrist, true);
|
||||
_lastLeftWrist = wrist; // 更新记录
|
||||
return result;
|
||||
}
|
||||
private bool _firstHandTriggered = false;
|
||||
|
||||
/// <summary>
|
||||
/// 识别右手动作
|
||||
/// </summary>
|
||||
/// <param name="wrist"></param>
|
||||
/// <param name="elbow"></param>
|
||||
/// <returns></returns>
|
||||
public int RecognizeRightHandGesture(Point wrist, Point elbow)
|
||||
{
|
||||
// --- 先判断水平挥手 ---
|
||||
int waveResult = DetectHorizontalWave(wrist, elbow, _lastRightWrist, false);
|
||||
_lastRightWrist = wrist; // 更新记录
|
||||
if (waveResult != (int)WavingAction.None)
|
||||
return waveResult;
|
||||
|
||||
// --- 举手逻辑 ---
|
||||
double verticalRise = elbow.Y - wrist.Y; // 手腕在肘上方 → 正值
|
||||
if (verticalRise > 100) // 举手阈值
|
||||
if (lastWrist != null)
|
||||
{
|
||||
// 初始化计时
|
||||
if (_raiseStartTime == null)
|
||||
_raiseStartTime = DateTime.Now;
|
||||
double dx = wrist.X - lastWrist.Value.X;
|
||||
double dy = Math.Abs(wrist.Y - lastWrist.Value.Y);
|
||||
|
||||
if (_wristStartTime == null)
|
||||
_wristStartTime = DateTime.Now;
|
||||
// 累计水平位移
|
||||
deltaX += dx;
|
||||
|
||||
var wristDuration = DateTime.Now - _wristStartTime.Value;
|
||||
|
||||
// 保持 >1 秒才触发一次 FirstHand
|
||||
if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1)
|
||||
if (Math.Abs(deltaX) > 40 && dy < 60 && Math.Abs(wrist.Y - elbow.Y) < 60)
|
||||
{
|
||||
_firstHandTriggered = true;
|
||||
return (int)WavingAction.FirstHand; // 举手开始,只触发一次
|
||||
}
|
||||
|
||||
// 判断是否完成3秒举手
|
||||
var duration = DateTime.Now - _raiseStartTime.Value;
|
||||
if (duration.TotalSeconds >= 4)
|
||||
{
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
_firstHandTriggered = false; // 重置状态
|
||||
return (int)WavingAction.RaiseHand; // 举手完成
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)WavingAction.Raising; // 举手中
|
||||
deltaX = 0; // 重置
|
||||
if (CheckCooldown())
|
||||
{
|
||||
return isLeft ? (int)WavingAction.LeftWave : (int)WavingAction.RightWave;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 手放下,重置计时和状态
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
_firstHandTriggered = false;
|
||||
}
|
||||
|
||||
lastWrist = wrist;
|
||||
return (int)WavingAction.None;
|
||||
}
|
||||
|
||||
@ -189,5 +171,8 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
_lastActionTime = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
46
Wpf_AiSportsMicrospace/Common/Utils.cs
Normal file
46
Wpf_AiSportsMicrospace/Common/Utils.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.Common
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
private static MediaPlayer _bgPlayer;
|
||||
public static void PlayBackgroundMusic(string musicFileName)
|
||||
{
|
||||
string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\..");
|
||||
string musicPath = Path.Combine(projectRoot, "Resources", "Music", musicFileName);
|
||||
|
||||
if (_bgPlayer == null)
|
||||
{
|
||||
_bgPlayer = new MediaPlayer();
|
||||
_bgPlayer.Volume = 0.5;
|
||||
_bgPlayer.MediaEnded += (s, e) =>
|
||||
{
|
||||
_bgPlayer.Position = TimeSpan.Zero; // 循环播放
|
||||
_bgPlayer.Play();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_bgPlayer.Stop();
|
||||
}
|
||||
|
||||
_bgPlayer.Open(new Uri(musicPath, UriKind.Absolute));
|
||||
_bgPlayer.Play();
|
||||
}
|
||||
public static void StopBackgroundMusic()
|
||||
{
|
||||
if (_bgPlayer != null)
|
||||
{
|
||||
_bgPlayer.Stop();
|
||||
//_bgPlayer.Close(); // 如果想释放资源,可以取消注释
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,31 +24,11 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
/// CoverFlowControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class CoverFlowControl : UserControl
|
||||
{
|
||||
//public ObservableCollection<Uri> Images { get; set; } = new ObservableCollection<Uri>();
|
||||
public ObservableCollection<CoverFlowItem> Images { get; set; } = new ObservableCollection<CoverFlowItem>();
|
||||
|
||||
// 新增事件:进度条完成
|
||||
{// 新增事件:进度条完成
|
||||
public event Action<CoverFlowItem> ProgressCompleted;
|
||||
|
||||
// 添加附加属性帮助类
|
||||
public static class LayoutHelper
|
||||
{
|
||||
public static int GetZIndex(DependencyObject obj)
|
||||
{
|
||||
return (int)obj.GetValue(ZIndexProperty);
|
||||
}
|
||||
|
||||
public static void SetZIndex(DependencyObject obj, int value)
|
||||
{
|
||||
obj.SetValue(ZIndexProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ZIndexProperty =
|
||||
DependencyProperty.RegisterAttached("ZIndex", typeof(int), typeof(LayoutHelper), new PropertyMetadata(0));
|
||||
}
|
||||
|
||||
public ObservableCollection<CoverFlowItem> Images { get; set; } = new ObservableCollection<CoverFlowItem>();
|
||||
private int _selectedIndex = 0;
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get => _selectedIndex;
|
||||
@ -59,25 +39,9 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
if (value < 0) _selectedIndex = Images.Count - 1;
|
||||
else if (value >= Images.Count) _selectedIndex = 0;
|
||||
else _selectedIndex = value;
|
||||
|
||||
|
||||
for (int i = 0; i < Images.Count; i++)
|
||||
{
|
||||
Images[i].IsSelected = i == _selectedIndex;
|
||||
//if (SelectedIndex == 0)
|
||||
//{
|
||||
// Images[i].ProgressColor = (Brush)new BrushConverter().ConvertFromString("#e73d42")!;
|
||||
//}
|
||||
//else if (SelectedIndex == Images.Count - 1)
|
||||
//{
|
||||
// Images[i].ProgressColor = (Brush)new BrushConverter().ConvertFromString("#215bc7")!;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// Images[i].ProgressColor = (Brush)new BrushConverter().ConvertFromString("#fc640e")!;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
UpdateLayoutWithAnimation();
|
||||
|
||||
@ -85,44 +49,32 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
//var current = Images[_selectedIndex];
|
||||
//StartProgress(current);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public CoverFlowControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
Loaded += (s, e) => UpdateLayoutWithAnimation(true);
|
||||
|
||||
//var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(5) };
|
||||
//timer.Tick += (s, e) =>
|
||||
//{
|
||||
// if (Images.Count == 0) return;
|
||||
// var current = Images[_selectedIndex];
|
||||
// if (current.Progress < 1)
|
||||
// current.Progress += 0.01; // 调整速度
|
||||
//};
|
||||
//timer.Start();
|
||||
}
|
||||
private EventHandler _renderHandler;
|
||||
|
||||
private CoverFlowItem _currentItem;
|
||||
|
||||
private void StartProgress(CoverFlowItem item)
|
||||
{
|
||||
StopProgress();
|
||||
|
||||
_currentItem = item;
|
||||
_currentItem.Progress = 0;
|
||||
|
||||
_renderHandler = (s, e) =>
|
||||
{
|
||||
if (_currentItem.Progress < 1)
|
||||
{
|
||||
_currentItem.Progress += 1.0 / (3 * 60.0); // 3秒完成一圈,假设60帧/s
|
||||
if (_currentItem.Progress > 1) _currentItem.Progress = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
StopProgress();
|
||||
}
|
||||
};
|
||||
|
||||
CompositionTarget.Rendering += _renderHandler;
|
||||
}
|
||||
|
||||
private void StopProgress()
|
||||
{
|
||||
if (_renderHandler != null)
|
||||
{
|
||||
CompositionTarget.Rendering -= _renderHandler;
|
||||
_renderHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 针对当前选中项启动进度条动画
|
||||
/// </summary>
|
||||
public void StartSelectedProgress()
|
||||
{
|
||||
if (SelectedIndex >= 0 && SelectedIndex < Images.Count)
|
||||
@ -145,23 +97,83 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
current.Progress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public CoverFlowControl()
|
||||
public void StartProgress(CoverFlowItem item)
|
||||
{
|
||||
StopProgress();
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
Loaded += (s, e) => UpdateLayoutWithAnimation(true);
|
||||
_currentItem = item;
|
||||
item.Progress = 0;
|
||||
|
||||
//var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(5) };
|
||||
//timer.Tick += (s, e) =>
|
||||
//{
|
||||
// if (Images.Count == 0) return;
|
||||
// var current = Images[_selectedIndex];
|
||||
// if (current.Progress < 1)
|
||||
// current.Progress += 0.01; // 调整速度
|
||||
//};
|
||||
//timer.Start();
|
||||
var container = ItemsHost.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
|
||||
if (container == null) return;
|
||||
|
||||
var canvas = FindVisualChild<Canvas>(container);
|
||||
var spark = FindVisualChild<Ellipse>(canvas);
|
||||
if (spark != null)
|
||||
spark.Visibility = Visibility.Visible;
|
||||
|
||||
_renderHandler = (s, e) =>
|
||||
{
|
||||
if (_currentItem.Progress < 1)
|
||||
{
|
||||
_currentItem.Progress += 0.00556; // 控制速度
|
||||
if (spark != null)
|
||||
{
|
||||
var pos = GetSparkPosition(_currentItem.Progress, 150, 200);
|
||||
Canvas.SetLeft(spark, pos.X - spark.Width / 2);
|
||||
Canvas.SetTop(spark, pos.Y - spark.Height / 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgressCompleted?.Invoke(_currentItem);
|
||||
// 停止动画
|
||||
StopProgress();
|
||||
}
|
||||
};
|
||||
|
||||
CompositionTarget.Rendering += _renderHandler;
|
||||
}
|
||||
|
||||
private void StopProgress()
|
||||
|
||||
{
|
||||
if (_renderHandler != null)
|
||||
{
|
||||
CompositionTarget.Rendering -= _renderHandler;
|
||||
_renderHandler = null;
|
||||
}
|
||||
|
||||
if (_currentItem != null)
|
||||
{
|
||||
_currentItem.Progress = 0;
|
||||
//_currentItem.IsSelected = true;
|
||||
|
||||
var container = ItemsHost.ItemContainerGenerator.ContainerFromItem(_currentItem) as ContentPresenter;
|
||||
if (container != null)
|
||||
{
|
||||
var canvas = FindVisualChild<Canvas>(container);
|
||||
var spark = FindVisualChild<Ellipse>(canvas);
|
||||
if (spark != null)
|
||||
spark.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
_currentItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Point GetSparkPosition(double progress, double width, double height)
|
||||
{
|
||||
double perimeter = 2 * (width + height);
|
||||
double len = progress * perimeter;
|
||||
|
||||
if (len <= width) return new Point(len, 0); // 上边
|
||||
len -= width;
|
||||
if (len <= height) return new Point(width, len); // 右边
|
||||
len -= height;
|
||||
if (len <= width) return new Point(width - len, height); // 下边
|
||||
len -= width;
|
||||
return new Point(0, height - len); // 左边
|
||||
}
|
||||
|
||||
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
@ -180,9 +192,9 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
private void UpdateLayoutWithAnimation(bool instant = false)
|
||||
{
|
||||
double centerX = ActualWidth / 2;
|
||||
double spacing = 500;
|
||||
double sideScale = 1;
|
||||
double centerScale = 1.33;
|
||||
double spacing = 550;
|
||||
double sideScale = 0.93;
|
||||
double centerScale = 1.43;
|
||||
|
||||
for (int i = 0; i < ItemsHost.Items.Count; i++)
|
||||
{
|
||||
@ -193,7 +205,7 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
if (border == null) continue;
|
||||
|
||||
var transformGroup = border.RenderTransform as TransformGroup;
|
||||
var scale = transformGroup!.Children[0] as ScaleTransform;
|
||||
var scale = transformGroup.Children[0] as ScaleTransform;
|
||||
var translate = transformGroup.Children[1] as TranslateTransform;
|
||||
|
||||
double targetX;
|
||||
@ -202,67 +214,52 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
|
||||
if (i == SelectedIndex)
|
||||
{
|
||||
targetX = centerX - 190;
|
||||
targetX = centerX - 75;
|
||||
targetScale = centerScale;
|
||||
targetOpacity = 1.0;
|
||||
}
|
||||
else if (i == SelectedIndex - 1 || (SelectedIndex == 0 && i == Images.Count - 1))
|
||||
{
|
||||
// 左边图片,循环处理
|
||||
targetX = centerX - spacing - 190;
|
||||
targetX = centerX - spacing - 75;
|
||||
targetScale = sideScale;
|
||||
targetOpacity = 1.0;
|
||||
}
|
||||
else if (i == SelectedIndex + 1 || (SelectedIndex == Images.Count - 1 && i == 0))
|
||||
{
|
||||
// 右边图片,循环处理
|
||||
targetX = centerX + spacing - 190;
|
||||
targetX = centerX + spacing - 75;
|
||||
targetScale = sideScale;
|
||||
targetOpacity = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetX = centerX - 190;
|
||||
targetX = centerX - 75;
|
||||
targetScale = sideScale;
|
||||
targetOpacity = 0.0;
|
||||
}
|
||||
|
||||
if (instant)
|
||||
{
|
||||
translate!.X = targetX;
|
||||
scale!.ScaleX = scale.ScaleY = targetScale;
|
||||
translate.X = targetX;
|
||||
scale.ScaleX = scale.ScaleY = targetScale;
|
||||
border.Opacity = targetOpacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
translate!.BeginAnimation(TranslateTransform.XProperty,
|
||||
translate.BeginAnimation(TranslateTransform.XProperty,
|
||||
new DoubleAnimation(targetX, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
|
||||
scale!.BeginAnimation(ScaleTransform.ScaleXProperty,
|
||||
scale.BeginAnimation(ScaleTransform.ScaleXProperty,
|
||||
new DoubleAnimation(targetScale, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
|
||||
scale.BeginAnimation(ScaleTransform.ScaleYProperty,
|
||||
new DoubleAnimation(targetScale, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() });
|
||||
border.BeginAnimation(Border.OpacityProperty,
|
||||
new DoubleAnimation(targetOpacity, TimeSpan.FromMilliseconds(400)));
|
||||
}
|
||||
|
||||
var item = Images[i];
|
||||
|
||||
if (i == SelectedIndex)
|
||||
{
|
||||
item.IsSelected = true;
|
||||
StartProgress(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.IsSelected = false;
|
||||
item.Progress = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
|
||||
{
|
||||
if (obj == null) return null;
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(obj, i);
|
||||
@ -272,17 +269,17 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 外部调用:左滑(下一张)
|
||||
|
||||
|
||||
public void SlideLeft()
|
||||
{
|
||||
StopProgress();
|
||||
SelectedIndex++;
|
||||
}
|
||||
|
||||
// 外部调用:右滑(上一张)
|
||||
public void SlideRight()
|
||||
{
|
||||
StopProgress();
|
||||
SelectedIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
using _3DTools;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/1.gif
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/1.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/2.gif
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/2.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/3.gif
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/3.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
BIN
Wpf_AiSportsMicrospace/Resources/Music/homeprojectselected.mp3
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Music/homeprojectselected.mp3
Normal file
Binary file not shown.
BIN
Wpf_AiSportsMicrospace/Resources/Music/homeprojectselected1.mp3
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Music/homeprojectselected1.mp3
Normal file
Binary file not shown.
BIN
Wpf_AiSportsMicrospace/Resources/Music/musicjumprope.mp3
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Music/musicjumprope.mp3
Normal file
Binary file not shown.
BIN
Wpf_AiSportsMicrospace/Resources/Music/musicjumprope1.mp3
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Music/musicjumprope1.mp3
Normal file
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
<Window x:Class="Wpf_AiSportsMicrospace.Home"
|
||||
<UserControl x:Class="Wpf_AiSportsMicrospace.Home"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
||||
@ -68,4 +68,4 @@
|
||||
Margin="100,120,200,20"
|
||||
/>
|
||||
</Grid>
|
||||
</Window>
|
||||
</UserControl>
|
||||
@ -29,8 +29,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
/// <summary>
|
||||
/// Home.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
///
|
||||
public partial class Home : Window
|
||||
public partial class Home : UserControl
|
||||
{
|
||||
private IHumanPredictor _humanPredictor;
|
||||
private WebcamClient _webcamClient;
|
||||
@ -58,7 +57,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
//coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "4.jpg")));
|
||||
//coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "5.jpg")));
|
||||
|
||||
coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_test.png")) , ProgressColor1 = "#215bc7" , ProgressColor2 = "#fc640e" });
|
||||
coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_test.png")), ProgressColor1 = "#215bc7", ProgressColor2 = "#fc640e" });
|
||||
coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_play.png")), ProgressColor1 = "#e73d42", ProgressColor2 = "#fd8212" });
|
||||
coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_history.png")), ProgressColor1 = "#e73d42", ProgressColor2 = "#215bc7" });
|
||||
|
||||
@ -81,10 +80,12 @@ namespace Wpf_AiSportsMicrospace
|
||||
//};
|
||||
//_webcamClient.StartExtract();
|
||||
|
||||
//StartFrameProcessing();
|
||||
StartFrameProcessing();
|
||||
|
||||
Utils.PlayBackgroundMusic("homeprojectselected.mp3");
|
||||
|
||||
//coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted;
|
||||
}
|
||||
|
||||
private void CoverFlow_ProgressCompleted(CoverFlowItem item)
|
||||
{
|
||||
// 停止抽帧线程/释放资源
|
||||
@ -105,22 +106,20 @@ namespace Wpf_AiSportsMicrospace
|
||||
|
||||
// 根据图片跳转新窗口
|
||||
string uri = item.ImageUri.ToString();
|
||||
Window newWindow = null;
|
||||
|
||||
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();
|
||||
//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
|
||||
|
||||
if (newWindow != null)
|
||||
var mainWin = Application.Current.MainWindow as Main;
|
||||
if (mainWin != null)
|
||||
{
|
||||
Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
newWindow.Show(); // 先显示新窗口
|
||||
this.Close(); // 再关闭当前窗口
|
||||
});
|
||||
Utils.PlayBackgroundMusic("musicjumprope1.mp3");
|
||||
mainWin.SwitchPage(new GroupJumpRope(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,9 +142,8 @@ namespace Wpf_AiSportsMicrospace
|
||||
//};
|
||||
//_webcamClient.StartExtract();
|
||||
|
||||
|
||||
_webcamClient.StartExtract();
|
||||
Thread.Sleep(5);
|
||||
//Thread.Sleep(5);
|
||||
}
|
||||
}
|
||||
}, _cts.Token);
|
||||
@ -177,10 +175,10 @@ namespace Wpf_AiSportsMicrospace
|
||||
var wavingaction = _sportOperate.VerifyWavingAction(human);
|
||||
|
||||
// 把低 8 位作为动作类型,高 8 位作为进度
|
||||
int actionType = wavingaction & 0xFF;
|
||||
int progress = (wavingaction >> 8) & 0xFF;
|
||||
//int actionType = wavingaction & 0xFF;
|
||||
//int progress = (wavingaction >> 8) & 0xFF;
|
||||
|
||||
switch (actionType)
|
||||
switch (wavingaction)
|
||||
{
|
||||
case (int)WavingAction.LeftWave: // 左手挥动
|
||||
Dispatcher.BeginInvoke(() => coverFlow.SlideRight());
|
||||
@ -199,6 +197,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
|
||||
case (int)WavingAction.RaiseHand: // 举手完成
|
||||
_cts.Cancel();
|
||||
coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted;
|
||||
break;
|
||||
|
||||
default: // 没有动作 → 取消进度
|
||||
@ -212,5 +211,9 @@ namespace Wpf_AiSportsMicrospace
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,24 @@
|
||||
<Window x:Class="Wpf_AiSportsMicrospace.Views.GroupJumpRope"
|
||||
<UserControl x:Class="Wpf_AiSportsMicrospace.Views.GroupJumpRope"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views"
|
||||
mc:Ignorable="d"
|
||||
Title="多人跳绳" Height="450" Width="800">
|
||||
<Grid>
|
||||
|
||||
mc:Ignorable="d" Height="500" Width="800">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.Background>
|
||||
<ImageBrush ImageSource="/Resources/Img/Album/home_bg.png" Stretch="UniformToFill"/>
|
||||
</Grid.Background>
|
||||
|
||||
<Image x:Name="GifImage" Stretch="Uniform" Width="300" Height="300"/>
|
||||
|
||||
<!-- 顶部图片 -->
|
||||
<!--<Image
|
||||
Source="/Resources/Img/Album/title.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Width="615"
|
||||
Margin="0,100,0,0"
|
||||
/>-->
|
||||
</Grid>
|
||||
</Window>
|
||||
</UserControl>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -10,18 +11,27 @@ using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using Wpf_AiSportsMicrospace.Common;
|
||||
using WpfAnimatedGif;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// GroupJumpRope.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class GroupJumpRope : Window
|
||||
public partial class GroupJumpRope : UserControl
|
||||
{
|
||||
public GroupJumpRope()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.UriSource = new Uri("../../Resources/Img/gif/1.gif", UriKind.Relative); // 替换成你的 GIF 路径
|
||||
image.EndInit();
|
||||
|
||||
// 设置动画源
|
||||
ImageBehavior.SetAnimatedSource(GifImage, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
Wpf_AiSportsMicrospace/Views/Main.xaml
Normal file
12
Wpf_AiSportsMicrospace/Views/Main.xaml
Normal file
@ -0,0 +1,12 @@
|
||||
<Window x:Class="Wpf_AiSportsMicrospace.Views.Main"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views"
|
||||
mc:Ignorable="d" WindowState="Maximized">
|
||||
<Grid>
|
||||
<!-- 过渡容器 -->
|
||||
<ContentControl x:Name="MainContent"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
98
Wpf_AiSportsMicrospace/Views/Main.xaml.cs
Normal file
98
Wpf_AiSportsMicrospace/Views/Main.xaml.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Media.Media3D;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Main.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class Main : Window
|
||||
{
|
||||
public Main()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 默认显示首页
|
||||
MainContent.Content = new Home();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void AnimateNewPageEnhanced(UserControl newPage, bool fromRight)
|
||||
{
|
||||
var transformGroup = new TransformGroup();
|
||||
var scale = new ScaleTransform(0.7, 0.7);
|
||||
var skew = new SkewTransform(fromRight ? -45 : 45, 0);
|
||||
var translate = new TranslateTransform(fromRight ? ActualWidth : -ActualWidth, 0);
|
||||
transformGroup.Children.Add(scale);
|
||||
transformGroup.Children.Add(skew);
|
||||
transformGroup.Children.Add(translate);
|
||||
newPage.RenderTransformOrigin = new Point(fromRight ? 0 : 1, 0.5);
|
||||
newPage.RenderTransform = transformGroup;
|
||||
|
||||
// 动画
|
||||
var scaleAnim = new DoubleAnimation(0.7, 1, TimeSpan.FromMilliseconds(600)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } };
|
||||
var skewAnim = new DoubleAnimation(fromRight ? -45 : 45, 0, TimeSpan.FromMilliseconds(600)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } };
|
||||
var fadeAnim = new DoubleAnimation(0.2, 1, TimeSpan.FromMilliseconds(600));
|
||||
var translateAnim = new DoubleAnimation(fromRight ? ActualWidth : -ActualWidth, 0, TimeSpan.FromMilliseconds(600)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } };
|
||||
|
||||
scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnim);
|
||||
scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnim);
|
||||
skew.BeginAnimation(SkewTransform.AngleYProperty, skewAnim);
|
||||
translate.BeginAnimation(TranslateTransform.XProperty, translateAnim);
|
||||
newPage.BeginAnimation(OpacityProperty, fadeAnim);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,10 +35,16 @@
|
||||
<None Remove="Resources\Img\Badge\4.png" />
|
||||
<None Remove="Resources\Img\Badge\5.jpg" />
|
||||
<None Remove="Resources\Img\Badge\logo.png" />
|
||||
<None Remove="Resources\Img\gif\1.gif" />
|
||||
<None Remove="Resources\Img\gif\2.gif" />
|
||||
<None Remove="Resources\Img\gif\3.gif" />
|
||||
<None Remove="Resources\Music\homeprojectselected.mp3" />
|
||||
<None Remove="Resources\Music\homeprojectselected1.mp3" />
|
||||
<None Remove="Resources\Music\musicjumprope.mp3" />
|
||||
<None Remove="Resources\Music\musicjumprope1.mp3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="3DTools" Version="1.0.0" />
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Emgu.CV" Version="4.12.0.5764" />
|
||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.12.0.5764" />
|
||||
@ -155,6 +161,27 @@
|
||||
<Resource Include="Resources\Img\Badge\5.jpg">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Img\gif\1.gif">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Img\gif\2.gif">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Img\gif\3.gif">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Music\homeprojectselected.mp3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Music\homeprojectselected1.mp3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Music\musicjumprope.mp3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Music\musicjumprope1.mp3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user