FrameBasedAnimation.axaml代码
<Window 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"Height="396" Width="463.2"x:Class="AvaloniaUI.FrameBasedAnimation"Title="FrameBasedAnimation"><Grid Margin="3" RowDefinitions="auto,*"><StackPanel Orientation="Horizontal"><Button Margin="3" Padding="3" Click="cmdStart_Clicked">Start</Button><Button Margin="3" Padding="3" Click="cmdStop_Clicked">Stop</Button></StackPanel><Canvas Name="canvas" Grid.Row="1" Margin="3"></Canvas></Grid> </Window>
FrameBasedAnimation.axaml.cs代码
using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Media; using Shares.Avalonia; using System; using System.Collections.Generic; using System.Linq;namespace AvaloniaUI; public class EllipseInfo {public Ellipse Ellipse { get; set; }public double VelocityY { get; set; }public bool HasStopped { get; set; } // 当前是否停止public int BouncesRemaining { get; set; } // 剩余弹跳次数public double MaxBounceSpeed { get; set; } // 最大反弹初速度public EllipseInfo(Ellipse ellipse, double velocityY, int bounces, double bounceSpeed){Ellipse = ellipse;VelocityY = velocityY;HasStopped = false;BouncesRemaining = bounces;MaxBounceSpeed = bounceSpeed;} } public partial class FrameBasedAnimation : Window {private readonly AnimationPlayer player = new AnimationPlayer();private readonly List<EllipseInfo> ellipses = new();private readonly Random rand = new Random();private double accelerationY = 0.1;private int minStartingSpeed = 1;private int maxStartingSpeed = 50;private double speedRatio = 0.1;private int minEllipses = 20;private int maxEllipses = 100;private int ellipseRadius = 10;public FrameBasedAnimation(){InitializeComponent();player.Duration = 8; // 整个动画总时间(秒),自动清屏player.At(0).PlayGlobal(progress =>{if (ellipses.Count == 0){player.Stop();return;}double bottomLimit = canvas.Bounds.Height - ellipseRadius * 2 - 10;foreach (var info in ellipses){double top = Canvas.GetTop(info.Ellipse);// 弹跳逻辑if (info.HasStopped){if (info.BouncesRemaining > 0 && top >= bottomLimit){// 随机弹起info.VelocityY = -rand.NextDouble() * info.MaxBounceSpeed;info.BouncesRemaining--;info.HasStopped = false; // 重新开始运动}else{Canvas.SetTop(info.Ellipse, bottomLimit);info.VelocityY = 0;continue;}}double newTop = top + info.VelocityY;info.VelocityY += accelerationY;if (newTop >= bottomLimit){Canvas.SetTop(info.Ellipse, bottomLimit);info.HasStopped = true;}else{Canvas.SetTop(info.Ellipse, newTop);}}});player.AnimationCompleted += () =>{ellipses.Clear();canvas.Children.Clear();};}private void cmdStart_Clicked(object? sender, RoutedEventArgs e){ellipses.Clear();canvas.Children.Clear();int halfCanvasWidth = (int)canvas.Bounds.Width / 2;int ellipseCount = rand.Next(minEllipses, maxEllipses + 1);for (int i = 0; i < ellipseCount; i++){var ellipse = new Ellipse{Fill = Brushes.LimeGreen,Width = ellipseRadius,Height = ellipseRadius};Canvas.SetLeft(ellipse, halfCanvasWidth + rand.Next(-halfCanvasWidth, halfCanvasWidth));Canvas.SetTop(ellipse, 0);canvas.Children.Add(ellipse);var info = new EllipseInfo(ellipse,speedRatio * rand.Next(minStartingSpeed, maxStartingSpeed),rand.Next(0, 3), // 随机弹跳次数 0~2rand.Next(1,4) //随机反弹速度1~3);ellipses.Add(info);}player.Start();}private void cmdStop_Clicked(object? sender, RoutedEventArgs e){player.Stop();ellipses.Clear();canvas.Children.Clear();} }
运行效果