当前位置: 首页 > news >正文

C# Avalonia 15- Animation- ImageWipe

在上一个AnimationPlayer例子上进行扩展,让其具备完整的小型动画功能。

AnimationPlayer类

    public partial class AnimationPlayer : ObservableObject{// --------------------- 内部类 ---------------------public class TimedAction{public double StartTime { get; set; }public double? EndTime { get; set; }public bool OneTime { get; set; }private bool executed = false;private double fps;internal Action<double>? localAction;internal Action<double>? globalAction;internal Action<double, double>? dualAction;public TimedAction(double startTime, double? endTime = null){StartTime = startTime;EndTime = endTime;}public TimedAction Once(){OneTime = true;return this;}public TimedAction Fps(double fps) {this.fps = fps;return this;}public TimedAction PlayLocal(Action<double> action){localAction = action;return this;}public TimedAction PlayGlobal(Action<double> action){globalAction = action;return this;}public TimedAction PlayDual(Action<double, double> action){dualAction = action;return this;}public bool IsActive(double elapsedSeconds){return elapsedSeconds >= StartTime &&(EndTime == null || elapsedSeconds <= EndTime.Value + 1 / fps);}public void TryExecute(double elapsedSeconds, double globalProgress, double totalDuration){if (OneTime && elapsedSeconds < StartTime){executed = false;}if (!IsActive(elapsedSeconds)) return;double actionEnd = EndTime ?? totalDuration;double localProgress = (elapsedSeconds - StartTime) / (actionEnd - StartTime);localProgress = Math.Clamp(localProgress, 0, 1);if (OneTime && executed) return;localAction?.Invoke(localProgress);globalAction?.Invoke(globalProgress);dualAction?.Invoke(localProgress, globalProgress);if (OneTime)executed = true;}public void Reset() => executed = false;}// --------------------- 字段 ---------------------private readonly DispatcherTimer timer = new DispatcherTimer();private readonly Stopwatch stopwatch = new Stopwatch();private TimeSpan elapsedOffset = TimeSpan.Zero; // 累计暂停/Seek时间private bool isRunning;// --------------------- 属性 ---------------------[ObservableProperty] private double _speed = 1.0;[ObservableProperty] private double _duration = 10.0;[ObservableProperty] private double _progress;[ObservableProperty] private string _timeText = "[[ stopped ]]";[ObservableProperty] private bool _canPause = false;[ObservableProperty] private bool _canResume = false;[ObservableProperty] private bool _canStop = false;[ObservableProperty] private bool _canSeek = false;[ObservableProperty] private double _fps = 0; private List<TimedAction> Actions { get; } = new();public event Action? AnimationCompleted;public AnimationPlayer(){Fps = 60;timer.Tick += (_, __) => UpdateProgress();}partial void OnFpsChanged(double value){timer.Interval = TimeSpan.FromMilliseconds(1000 / value);foreach (var action in Actions)action.Fps(value);}// --------------------- 链式添加动作 ---------------------public TimedAction At(double startTime, double? endTime = null){var action = new TimedAction(startTime, endTime).Fps(Fps);Actions.Add(action);return action;}// --------------------- 控制方法 ---------------------public void Start(){stopwatch.Restart();elapsedOffset = TimeSpan.Zero;isRunning = true;foreach (var action in Actions)action.Reset();timer.Start();UpdateStates();}public void Pause(){if (!CanPause) return;stopwatch.Stop();elapsedOffset += stopwatch.Elapsed;isRunning = false;timer.Stop();UpdateStates();}public void Resume(){if (!CanResume) return;stopwatch.Restart();isRunning = true;timer.Start();UpdateStates();}public void Stop(){if (!CanStop) return;isRunning = false;timer.Stop();stopwatch.Reset();elapsedOffset = TimeSpan.Zero;Progress = 0;TimeText = "[[ stopped ]]";foreach (var action in Actions)action.Reset();UpdateStates();}public void Seek(double seconds){if (!CanSeek) return;seconds = Math.Clamp(seconds, 0, Duration);elapsedOffset = TimeSpan.FromSeconds(seconds / Speed);       stopwatch.Restart();UpdateProgress();}// --------------------- 更新方法 ---------------------private void UpdateProgress(){double elapsedSeconds = (stopwatch.Elapsed + elapsedOffset).TotalSeconds * Speed;if (elapsedSeconds >= Duration){elapsedSeconds = Duration;isRunning = false;timer.Stop();AnimationCompleted?.Invoke();}Progress = Math.Clamp(elapsedSeconds / Duration, 0, 1);TimeText = TimeSpan.FromSeconds(elapsedSeconds).ToString(@"hh\:mm\:ss\.fff");foreach (var timedAction in Actions)timedAction.TryExecute(elapsedSeconds, Progress, Duration);UpdateStates();}// --------------------- 状态更新 ---------------------private void UpdateStates(){CanPause = isRunning;CanResume = !isRunning && Progress > 0 && Progress < 1;CanStop = isRunning || (Progress > 0 && Progress < 1);CanSeek = Progress > 0 && Progress < 1; }}

ImageWipe.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="250" Width="300"x:Class="AvaloniaUI.ImageWipe"Title="ImageWipe"><Grid RowDefinitions="auto,auto"><Grid><Image Source="avares://AvaloniaUI/Resources/Images/night.jpg"/><Image Source="avares://AvaloniaUI/Resources/Images/day.jpg" Name="imgDay"/></Grid><Button Grid.Row="1" Content="Start" HorizontalAlignment="Center" Click="Button_Click"/></Grid>
</Window>

ImageWipe.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using Shares.Avalonia;
using System.Net;namespace AvaloniaUI;public partial class ImageWipe : Window
{private readonly AnimationPlayer player = new AnimationPlayer() { Duration = 5 };private GradientStop transparentStop = new GradientStop() { Color = Colors.Transparent };private GradientStop visibleStop = new GradientStop() { Color = Colors.Black };public ImageWipe(){InitializeComponent();player.At(0).PlayLocal(p =>{transparentStop.Offset = p;visibleStop.Offset = p + 0.2;imgDay.OpacityMask = new LinearGradientBrush{StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),EndPoint = new RelativePoint(1, 0, RelativeUnit.Relative),GradientStops = new GradientStops { transparentStop, visibleStop }};});}private void Button_Click(object? sender, RoutedEventArgs e){player.Start();}
}

运行效果

 

image

 

http://www.hskmm.com/?act=detail&tid=15726

相关文章:

  • 题解:P8067 [BalkanOI 2012] balls
  • 题解:P8300 [COCI 2012/2013 #2] INSPEKTOR
  • SuperHarness-3D低压柜机电协同设计方案!
  • 详细介绍:.NET驾驭Word之力:打造专业文档 - 页面设置与打印控制完全指南
  • 使用.NET标准库实现多任务并行处理的详细过程 - 实践
  • 模型训练中 平均损失值和平均准确率的深入理解
  • torch.max函数在分类问题中的使用 学习
  • godot3.6字典遍历
  • 国产DevOps工具链崛起:Gitee领衔的本土化技术生态全景解读
  • 安装 elasticsearch-9.1.4的 IK分词器
  • react性能优化
  • 从研发效能到知识中枢:Gitee Wiki如何重塑企业知识管理范式
  • Gitee DevSecOps平台:军工软件研发的智能化革命
  • 杆状病毒表达系统为何成为蛋白表达首选
  • 日记3
  • Gitee如何重塑中国开发者的代码托管体验
  • 模块化面向对象 2章
  • css `isolation: isolate` - 详解
  • Debezium + Kafka + Flink/Doris Stream Load 实时数仓
  • Gitee DevOps平台:中国企业数字化转型的代码管理新范式
  • Ansible + Docker 部署 Zookeeper 集群
  • 幂运算与航班中转的奇妙旅行:探索算法世界的两极 - 实践
  • Gemini CLI 配置问题
  • 本土化与全球化博弈下的项目管理工具选型:Gitee如何为中国企业破局?
  • 论Linux安装后需要进行的配置
  • 51单片机-驱动DS1302时钟芯片模块教程 - 实践
  • tomato WP复盘
  • SQLite的并发问题
  • 域渗透靶场-vulntarget-a综合靶场
  • 数组和链表读取、插入、删除以及查找的区别