C# 在wpf中旋转形状以跟随光标
我已经把大部分工作做好了。然而,这个问题发生在我的数学计算中。我希望箭头(用户控件)在wpf中“单击”画布时旋转指向光标。 我已经知道了如何用弧度计算角度。但是,当我应用该值时,它似乎没有按预期工作 当前 目标C# 在wpf中旋转形状以跟随光标,c#,wpf,xaml,C#,Wpf,Xaml,我已经把大部分工作做好了。然而,这个问题发生在我的数学计算中。我希望箭头(用户控件)在wpf中“单击”画布时旋转指向光标。 我已经知道了如何用弧度计算角度。但是,当我应用该值时,它似乎没有按预期工作 当前 目标 <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Canvas
MouseDown="Canvas_MouseDown"
MouseUp="Canvas_MouseUp"
MouseMove="Canvas_MouseMove"
Background="LightBlue">
<local:Arrow Canvas.Left="158" Canvas.Top="43"/>
<local:Arrow Canvas.Left="38" Canvas.Top="108"/>
<local:Arrow Canvas.Left="158" Canvas.Top="170"/>
<local:Arrow Canvas.Left="78" Canvas.Top="158"/>
<local:Arrow Canvas.Left="196" Canvas.Top="108"/>
<local:Arrow Canvas.Left="78" Canvas.Top="53"/>
</Canvas>
</Window>
<UserControl x:Class="WpfApplication1.Arrow"
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:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50">
<Grid>
<Path Data="M 0 4 L 4 0 L 8 4 Z" RenderTransformOrigin="0.5,0.5"
Width="50"
Height="50"
Stretch="Uniform"
Fill="Red">
<Path.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Path.RenderTransform>
</Path>
</Grid>
</UserControl>
重点关注的主要代码片段如下
private double Angle(Point origin, Point target)
{
//Calculate the distance from the square to the mouse's X and Y position
var radians = Math.Atan2(origin.Y - target.Y, origin.X - target.X);
var degrees = radians * (180 / Math.PI) - 90;
Console.WriteLine(target + "--" + origin + "--" + degrees);
return degrees;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
var canvas = (Canvas)sender;
if (e.LeftButton == MouseButtonState.Pressed)
{
if (_followMouse)
{
// Get Cursor Position
Point _targetPoint = e.GetPosition(this);
// Follow mouse
foreach (UIElement element in canvas.Children)
{
Arrow arrow = (Arrow)element;
// example 1
double x = Canvas.GetTop(arrow) + arrow.ActualWidth / 2.0;
double y = Canvas.GetLeft(arrow) + arrow.ActualHeight / 2.0;
Point _originPoint = new Point(x,y);
arrow.rotateTransform.Angle = Angle(_originPoint, _targetPoint);
}
}
}
}
整个项目代码如下 MainWindow.xaml.cs
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.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
bool _followMouse;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
_followMouse = true;
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
_followMouse = false;
}
private double Angle(Point origin, Point target)
{
//Calculate the distance from the square to the mouse's X and Y position
var radians = Math.Atan2(origin.Y - target.Y, origin.X - target.X);
var degrees = radians * (180 / Math.PI) - 90;
Console.WriteLine(target + "--" + origin + "--" + degrees);
return degrees;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
var canvas = (Canvas)sender;
if (e.LeftButton == MouseButtonState.Pressed)
{
if (_followMouse)
{
// Get Cursor Position
Point _targetPoint = e.GetPosition(this);
// Follow mouse
foreach (UIElement element in canvas.Children)
{
Arrow arrow = (Arrow)element;
// example 1
double x = Canvas.GetTop(arrow) + arrow.ActualWidth / 2.0;
double y = Canvas.GetLeft(arrow) + arrow.ActualHeight / 2.0;
Point _originPoint = new Point(x,y);
arrow.rotateTransform.Angle = Angle(_originPoint, _targetPoint);
}
}
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Documents;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;
使用System.Windows.Shapes;
命名空间WpfApplication1
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
bool_followMouse;
私有void Canvas_MouseDown(对象发送器,MouseButtonEventArgs e)
{
_followMouse=true;
}
私有void Canvas_MouseUp(对象发送器,MouseButtonEventArgs e)
{
_followMouse=false;
}
专用双角度(点原点、点目标)
{
//计算从正方形到鼠标X和Y位置的距离
var radians=Math.Atan2(origin.Y-target.Y,origin.X-target.X);
var度=弧度*(180/数学PI)-90;
控制台写入线(目标+“--”+原点+“--”+度);
返回度;
}
私有void Canvas_MouseMove(对象发送方,MouseEventArgs e)
{
var canvas=(canvas)发送方;
如果(e.LeftButton==鼠标按钮状态。按下)
{
if(_followMouse)
{
//获取光标位置
点_targetPoint=e.GetPosition(本);
//跟随鼠标
foreach(canvas.Children中的UIElement)
{
Arrow=(Arrow)元素;
//例1
double x=Canvas.GetTop(arrow)+arrow.ActualWidth/2.0;
双y=Canvas.GetLeft(箭头)+arrow.ActualHeight/2.0;
点_originPoint=新点(x,y);
arrow.rotateTransform.Angle=角度(_originPoint,_targetPoint);
}
}
}
}
}
}
main window.xaml
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Canvas
MouseDown="Canvas_MouseDown"
MouseUp="Canvas_MouseUp"
MouseMove="Canvas_MouseMove"
Background="LightBlue">
<local:Arrow Canvas.Left="158" Canvas.Top="43"/>
<local:Arrow Canvas.Left="38" Canvas.Top="108"/>
<local:Arrow Canvas.Left="158" Canvas.Top="170"/>
<local:Arrow Canvas.Left="78" Canvas.Top="158"/>
<local:Arrow Canvas.Left="196" Canvas.Top="108"/>
<local:Arrow Canvas.Left="78" Canvas.Top="53"/>
</Canvas>
</Window>
<UserControl x:Class="WpfApplication1.Arrow"
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:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50">
<Grid>
<Path Data="M 0 4 L 4 0 L 8 4 Z" RenderTransformOrigin="0.5,0.5"
Width="50"
Height="50"
Stretch="Uniform"
Fill="Red">
<Path.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Path.RenderTransform>
</Path>
</Grid>
</UserControl>
箭头.xaml
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Canvas
MouseDown="Canvas_MouseDown"
MouseUp="Canvas_MouseUp"
MouseMove="Canvas_MouseMove"
Background="LightBlue">
<local:Arrow Canvas.Left="158" Canvas.Top="43"/>
<local:Arrow Canvas.Left="38" Canvas.Top="108"/>
<local:Arrow Canvas.Left="158" Canvas.Top="170"/>
<local:Arrow Canvas.Left="78" Canvas.Top="158"/>
<local:Arrow Canvas.Left="196" Canvas.Top="108"/>
<local:Arrow Canvas.Left="78" Canvas.Top="53"/>
</Canvas>
</Window>
<UserControl x:Class="WpfApplication1.Arrow"
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:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50">
<Grid>
<Path Data="M 0 4 L 4 0 L 8 4 Z" RenderTransformOrigin="0.5,0.5"
Width="50"
Height="50"
Stretch="Uniform"
Fill="Red">
<Path.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Path.RenderTransform>
</Path>
</Grid>
</UserControl>
In:var angleInRadians=Math.Atan2(yDiff,xDiff)*180.0/Math.PI Math.Atan2已返回以弧度为单位的向量,因此不需要“*180.0/Math.PI”部分。如果您确实需要度,请将变量重命名为angle,而不是angleInRadian。我无法回答为什么这样做,但我从假设获取顶部和左侧是不正确的开始:您想要获取顶部中心(箭头的点)。另外,x应该是左边,y应该是顶部。然后添加一半宽度,使x成为中心,而不是左上角
double x = Canvas.GetLeft(arrow);
double y = Canvas.GetTop(arrow);
x += (.5*arrow.ActualWidth);
这让我们很接近。。。下一部分是我不确定它为什么有效的部分,但它似乎有效。也许有一个更精通数学的人能帮上忙。将角度增加90度:
arrow.rotateTransform.Angle = (angleInRadians+90);
我无意中发现了这一点,因为你计算的角度似乎不正确。。。通过分析迭代的第一个箭头的顶部和左侧的值,我得出结论,它是最顶部的。假设,单击左上角区域应该将其旋转270度左右,但我得到的值更接近180度。所以,我加了90,它似乎适用于任何光标位置,尽管我说我不知道为什么 这个问题本质上归结为找到两个向量之间的角度,即0角度的向量(即(0,-1))和从箭头到单击点的向量 数学是相当直接的,acos(DotProduct(v1,v2)),因为这是一个非常关键的等式,有很多关于为什么()的资源。但请注意,这会在向量之间提供一个无符号角度。要算出符号,应将符号设置为向量To的点积的符号和与0向量正交的向量的符号 用简单的(er)术语来说,想象一下应用于时钟臂的旋转,其中0角是12(由向量(0,-1)表示)。如果以正角度旋转,结果将具有正X值,因为臂尖将位于时钟中心的右侧(3点钟为(0,1))。向左旋转会得到负值(9点是(0,-1)。因此,如果你知道你想要的结果最终是在右边(因为你的X值是正的),那么你知道你旋转的角度应该是正的,反之亦然。这基本上是上述数学的简化视觉表示 任何人-代码
private double Angle(Point origin, Point target)
{
// Get the vector from origin->point
Vector vecTo = target - origin;
// Normalize the vector
vecTo.Normalize();
// 0-angle is pointing straight up, aligned with (0, -1).
// The equation for the angle between 2 vectors is acos(Dot(v1, v1))
// Our DotProduct is trivial, as know v1 is (0, -1). This exands
// 0 * v2.X + -1 * v2.Y
double dotAngle = -vecTo.Y;
double angle = Math.Acos(dotAngle);
// Convert to rad
angle = angle * 180 / Math.PI;
// ACos will always return a positive number, but because Cos is
// symmetric around 0 a -ve number is also valid, Figure out which
// is correct by taking the Dot vs (1, 0). If result is positive,
// then vecTo point in the same general direction as (1, 0), and
// the angle returned should also be positive. I've skipped
// all the actual math, but thats the idea.
if (vecTo.X > 0)
return angle;
else
return -angle;
}
另一个问题是,箭头的X&Y坐标是反向的,应该是:
double y = Canvas.GetTop(arrow) + arrow.ActualWidth / 2.0;
double x = Canvas.GetLeft(arrow) + arrow.ActualHeight / 2.0;
Point _originPoint = new Point(x,y);
每当移动到一个新的图形包时,计算出哪个方向是向上和向右都是值得的。如果找不到文档,只需在某个地方放置一个断点,然后单击左上角和右下角,看看有哪些坐标被划出。您确定转换是正确的吗