ReusableFollowExample.axaml代码

<UserControl xmlns="https://github.com/avaloniaui"
             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" d:DesignWidth="800" d:DesignHeight="450"
               xmlns:local="using:AvaloniaUI"
             x:Class="AvaloniaUI.ReusableFollowExample">
    <Canvas Background="Transparent">
        <!-- 红色方块 -->
        <local:FollowMouseCanvas Canvas.Left="0" Canvas.Top="0" Width="50" Height="50" Background="Red">
            <!-- 可以在这里放更多内容 -->
        </local:FollowMouseCanvas>

        <!-- 绿色方块 -->
        <local:FollowMouseCanvas Canvas.Left="300" Canvas.Top="0" Width="50" Height="50" Background="Green">
        </local:FollowMouseCanvas>

        <!-- 蓝色方块 -->
        <local:FollowMouseCanvas Canvas.Left="0" Canvas.Top="300" Width="50" Height="50" Background="Blue">
        </local:FollowMouseCanvas>
    </Canvas>
</UserControl>

ReusableFollowExample.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.VisualTree;
using Shares.Avalonia;
using System;

namespace AvaloniaUI;

public class FollowMouseCanvas : Canvas
{
    private Vector velocity;
    private Point lastMouse;
    private Canvas? parentCanvas;
    private readonly AnimationPlayer player = new AnimationPlayer();

    public FollowMouseCanvas()
    {
        player.Fps = 60;
        player.Loop = true;
        player.Duration = double.MaxValue;
        player.At(0).PlayGlobal(UpdatePosition);
        this.AttachedToVisualTree += (_, __) =>
        {
            EnsureParent();
            player.Start();
        };
    }

    private void EnsureParent()
    {
        parentCanvas = this.GetVisualParent() as Canvas;
        if (parentCanvas != null)
        {
            parentCanvas.PointerMoved += OnPointerMoved;
        }
    }

    private void OnPointerMoved(object? sender, PointerEventArgs e)
    {
        if (parentCanvas != null)
            lastMouse = e.GetPosition(parentCanvas);
    }

    private void UpdatePosition(double progress)
    {
        if (parentCanvas == null)
            return;

        double left = Canvas.GetLeft(this);
        double top = Canvas.GetTop(this);
        if (double.IsNaN(left)) left = 0;
        if (double.IsNaN(top)) top = 0;

        var location = new Point(left, top);
        var toMouse = lastMouse - location;

        // 根据 progress 做周期性调制,比如让速度在一个周期中变化
        double followForce = 0.01 + 0.005 * Math.Sin(progress * 2 * Math.PI * 2); // 2Hz波动
        double drag = 0.8 + 0.1 * Math.Sin(progress * 2 * Math.PI); // 阻尼轻微变化

        velocity += toMouse * followForce;
        velocity *= drag;
        location += velocity;

        Canvas.SetLeft(this, location.X);
        Canvas.SetTop(this, location.Y);
    }
}
public partial class ReusableFollowExample : UserControl
{
    public ReusableFollowExample()
    {
        InitializeComponent();
    }
}

运行效果

image

 

posted on 2025-10-22 09:43  dalgleish  阅读(2)  评论(0)    收藏  举报