跟随鼠标指针移动的对象

5
我正在开发一款Wpf应用程序,我创建了一个眼睛形状的控件,在Canvas中放置了一个椭圆(眼睛),我的目的是当鼠标指针进入Canvas时,椭圆跟随鼠标指针移动。 您有什么建议如何完成这个任务吗? 非常感谢您的关注。
祝好!
编辑:
我已经在Xaml中更新了我的代码:
<Window Height="480" Title="Window2" Width="640" x:Class="WpfApplication5.Window2"
   x:Name="Window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Window.Resources>
    <Storyboard x:Key="OnLoaded1">
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ctrCircle" 
        Storyboard.TargetProperty="(UIElement.RenderTransform).( TransformGroup.Children)[3].(TranslateTransform.X)">
        <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1">
          <EasingDoubleKeyFrame.EasingFunction>
            <ExponentialEase EasingMode="EaseOut" />
          </EasingDoubleKeyFrame.EasingFunction>
      </EasingDoubleKeyFrame>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ctrCircle" 
          Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
        <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1">
        <EasingDoubleKeyFrame.EasingFunction>
            <ExponentialEase EasingMode="EaseOut" />
          </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Style TargetType="Ellipse">
      <Setter Property="RenderTransform">
        <Setter.Value>
          <ScaleTransform ScaleX="1" ScaleY="1"/>
        </Setter.Value>
      </Setter>
      <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
    </Style>
  </Window.Resources>

  <Canvas MouseMove="mov" x:Name="LayoutRoot">
    <Border ackground="Black" B="" Canvas.Left="178" Canvas.Top="103" 
      CornerRadius="250" Height="255.5" Width="290" x:Name="border_eye">
      <Ellipse Fill="#FFFFC600" Height="12" HorizontalAlignment="Left" 
        Margin="0" RenderTransformOrigin="0.5,0.5" Stroke="{x:Null}" 
        VerticalAlignment="Center" Visibility="Visible" Width="12" x:Name="ctrCircle">
        <Ellipse.RenderTransform>
          <TransformGroup>
            <ScaleTransform />
            <SkewTransform />
            <RotateTransform />
            <TranslateTransform />                 
          </TransformGroup>
        </Ellipse.RenderTransform>
      </Ellipse>
    </Border>
  </Canvas>
</Window>

在代码后台:

private void mov(object sender, MouseEventArgs e)
    {
 System.Windows.Point pt = e.GetPosition((Canvas)sender);
      Storyboard invokeStoryboard = this.Resources["OnLoaded1"] as Storyboard;
        ((DoubleAnimationUsingKeyFrames)invokeStoryboard.Children[0]).KeyFrames[0].Value = pt.X;
        ((DoubleAnimationUsingKeyFrames)invokeStoryboard.Children[1]).KeyFrames[0].Value = pt.Y;
        invokeStoryboard.Begin();
    }

现在我的目的是当我在Canvas区域(LayoutRoot)移动鼠标时,椭圆形(ctrCircle)只能在Border(border_eye)内移动,并且不能超出"border_eye"的区域。这种效果类似于眼睛。
你有什么想法可以帮助解决这个问题吗?
非常感谢。
祝您有愉快的一天。
干杯!
3个回答

7
这里是使用Rx框架在WPF画布中绘制眼睛的示例。使用Rx而不是直接附加到鼠标移动事件可以缓冲事件,并且每10毫秒仅更新瞳孔位置,从而减少CPU负载。

Xaml代码:

<UserControl
    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"
    mc:Ignorable="d"
    x:Class="namespace.EyeDemo"
    x:Name="UserControl"
    d:DesignWidth="640" d:DesignHeight="480">

    <Canvas x:Name="LayoutRoot" Background="GreenYellow">
        <Ellipse Fill="Black" Width="120" Height="70" Canvas.Left="90" Canvas.Top="115"/>
        <Ellipse x:Name="Eye" Fill="Black" Width="100" Height="50" Canvas.Left="100" Canvas.Top="125"/>
        <Ellipse x:Name="Pupil" Fill="Red" Height="20" Canvas.Left="139" Canvas.Top="138" Width="20"/>
    </Canvas>
</UserControl>

代码背后的实现

/// <summary>
/// Interaction logic for EyeDemo.xaml
/// </summary>
public partial class EyeDemo : UserControl
{
    public EyeDemo()
    {
        this.InitializeComponent();

        double majorRadius = Eye.Width / 2d;
        double minorRadius = Eye.Height / 2d;
        Point center = new Point( Canvas.GetLeft( Eye ) + majorRadius, Canvas.GetTop( Eye ) + minorRadius );

        // create event streams for mouse down/up/move using reflection
        // to keep taking mouse move events and return the X, Y positions
        var mouseMove = from evt in Observable.FromEvent<MouseEventArgs>( LayoutRoot, "PreviewMouseMove" )
                        select (Point?)evt.EventArgs.GetPosition( this );

        // subscribe to the stream of position changes and modify the Canvas.Left and Canvas.Top
        // use the bound by elipse function to restrain the pupil to with the eye.
        mouseMove.BufferWithTime( TimeSpan.FromMilliseconds( 10 ) ).Select( p => BoundByElipse( majorRadius, minorRadius, center, p.LastOrDefault() ) )
            .ObserveOnDispatcher().Subscribe( pos =>
              {
                  if( pos.HasValue )
                  {
                      Canvas.SetLeft( Pupil, pos.Value.X - Pupil.Width / 2d );
                      Canvas.SetTop( Pupil, pos.Value.Y - Pupil.Height / 2d );
                  }
              } );
    }

    private Point? BoundByElipse( double majorRadius, double minorRadius, Point center, Point? point )
    {
        if( point.HasValue )
        {
            // Formular for an elipse is  x^2 / a^2 + y^2 / b^2 = 1
            // where a = majorRadius and b = minorRadius
            // Using this formular we can work out if the point is with in the elipse 
            // or find the boundry point closest to the point

            // Find the location of the point relative to the center.
            Point p = new Point( point.Value.X - center.X, point.Value.Y - center.Y );
            double a = majorRadius;
            double b = minorRadius;

            double f = p.X * p.X / (a * a) + p.Y * p.Y / (b * b);
            if( f <= 1 )
            {
                // the point is with in the elipse;
                return point;
            }
            else
            {
                // the point is outside the elipse, therefore need to find the closest location on the boundry.
                double xdirection = point.Value.X > center.X ? 1 : -1;
                double ydirection = point.Value.X > center.X ? 1 : -1;

                double r = p.X / p.Y;

                double x = p.Y != 0 ? Math.Sqrt( r * r * a * a * b * b / (r * r * b * b + a * a) ) : a;
                double y = r != 0 ? x / r : (point.Value.Y > center.Y ? -b : b);

                return new Point( center.X + xdirection * x, center.Y + ydirection * y );
            }
        }
        else
        {
            return null;
        }
    }
}

2

使用MouseMove事件并确保Canvas有背景进行命中测试。

XAML:

<Canvas MouseMove="Canvas_MouseMove"
        Background="Transparent">
    <Ellipse x:Name="eye"
             Width="50"
             Height="20"
             Fill="Blue" />
</Canvas>

代码后台:

private void Canvas_MouseMove( object sender, System.Windows.Input.MouseEventArgs e )
{
   var pos = e.GetPosition((Canvas)sender);
   Canvas.SetLeft(eye, pos.X);
   Canvas.SetTop(eye, pos.Y);
}

谢谢Chris,我忘记写下一个细节了,眼睛停留在另一个组件中,我写下了代码:<Canvas_MouseMove="Canvas_MouseMove" x:Name="main_canvas" Width=500" Height="500" Background="Transparent"> <Canvas Name="canvas_Eye" Margin="120"> <Ellipse x:Name="eye" Width="50" Height="20" Fill="Blue" /></Canvas> </Canvas> ...所以我希望当鼠标进入mainCanvas时,眼睛可以在canvas_eye内移动,而不会超出canvas_eye的边界。之前我的解释不好,希望你能理解我的任务要点。 - JayJay
看起来JayJay也想在XAML中定义一个8秒的缓动延迟。 - bitbonk

1
你的方法几乎是正确的,但在动画使用后不能修改动画属性。你需要使用Clone()创建一个新的动画并进行修改,移除旧动画,然后应用新修改的动画。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接