# Conflicts:
#	Wpf_AiSportsMicrospace/Views/Home.xaml
#	Wpf_AiSportsMicrospace/Views/Home.xaml.cs
This commit is contained in:
ltx 2025-10-09 10:38:53 +08:00
commit bf2027035c
19 changed files with 435 additions and 245 deletions

View File

@ -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>

View File

@ -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;
}
}
}

View 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(); // 如果想释放资源,可以取消注释
}
}
}
}

View File

@ -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--;
}
}
}
}

View File

@ -1,4 +1,4 @@
using _3DTools;

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View 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>

View 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);
}
}
}

View File

@ -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>