操纵杆不会旋转超出范围
Joystick does not rotate out of range
我的项目中需要一个操纵杆。我在设计和功能方面找到了一个很好的例子。
但是,当鼠标指针离开操纵杆时,此示例不旋转并冻结。自然,这会给用户留下不好的印象。我该如何解决这个问题?
我也分享了代码,因为他们无法通过 Github 下载项目。
CustomControl.cs
public partial class OnScreenJoystick : UserControl
{
/// <summary>Current angle in degrees from 0 to 360</summary>
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(OnScreenJoystick), null);
/// <summary>Current distance (or "power"), from 0 to 100</summary>
public static readonly DependencyProperty DistanceProperty =
DependencyProperty.Register("Distance", typeof(double), typeof(OnScreenJoystick), null);
/// <summary>How often should be raised StickMove event in degrees</summary>
public static readonly DependencyProperty AngleStepProperty =
DependencyProperty.Register("AngleStep", typeof(double), typeof(OnScreenJoystick), new PropertyMetadata(1.0));
/// <summary>How often should be raised StickMove event in distance units</summary>
public static readonly DependencyProperty DistanceStepProperty =
DependencyProperty.Register("DistanceStep", typeof(double), typeof(OnScreenJoystick), new PropertyMetadata(1.0));
/* Unstable - needs work */
///// <summary>Indicates whether the joystick knob resets its place after being released</summary>
//public static readonly DependencyProperty ResetKnobAfterReleaseProperty =
// DependencyProperty.Register(nameof(ResetKnobAfterRelease), typeof(bool), typeof(VirtualJoystick), new PropertyMetadata(true));
/// <summary>Current angle in degrees from 0 to 360</summary>
public double Angle
{
get { return Convert.ToDouble(GetValue(AngleProperty)); }
private set { SetValue(AngleProperty, value); }
}
/// <summary>current distance (or "power"), from 0 to 100</summary>
public double Distance
{
get { return Convert.ToDouble(GetValue(DistanceProperty)); }
private set { SetValue(DistanceProperty, value); }
}
/// <summary>How often should be raised StickMove event in degrees</summary>
public double AngleStep
{
get { return Convert.ToDouble(GetValue(AngleStepProperty)); }
set
{
if (value < 1) value = 1; else if (value > 90) value = 90;
SetValue(AngleStepProperty, Math.Round(value));
}
}
/// <summary>How often should be raised StickMove event in distance units</summary>
public double DistanceStep
{
get { return Convert.ToDouble(GetValue(DistanceStepProperty)); }
set
{
if (value < 1) value = 1; else if (value > 50) value = 50;
SetValue(DistanceStepProperty, value);
}
}
/// <summary>Indicates whether the joystick knob resets its place after being released</summary>
//public bool ResetKnobAfterRelease
//{
// get { return Convert.ToBoolean(GetValue(ResetKnobAfterReleaseProperty)); }
// set { SetValue(ResetKnobAfterReleaseProperty, value); }
//}
/// <summary>Delegate holding data for joystick state change</summary>
/// <param name="sender">The object that fired the event</param>
/// <param name="args">Holds new values for angle and distance</param>
public delegate void OnScreenJoystickEventHandler(OnScreenJoystick sender, VirtualJoystickEventArgs args);
/// <summary>Delegate for joystick events that hold no data</summary>
/// <param name="sender">The object that fired the event</param>
public delegate void EmptyJoystickEventHandler(OnScreenJoystick sender);
/// <summary>This event fires whenever the joystick moves</summary>
public event OnScreenJoystickEventHandler Moved;
/// <summary>This event fires once the joystick is released and its position is reset</summary>
public event EmptyJoystickEventHandler Released;
/// <summary>This event fires once the joystick is captured</summary>
public event EmptyJoystickEventHandler Captured;
private Point _startPos;
private double _prevAngle, _prevDistance;
private readonly Storyboard centerKnob;
public OnScreenJoystick()
{
InitializeComponent();
Knob.MouseLeftButtonDown += Knob_MouseLeftButtonDown;
Knob.MouseLeftButtonUp += Knob_MouseLeftButtonUp;
Knob.MouseMove += Knob_MouseMove;
centerKnob = Knob.Resources["CenterKnob"] as Storyboard;
}
private void Knob_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPos = e.GetPosition(Base);
_prevAngle = _prevDistance = 0;
Captured?.Invoke(this);
Knob.CaptureMouse();
centerKnob.Stop();
}
private void Knob_MouseMove(object sender, MouseEventArgs e)
{
if (!Knob.IsMouseCaptured) return;
Point newPos = e.GetPosition(Base);
Point deltaPos = new Point(newPos.X - _startPos.X, newPos.Y - _startPos.Y);
double angle = Math.Atan2(deltaPos.Y, deltaPos.X) * 180 / Math.PI;
if (angle > 0)
angle += 90;
else
{
angle = 270 + (180 + angle);
if (angle >= 360) angle -= 360;
}
double distance = Math.Round(Math.Sqrt(deltaPos.X * deltaPos.X + deltaPos.Y * deltaPos.Y) / 135 * 100);
if (distance <= 100)
{
Angle = angle;
Distance = distance;
knobPosition.X = deltaPos.X;
knobPosition.Y = deltaPos.Y;
if (Moved == null ||
(!(Math.Abs(_prevAngle - angle) > AngleStep) && !(Math.Abs(_prevDistance - distance) > DistanceStep)))
return;
Moved?.Invoke(this, new VirtualJoystickEventArgs { Angle = Angle, Distance = Distance });
_prevAngle = Angle;
_prevDistance = Distance;
}
}
private void Knob_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Knob.ReleaseMouseCapture();
centerKnob.Begin();
}
private void centerKnob_Completed(object sender, EventArgs e)
{
Angle = Distance = _prevAngle = _prevDistance = 0;
Released?.Invoke(this);
}
}
CustomControl.xaml
<UserControl x:Class="WpfCustomControls.OnScreenJoystick"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfCustomControls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Viewbox Name="Viewbox" Stretch="Uniform">
<Grid Background="Transparent">
<Canvas x:Name="Base" Margin="0" Width="340" Height="340">
<Ellipse HorizontalAlignment="Left" Height="340" VerticalAlignment="Top" Width="340">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF2C2A2A" Offset="1" />
<GradientStop Color="#FF3A3737" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="170" VerticalAlignment="Top" Width="170" Canvas.Left="84" Canvas.Top="84">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF0E0E0E" Offset="1" />
<GradientStop Color="#FF1D1D1D" />
<GradientStop Color="#FF323030" Offset="0.453" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" Canvas.Left="147.875" Canvas.Top="37.625" />
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" RenderTransformOrigin="0.5,0.5" Canvas.Left="147.875" Canvas.Top="284.125">
<Path.RenderTransform>
<ScaleTransform ScaleY="-1" />
</Path.RenderTransform>
</Path>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" RenderTransformOrigin="0.5,0.5" Canvas.Left="270.875" Canvas.Top="162.125">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="-1" ScaleX="-1" />
<RotateTransform Angle="-90" />
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" RenderTransformOrigin="0.5,0.5" Canvas.Left="24.375" Canvas.Top="163.625">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90" />
<ScaleTransform ScaleX="-1" />
</TransformGroup>
</Path.RenderTransform>
</Path>
<Canvas x:Name="Knob" VerticalAlignment="Top" HorizontalAlignment="Left" Width="0" Height="0" RenderTransformOrigin="0.5,0.5" Canvas.Left="125" Canvas.Top="125">
<!--<Ellipse x:Name="Shadow" HorizontalAlignment="Left" Height="88" VerticalAlignment="Top" Width="86" Fill="#52131212" Canvas.Left="22" Canvas.Top="18" />-->
<Ellipse x:Name="KnobBase" HorizontalAlignment="Left" Height="90" VerticalAlignment="Top" Width="90" RenderTransformOrigin="0.5,0.5" Canvas.Top="1">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF8A8A8A" />
<GradientStop Color="#FF979797" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="74.313" VerticalAlignment="Top" Width="82.189" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.613" Canvas.Top="1.692">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#C0828080" Offset="0.797" />
<GradientStop Color="#FD000000" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-28.434" />
<SkewTransform AngleX="-2.144" />
<TranslateTransform X="-1.199" Y="0.649" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="75.491" VerticalAlignment="Top" Width="70.887" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="12.396" Canvas.Top="5.057">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00858585" Offset="0" />
<GradientStop Color="#1AFFFFFF" Offset="1" />
<GradientStop Color="#3FC2C2C2" Offset="0.349" />
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<SkewTransform CenterX="3" CenterY="-4" />
<RotateTransform Angle="-.7628" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="72.722" VerticalAlignment="Top" Width="72.936" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.631" Canvas.Top="4.896">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#9A909090" Offset="1" />
<GradientStop Color="Gray" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform Angle="-31.733"></RotateTransform>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="39" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="14.001" Canvas.Top="11.001">
<Ellipse.RenderTransform>
<SkewTransform CenterX="-8"></SkewTransform>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF898989" Offset="0" />
<GradientStop Color="#38777777" Offset="1" />
<GradientStop Color="#55676767" Offset="0.672" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M9.74935,11.916 L13.084,15.166 L12.1668,16.833 L11.3333,18.583 L10.4999,20.416 L9.24961,20.833 L6.99967,20.583 L6.75,18.333 L7.66697,15.333 L8.75037,12.916 z M3.6672,9.74999 L7.084,10.083 L5.75037,12.25 L4.66704,14 L4.33365,16.583 L4.25036,18.75 L4.41695,20.5 L0,20.166 L0.16699,16.916 L1.16693,13.833 L2.50016,11.583 z M18.1671,6.33301 L21.167,6.33301 L21.667,8.5 L20.75,9.75 L18.5841,10.833 L15.8337,13 L12.584,8.83301 L15.2502,7 z M20.917,0 L20.917,3.16601 L18.1674,2.99999 L15.8337,3.583 L13.5837,4.833 L11.3337,5.99999 L10.5003,6.416 L8.584,3.833 L11.0842,2.41601 L13.3341,0.833006 L16.417,0.166016 z" Fill="#99EEEEEE" HorizontalAlignment="Left" Height="20.833" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="21.667" Canvas.Left="18.166" Canvas.Top="15.917" />
<Canvas.RenderTransform>
<TranslateTransform x:Name="knobPosition" />
</Canvas.RenderTransform>
<Canvas.Resources>
<Storyboard x:Key="CenterKnob" Name="centerKnob" Completed="centerKnob_Completed">
<DoubleAnimation Storyboard.TargetName="knobPosition"
Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="knobPosition" Storyboard.TargetProperty="Y" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Canvas.Resources>
</Canvas>
</Canvas>
</Grid>
</Viewbox>
我用不同的方法解决了这个问题。现在下面的代码可以工作了。我还添加了键盘控制。
Xaml代码:
<Window x:Class="Joystick.MainWindow"
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:Joystick"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="1000" MouseMove="Ellipse_MouseMove" MouseLeftButtonUp="Joystick_MouseLeftButtonUp" KeyDown="Window_KeyDown">
<Grid Name="MainGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="KNOB POSITION"/>
<TextBlock Name="XTextBlock"/>
<TextBlock Name="YTextBlock"/>
<TextBlock Text="MOUSE POSITION"/>
<TextBlock Name="XMousePos"/>
<TextBlock Name="YMousePos"/>
<TextBlock Text="ANGEL / DISTANCE"/>
<TextBlock Name="Angle"/>
<TextBlock Name="Distance"/>
</StackPanel>
<Canvas Name="LayoutRoot" Grid.Column="1" Width="300" Height="300" Margin="0,234,444,235" >
<Ellipse Name="Joystick" Height="200" Canvas.Left="50" Canvas.Top="50" Stroke="Black" Width="200" >
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF2C2A2A" Offset="1" />
<GradientStop Color="#FF3A3737" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Height="100" Width="100" Canvas.Left="100" Canvas.Top="100">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Red" Offset="1" />
<GradientStop Color="#FF1D1D1D" />
<GradientStop Color="#FF323030" Offset="0.453" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Name="UpArrow" Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="Red" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="137.175" Canvas.Top="70"/>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="{Binding ElementName=UpArrow , Path=Fill}" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="70" Canvas.Top="137.175">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90"/>
<ScaleTransform ScaleX="-1"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="{Binding ElementName=UpArrow , Path=Fill}" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="230" Canvas.Top="137.175">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-90"/>
<ScaleTransform ScaleX="-1" ScaleY="-1"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path Name="DownArrow" Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="{Binding ElementName=UpArrow , Path=Fill}" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="137.175" Canvas.Top="230">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform/>
<ScaleTransform ScaleY="-1"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Canvas x:Name="Knob" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50" Height="50" Canvas.Left="125" Canvas.Top="125" MouseDown="Knob_MouseDown" MouseUp="Knob_MouseUp">
<!--<Ellipse x:Name="Shadow" HorizontalAlignment="Left" Height="88" VerticalAlignment="Top" Width="86" Fill="#52131212" Canvas.Left="22" Canvas.Top="18" />-->
<Ellipse x:Name="KnobBase" HorizontalAlignment="Left" Height="50" VerticalAlignment="Top" Width="50" RenderTransformOrigin="0.5,0.5" Canvas.Top="1">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF8A8A8A" />
<GradientStop Color="#FF979797" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="38.795" VerticalAlignment="Top" Width="44.420" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.613" Canvas.Top="1.692">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#C0828080" Offset="0.797" />
<GradientStop Color="#FD000000" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-28.434" />
<SkewTransform AngleX="-2.144" />
<TranslateTransform X="-1.199" Y="0.649" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="39.636" VerticalAlignment="Top" Width="36.347" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="12.396" Canvas.Top="5.057">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00858585" Offset="0" />
<GradientStop Color="#1AFFFFFF" Offset="1" />
<GradientStop Color="#3FC2C2C2" Offset="0.349" />
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<SkewTransform CenterX="3" CenterY="-4" />
<RotateTransform Angle="-.7628" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="37.658" VerticalAlignment="Top" Width="37.811" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.631" Canvas.Top="4.896">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#9A909090" Offset="1" />
<GradientStop Color="Gray" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform Angle="-31.733"></RotateTransform>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="12.142" VerticalAlignment="Top" Width="13.571" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="14.001" Canvas.Top="11.001">
<Ellipse.RenderTransform>
<SkewTransform CenterX="-8"></SkewTransform>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF898989" Offset="0" />
<GradientStop Color="#38777777" Offset="1" />
<GradientStop Color="#55676767" Offset="0.672" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M9.74935,11.916 L13.084,15.166 L12.1668,16.833 L11.3333,18.583 L10.4999,20.416 L9.24961,20.833 L6.99967,20.583 L6.75,18.333 L7.66697,15.333 L8.75037,12.916 z M3.6672,9.74999 L7.084,10.083 L5.75037,12.25 L4.66704,14 L4.33365,16.583 L4.25036,18.75 L4.41695,20.5 L0,20.166 L0.16699,16.916 L1.16693,13.833 L2.50016,11.583 z M18.1671,6.33301 L21.167,6.33301 L21.667,8.5 L20.75,9.75 L18.5841,10.833 L15.8337,13 L12.584,8.83301 L15.2502,7 z M20.917,0 L20.917,3.16601 L18.1674,2.99999 L15.8337,3.583 L13.5837,4.833 L11.3337,5.99999 L10.5003,6.416 L8.584,3.833 L11.0842,2.41601 L13.3341,0.833006 L16.417,0.166016 z" Fill="#99EEEEEE" HorizontalAlignment="Left" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Height="11.904" Width="15.476" Canvas.Left="12.166" Canvas.Top="12.917" />
</Canvas>
</Canvas>
</Grid>
变量
Point KeyjoystickPos = new Point();
double step = 0.01;
double stepMaxRange = 1.00;
double maxRange = 1.00;
public MainWindow()
{
InitializeComponent();
}
Ellipse_MoseMove
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
if (!Knob.IsMouseCaptured) return;
double joystickRadius = Joystick.Height * 0.5;
Vector joystickPos = e.GetPosition(Joystick) - new Point(joystickRadius, joystickRadius);
joystickPos /= joystickRadius;
double angle = Math.Atan2(joystickPos.Y, joystickPos.X);
if (e.LeftButton == MouseButtonState.Pressed)
{
if (joystickPos.Length > 1.0)
{
joystickPos.X = Math.Cos(angle);
joystickPos.Y = Math.Sin(angle);
}
UpdateKnobPosition(joystickPos.X , joystickPos.Y);
}
}
更新旋钮位置
private void UpdateKnobPosition(double joystickPosX ,double joystickPosY)
{
double fJoystickRadius = Joystick.Height * 0.5;
double fKnobRadius = Knob.Width * 0.5;
Canvas.SetLeft(Knob, Canvas.GetLeft(Joystick) +
joystickPosX * fJoystickRadius + fJoystickRadius - fKnobRadius);
Canvas.SetTop(Knob, Canvas.GetTop(Joystick) +
joystickPosY * fJoystickRadius + fJoystickRadius - fKnobRadius);
double distance = Math.Round(Math.Sqrt(joystickPosX * 100.00 * joystickPosX * 100.00 + joystickPosY * 100.00 * joystickPosY * 100.00) / 135.00 * 135.00);
var angle2 = Math.Atan2(joystickPosY, joystickPosX) * 180 / Math.PI;
if (angle2 > 0)
angle2 += 90;
else
{
angle2 = 270 + (180 + angle2);
if (angle2 >= 360) angle2 -= 360;
}
Angle.Text = angle2.ToString();
Distance.Text = distance.ToString();
XMousePos.Text = Convert.ToString(joystickPosX * 100);
YMousePos.Text = Convert.ToString(joystickPosY * 100);
}
Joystick_MouseLeftButtonUp
private void Joystick_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
double joystickRadius = Joystick.Height * 0.5;
Vector joystickPos = e.GetPosition(Joystick) -
new Point(joystickRadius, joystickRadius);
joystickPos /= joystickRadius;
joystickPos.X = 0;
joystickPos.Y = 0;
KeyjoystickPos.X = 0;
KeyjoystickPos.Y = 0;
UpdateKnobPosition(joystickPos.X , joystickPos.Y);
}
Knob_MouseDown
private void Knob_MouseDown(object sender, MouseButtonEventArgs e)
{
var Knob = (Canvas)sender;
Knob.CaptureMouse();
}
Knob_MouseUp
private void Knob_MouseUp(object sender, MouseButtonEventArgs e)
{
var Knob = (Canvas)sender;
Knob.ReleaseMouseCapture();
}
Window_KeyDown
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.W || e.Key == Key.Up)
{
KeyjoystickPos.Y = (KeyjoystickPos.Y <= -stepMaxRange) ? KeyjoystickPos.Y = -maxRange : KeyjoystickPos.Y = KeyjoystickPos.Y - step;
}
if (e.Key == Key.A || e.Key == Key.Left)
{
KeyjoystickPos.X = (KeyjoystickPos.X <= -stepMaxRange) ? KeyjoystickPos.X = -maxRange : KeyjoystickPos.X = KeyjoystickPos.X - step;
}
if (e.Key == Key.S || e.Key == Key.Down)
{
KeyjoystickPos.Y = (KeyjoystickPos.Y >= stepMaxRange) ? KeyjoystickPos.Y = maxRange : KeyjoystickPos.Y = KeyjoystickPos.Y + step;
}
if (e.Key == Key.D || e.Key == Key.Right)
{
KeyjoystickPos.X = (KeyjoystickPos.X >= stepMaxRange) ? KeyjoystickPos.X = maxRange : KeyjoystickPos.X = KeyjoystickPos.X + step;
}
if (e.Key == Key.Space)
{
KeyjoystickPos.X = 0;
KeyjoystickPos.Y = 0;
}
double angle = Math.Atan2(KeyjoystickPos.Y, KeyjoystickPos.X);
if(KeyjoystickPos.X*100 * KeyjoystickPos.X*100 + KeyjoystickPos.Y*100 * KeyjoystickPos.Y*100 > 100*100)
{
KeyjoystickPos.X = Math.Cos(angle);
KeyjoystickPos.Y = Math.Sin(angle);
}
UpdateKnobPosition(KeyjoystickPos.X, KeyjoystickPos.Y);
}
我的项目中需要一个操纵杆。我在设计和功能方面找到了一个很好的例子。
但是,当鼠标指针离开操纵杆时,此示例不旋转并冻结。自然,这会给用户留下不好的印象。我该如何解决这个问题?
我也分享了代码,因为他们无法通过 Github 下载项目。
CustomControl.cs
public partial class OnScreenJoystick : UserControl
{
/// <summary>Current angle in degrees from 0 to 360</summary>
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(OnScreenJoystick), null);
/// <summary>Current distance (or "power"), from 0 to 100</summary>
public static readonly DependencyProperty DistanceProperty =
DependencyProperty.Register("Distance", typeof(double), typeof(OnScreenJoystick), null);
/// <summary>How often should be raised StickMove event in degrees</summary>
public static readonly DependencyProperty AngleStepProperty =
DependencyProperty.Register("AngleStep", typeof(double), typeof(OnScreenJoystick), new PropertyMetadata(1.0));
/// <summary>How often should be raised StickMove event in distance units</summary>
public static readonly DependencyProperty DistanceStepProperty =
DependencyProperty.Register("DistanceStep", typeof(double), typeof(OnScreenJoystick), new PropertyMetadata(1.0));
/* Unstable - needs work */
///// <summary>Indicates whether the joystick knob resets its place after being released</summary>
//public static readonly DependencyProperty ResetKnobAfterReleaseProperty =
// DependencyProperty.Register(nameof(ResetKnobAfterRelease), typeof(bool), typeof(VirtualJoystick), new PropertyMetadata(true));
/// <summary>Current angle in degrees from 0 to 360</summary>
public double Angle
{
get { return Convert.ToDouble(GetValue(AngleProperty)); }
private set { SetValue(AngleProperty, value); }
}
/// <summary>current distance (or "power"), from 0 to 100</summary>
public double Distance
{
get { return Convert.ToDouble(GetValue(DistanceProperty)); }
private set { SetValue(DistanceProperty, value); }
}
/// <summary>How often should be raised StickMove event in degrees</summary>
public double AngleStep
{
get { return Convert.ToDouble(GetValue(AngleStepProperty)); }
set
{
if (value < 1) value = 1; else if (value > 90) value = 90;
SetValue(AngleStepProperty, Math.Round(value));
}
}
/// <summary>How often should be raised StickMove event in distance units</summary>
public double DistanceStep
{
get { return Convert.ToDouble(GetValue(DistanceStepProperty)); }
set
{
if (value < 1) value = 1; else if (value > 50) value = 50;
SetValue(DistanceStepProperty, value);
}
}
/// <summary>Indicates whether the joystick knob resets its place after being released</summary>
//public bool ResetKnobAfterRelease
//{
// get { return Convert.ToBoolean(GetValue(ResetKnobAfterReleaseProperty)); }
// set { SetValue(ResetKnobAfterReleaseProperty, value); }
//}
/// <summary>Delegate holding data for joystick state change</summary>
/// <param name="sender">The object that fired the event</param>
/// <param name="args">Holds new values for angle and distance</param>
public delegate void OnScreenJoystickEventHandler(OnScreenJoystick sender, VirtualJoystickEventArgs args);
/// <summary>Delegate for joystick events that hold no data</summary>
/// <param name="sender">The object that fired the event</param>
public delegate void EmptyJoystickEventHandler(OnScreenJoystick sender);
/// <summary>This event fires whenever the joystick moves</summary>
public event OnScreenJoystickEventHandler Moved;
/// <summary>This event fires once the joystick is released and its position is reset</summary>
public event EmptyJoystickEventHandler Released;
/// <summary>This event fires once the joystick is captured</summary>
public event EmptyJoystickEventHandler Captured;
private Point _startPos;
private double _prevAngle, _prevDistance;
private readonly Storyboard centerKnob;
public OnScreenJoystick()
{
InitializeComponent();
Knob.MouseLeftButtonDown += Knob_MouseLeftButtonDown;
Knob.MouseLeftButtonUp += Knob_MouseLeftButtonUp;
Knob.MouseMove += Knob_MouseMove;
centerKnob = Knob.Resources["CenterKnob"] as Storyboard;
}
private void Knob_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPos = e.GetPosition(Base);
_prevAngle = _prevDistance = 0;
Captured?.Invoke(this);
Knob.CaptureMouse();
centerKnob.Stop();
}
private void Knob_MouseMove(object sender, MouseEventArgs e)
{
if (!Knob.IsMouseCaptured) return;
Point newPos = e.GetPosition(Base);
Point deltaPos = new Point(newPos.X - _startPos.X, newPos.Y - _startPos.Y);
double angle = Math.Atan2(deltaPos.Y, deltaPos.X) * 180 / Math.PI;
if (angle > 0)
angle += 90;
else
{
angle = 270 + (180 + angle);
if (angle >= 360) angle -= 360;
}
double distance = Math.Round(Math.Sqrt(deltaPos.X * deltaPos.X + deltaPos.Y * deltaPos.Y) / 135 * 100);
if (distance <= 100)
{
Angle = angle;
Distance = distance;
knobPosition.X = deltaPos.X;
knobPosition.Y = deltaPos.Y;
if (Moved == null ||
(!(Math.Abs(_prevAngle - angle) > AngleStep) && !(Math.Abs(_prevDistance - distance) > DistanceStep)))
return;
Moved?.Invoke(this, new VirtualJoystickEventArgs { Angle = Angle, Distance = Distance });
_prevAngle = Angle;
_prevDistance = Distance;
}
}
private void Knob_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Knob.ReleaseMouseCapture();
centerKnob.Begin();
}
private void centerKnob_Completed(object sender, EventArgs e)
{
Angle = Distance = _prevAngle = _prevDistance = 0;
Released?.Invoke(this);
}
}
CustomControl.xaml
<UserControl x:Class="WpfCustomControls.OnScreenJoystick"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfCustomControls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Viewbox Name="Viewbox" Stretch="Uniform">
<Grid Background="Transparent">
<Canvas x:Name="Base" Margin="0" Width="340" Height="340">
<Ellipse HorizontalAlignment="Left" Height="340" VerticalAlignment="Top" Width="340">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF2C2A2A" Offset="1" />
<GradientStop Color="#FF3A3737" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="170" VerticalAlignment="Top" Width="170" Canvas.Left="84" Canvas.Top="84">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF0E0E0E" Offset="1" />
<GradientStop Color="#FF1D1D1D" />
<GradientStop Color="#FF323030" Offset="0.453" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" Canvas.Left="147.875" Canvas.Top="37.625" />
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" RenderTransformOrigin="0.5,0.5" Canvas.Left="147.875" Canvas.Top="284.125">
<Path.RenderTransform>
<ScaleTransform ScaleY="-1" />
</Path.RenderTransform>
</Path>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" RenderTransformOrigin="0.5,0.5" Canvas.Left="270.875" Canvas.Top="162.125">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="-1" ScaleX="-1" />
<RotateTransform Angle="-90" />
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="#FF575757" HorizontalAlignment="Left" Height="18.375" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="42.75" RenderTransformOrigin="0.5,0.5" Canvas.Left="24.375" Canvas.Top="163.625">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90" />
<ScaleTransform ScaleX="-1" />
</TransformGroup>
</Path.RenderTransform>
</Path>
<Canvas x:Name="Knob" VerticalAlignment="Top" HorizontalAlignment="Left" Width="0" Height="0" RenderTransformOrigin="0.5,0.5" Canvas.Left="125" Canvas.Top="125">
<!--<Ellipse x:Name="Shadow" HorizontalAlignment="Left" Height="88" VerticalAlignment="Top" Width="86" Fill="#52131212" Canvas.Left="22" Canvas.Top="18" />-->
<Ellipse x:Name="KnobBase" HorizontalAlignment="Left" Height="90" VerticalAlignment="Top" Width="90" RenderTransformOrigin="0.5,0.5" Canvas.Top="1">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF8A8A8A" />
<GradientStop Color="#FF979797" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="74.313" VerticalAlignment="Top" Width="82.189" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.613" Canvas.Top="1.692">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#C0828080" Offset="0.797" />
<GradientStop Color="#FD000000" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-28.434" />
<SkewTransform AngleX="-2.144" />
<TranslateTransform X="-1.199" Y="0.649" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="75.491" VerticalAlignment="Top" Width="70.887" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="12.396" Canvas.Top="5.057">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00858585" Offset="0" />
<GradientStop Color="#1AFFFFFF" Offset="1" />
<GradientStop Color="#3FC2C2C2" Offset="0.349" />
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<SkewTransform CenterX="3" CenterY="-4" />
<RotateTransform Angle="-.7628" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="72.722" VerticalAlignment="Top" Width="72.936" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.631" Canvas.Top="4.896">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#9A909090" Offset="1" />
<GradientStop Color="Gray" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform Angle="-31.733"></RotateTransform>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="39" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="14.001" Canvas.Top="11.001">
<Ellipse.RenderTransform>
<SkewTransform CenterX="-8"></SkewTransform>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF898989" Offset="0" />
<GradientStop Color="#38777777" Offset="1" />
<GradientStop Color="#55676767" Offset="0.672" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M9.74935,11.916 L13.084,15.166 L12.1668,16.833 L11.3333,18.583 L10.4999,20.416 L9.24961,20.833 L6.99967,20.583 L6.75,18.333 L7.66697,15.333 L8.75037,12.916 z M3.6672,9.74999 L7.084,10.083 L5.75037,12.25 L4.66704,14 L4.33365,16.583 L4.25036,18.75 L4.41695,20.5 L0,20.166 L0.16699,16.916 L1.16693,13.833 L2.50016,11.583 z M18.1671,6.33301 L21.167,6.33301 L21.667,8.5 L20.75,9.75 L18.5841,10.833 L15.8337,13 L12.584,8.83301 L15.2502,7 z M20.917,0 L20.917,3.16601 L18.1674,2.99999 L15.8337,3.583 L13.5837,4.833 L11.3337,5.99999 L10.5003,6.416 L8.584,3.833 L11.0842,2.41601 L13.3341,0.833006 L16.417,0.166016 z" Fill="#99EEEEEE" HorizontalAlignment="Left" Height="20.833" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="21.667" Canvas.Left="18.166" Canvas.Top="15.917" />
<Canvas.RenderTransform>
<TranslateTransform x:Name="knobPosition" />
</Canvas.RenderTransform>
<Canvas.Resources>
<Storyboard x:Key="CenterKnob" Name="centerKnob" Completed="centerKnob_Completed">
<DoubleAnimation Storyboard.TargetName="knobPosition"
Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="knobPosition" Storyboard.TargetProperty="Y" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Canvas.Resources>
</Canvas>
</Canvas>
</Grid>
</Viewbox>
我用不同的方法解决了这个问题。现在下面的代码可以工作了。我还添加了键盘控制。
Xaml代码:
<Window x:Class="Joystick.MainWindow"
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:Joystick"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="1000" MouseMove="Ellipse_MouseMove" MouseLeftButtonUp="Joystick_MouseLeftButtonUp" KeyDown="Window_KeyDown">
<Grid Name="MainGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="KNOB POSITION"/>
<TextBlock Name="XTextBlock"/>
<TextBlock Name="YTextBlock"/>
<TextBlock Text="MOUSE POSITION"/>
<TextBlock Name="XMousePos"/>
<TextBlock Name="YMousePos"/>
<TextBlock Text="ANGEL / DISTANCE"/>
<TextBlock Name="Angle"/>
<TextBlock Name="Distance"/>
</StackPanel>
<Canvas Name="LayoutRoot" Grid.Column="1" Width="300" Height="300" Margin="0,234,444,235" >
<Ellipse Name="Joystick" Height="200" Canvas.Left="50" Canvas.Top="50" Stroke="Black" Width="200" >
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF2C2A2A" Offset="1" />
<GradientStop Color="#FF3A3737" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Height="100" Width="100" Canvas.Left="100" Canvas.Top="100">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Red" Offset="1" />
<GradientStop Color="#FF1D1D1D" />
<GradientStop Color="#FF323030" Offset="0.453" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Name="UpArrow" Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="Red" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="137.175" Canvas.Top="70"/>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="{Binding ElementName=UpArrow , Path=Fill}" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="70" Canvas.Top="137.175">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90"/>
<ScaleTransform ScaleX="-1"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="{Binding ElementName=UpArrow , Path=Fill}" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="230" Canvas.Top="137.175">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-90"/>
<ScaleTransform ScaleX="-1" ScaleY="-1"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path Name="DownArrow" Data="M205.75,65.625 L226.875,47.25 L248.5,65.625 z" Fill="{Binding ElementName=UpArrow , Path=Fill}" HorizontalAlignment="Left" Height="11.025" Width="25.65" Stretch="Fill" UseLayoutRounding="True" VerticalAlignment="Top" Canvas.Left="137.175" Canvas.Top="230">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform/>
<ScaleTransform ScaleY="-1"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Canvas x:Name="Knob" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50" Height="50" Canvas.Left="125" Canvas.Top="125" MouseDown="Knob_MouseDown" MouseUp="Knob_MouseUp">
<!--<Ellipse x:Name="Shadow" HorizontalAlignment="Left" Height="88" VerticalAlignment="Top" Width="86" Fill="#52131212" Canvas.Left="22" Canvas.Top="18" />-->
<Ellipse x:Name="KnobBase" HorizontalAlignment="Left" Height="50" VerticalAlignment="Top" Width="50" RenderTransformOrigin="0.5,0.5" Canvas.Top="1">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF8A8A8A" />
<GradientStop Color="#FF979797" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="38.795" VerticalAlignment="Top" Width="44.420" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.613" Canvas.Top="1.692">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#C0828080" Offset="0.797" />
<GradientStop Color="#FD000000" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-28.434" />
<SkewTransform AngleX="-2.144" />
<TranslateTransform X="-1.199" Y="0.649" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="39.636" VerticalAlignment="Top" Width="36.347" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="12.396" Canvas.Top="5.057">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00858585" Offset="0" />
<GradientStop Color="#1AFFFFFF" Offset="1" />
<GradientStop Color="#3FC2C2C2" Offset="0.349" />
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TransformGroup>
<SkewTransform CenterX="3" CenterY="-4" />
<RotateTransform Angle="-.7628" />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="37.658" VerticalAlignment="Top" Width="37.811" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="0.631" Canvas.Top="4.896">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#9A909090" Offset="1" />
<GradientStop Color="Gray" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform Angle="-31.733"></RotateTransform>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse HorizontalAlignment="Left" Height="12.142" VerticalAlignment="Top" Width="13.571" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="14.001" Canvas.Top="11.001">
<Ellipse.RenderTransform>
<SkewTransform CenterX="-8"></SkewTransform>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF898989" Offset="0" />
<GradientStop Color="#38777777" Offset="1" />
<GradientStop Color="#55676767" Offset="0.672" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M9.74935,11.916 L13.084,15.166 L12.1668,16.833 L11.3333,18.583 L10.4999,20.416 L9.24961,20.833 L6.99967,20.583 L6.75,18.333 L7.66697,15.333 L8.75037,12.916 z M3.6672,9.74999 L7.084,10.083 L5.75037,12.25 L4.66704,14 L4.33365,16.583 L4.25036,18.75 L4.41695,20.5 L0,20.166 L0.16699,16.916 L1.16693,13.833 L2.50016,11.583 z M18.1671,6.33301 L21.167,6.33301 L21.667,8.5 L20.75,9.75 L18.5841,10.833 L15.8337,13 L12.584,8.83301 L15.2502,7 z M20.917,0 L20.917,3.16601 L18.1674,2.99999 L15.8337,3.583 L13.5837,4.833 L11.3337,5.99999 L10.5003,6.416 L8.584,3.833 L11.0842,2.41601 L13.3341,0.833006 L16.417,0.166016 z" Fill="#99EEEEEE" HorizontalAlignment="Left" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Height="11.904" Width="15.476" Canvas.Left="12.166" Canvas.Top="12.917" />
</Canvas>
</Canvas>
</Grid>
变量
Point KeyjoystickPos = new Point();
double step = 0.01;
double stepMaxRange = 1.00;
double maxRange = 1.00;
public MainWindow()
{
InitializeComponent();
}
Ellipse_MoseMove
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
if (!Knob.IsMouseCaptured) return;
double joystickRadius = Joystick.Height * 0.5;
Vector joystickPos = e.GetPosition(Joystick) - new Point(joystickRadius, joystickRadius);
joystickPos /= joystickRadius;
double angle = Math.Atan2(joystickPos.Y, joystickPos.X);
if (e.LeftButton == MouseButtonState.Pressed)
{
if (joystickPos.Length > 1.0)
{
joystickPos.X = Math.Cos(angle);
joystickPos.Y = Math.Sin(angle);
}
UpdateKnobPosition(joystickPos.X , joystickPos.Y);
}
}
更新旋钮位置
private void UpdateKnobPosition(double joystickPosX ,double joystickPosY)
{
double fJoystickRadius = Joystick.Height * 0.5;
double fKnobRadius = Knob.Width * 0.5;
Canvas.SetLeft(Knob, Canvas.GetLeft(Joystick) +
joystickPosX * fJoystickRadius + fJoystickRadius - fKnobRadius);
Canvas.SetTop(Knob, Canvas.GetTop(Joystick) +
joystickPosY * fJoystickRadius + fJoystickRadius - fKnobRadius);
double distance = Math.Round(Math.Sqrt(joystickPosX * 100.00 * joystickPosX * 100.00 + joystickPosY * 100.00 * joystickPosY * 100.00) / 135.00 * 135.00);
var angle2 = Math.Atan2(joystickPosY, joystickPosX) * 180 / Math.PI;
if (angle2 > 0)
angle2 += 90;
else
{
angle2 = 270 + (180 + angle2);
if (angle2 >= 360) angle2 -= 360;
}
Angle.Text = angle2.ToString();
Distance.Text = distance.ToString();
XMousePos.Text = Convert.ToString(joystickPosX * 100);
YMousePos.Text = Convert.ToString(joystickPosY * 100);
}
Joystick_MouseLeftButtonUp
private void Joystick_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
double joystickRadius = Joystick.Height * 0.5;
Vector joystickPos = e.GetPosition(Joystick) -
new Point(joystickRadius, joystickRadius);
joystickPos /= joystickRadius;
joystickPos.X = 0;
joystickPos.Y = 0;
KeyjoystickPos.X = 0;
KeyjoystickPos.Y = 0;
UpdateKnobPosition(joystickPos.X , joystickPos.Y);
}
Knob_MouseDown
private void Knob_MouseDown(object sender, MouseButtonEventArgs e)
{
var Knob = (Canvas)sender;
Knob.CaptureMouse();
}
Knob_MouseUp
private void Knob_MouseUp(object sender, MouseButtonEventArgs e)
{
var Knob = (Canvas)sender;
Knob.ReleaseMouseCapture();
}
Window_KeyDown
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.W || e.Key == Key.Up)
{
KeyjoystickPos.Y = (KeyjoystickPos.Y <= -stepMaxRange) ? KeyjoystickPos.Y = -maxRange : KeyjoystickPos.Y = KeyjoystickPos.Y - step;
}
if (e.Key == Key.A || e.Key == Key.Left)
{
KeyjoystickPos.X = (KeyjoystickPos.X <= -stepMaxRange) ? KeyjoystickPos.X = -maxRange : KeyjoystickPos.X = KeyjoystickPos.X - step;
}
if (e.Key == Key.S || e.Key == Key.Down)
{
KeyjoystickPos.Y = (KeyjoystickPos.Y >= stepMaxRange) ? KeyjoystickPos.Y = maxRange : KeyjoystickPos.Y = KeyjoystickPos.Y + step;
}
if (e.Key == Key.D || e.Key == Key.Right)
{
KeyjoystickPos.X = (KeyjoystickPos.X >= stepMaxRange) ? KeyjoystickPos.X = maxRange : KeyjoystickPos.X = KeyjoystickPos.X + step;
}
if (e.Key == Key.Space)
{
KeyjoystickPos.X = 0;
KeyjoystickPos.Y = 0;
}
double angle = Math.Atan2(KeyjoystickPos.Y, KeyjoystickPos.X);
if(KeyjoystickPos.X*100 * KeyjoystickPos.X*100 + KeyjoystickPos.Y*100 * KeyjoystickPos.Y*100 > 100*100)
{
KeyjoystickPos.X = Math.Cos(angle);
KeyjoystickPos.Y = Math.Sin(angle);
}
UpdateKnobPosition(KeyjoystickPos.X, KeyjoystickPos.Y);
}