同样用两种方式实现动画,各位自行选择。实现了一个ArithmeticConverter类。
ArithmeticConverter.cs类
using Avalonia.Data.Converters;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shares.Avalonia
{
public class ArithmeticConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value == null) return 0.0;
if (!double.TryParse(value.ToString(), out double input)) return value;
if (parameter == null) return input;
string[] ops = parameter.ToString()!.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var op in ops)
{
input = ApplyOperation(input, op.Trim());
}
return input;
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value == null) return 0.0;
if (!double.TryParse(value.ToString(), out double input)) return value;
if (parameter == null) return input;
string[] ops = parameter.ToString()!.Split(new[] { ',', ';',':'}, StringSplitOptions.RemoveEmptyEntries);
// 倒序执行逆运算
for (int i = ops.Length - 1; i >= 0; i--)
{
input = ApplyOperationBack(input, ops[i].Trim());
}
return input;
}
private double ApplyOperation(double input, string op)
{
try
{
if (op.StartsWith("+")) return input + double.Parse(op[1..]);
if (op.StartsWith("-")) return input - double.Parse(op[1..]);
if (op.StartsWith("*")) return input * double.Parse(op[1..]);
if (op.StartsWith("/")) return double.Parse(op[1..]) != 0 ? input / double.Parse(op[1..]) : 0;
if (op.StartsWith("^"))
{
double power = double.Parse(op[1..]);
// 负数的非整数幂检查
if (input < 0 && power % 1 != 0)
{
Console.WriteLine($"警告:负数底数 {input} 与非整数指数 {power} 会产生 NaN");
return double.NaN;
}
return Math.Pow(input, power);
}
if (op == "√") return input >= 0 ? Math.Sqrt(input) : double.NaN;
if (op == "abs") return Math.Abs(input);
if (op == "neg") return -input;
if (op == "floor") return Math.Floor(input);
if (op == "ceil") return Math.Ceiling(input);
if (op == "round") return Math.Round(input);
// 三角函数(度数模式)
if (op == "sin") return Math.Sin(input * Math.PI / 180);
if (op == "cos") return Math.Cos(input * Math.PI / 180);
if (op == "tan")
{
double angleMod = input % 180;
if (Math.Abs(angleMod - 90) < 1e-10)
{
Console.WriteLine($"警告:tan({input}) 接近 90 + k*180 度奇点");
return double.NaN;
}
return Math.Tan(input * Math.PI / 180);
}
if (op == "asin")
{
if (input < -1 || input > 1)
{
Console.WriteLine($"警告:asin({input}) 超出定义域 [-1,1]");
return double.NaN;
}
return Math.Asin(input) * 180 / Math.PI;
}
if (op == "acos")
{
if (input < -1 || input > 1)
{
Console.WriteLine($"警告:acos({input}) 超出定义域 [-1,1]");
return double.NaN;
}
return Math.Acos(input) * 180 / Math.PI;
}
if (op == "atan") return Math.Atan(input) * 180 / Math.PI;
// 对数与指数
if (op == "ln") return input > 0 ? Math.Log(input) : double.NaN;
if (op.StartsWith("log"))
{
double baseVal = 10;
if (op.Length > 3) double.TryParse(op[3..], out baseVal);
return input > 0 ? Math.Log(input, baseVal) : double.NaN;
}
if (op == "exp") return Math.Exp(input);
}
catch (Exception ex)
{
Console.WriteLine($"警告:ApplyOperation错误信息为{ex.Message}");
}
return input;
}
private double ApplyOperationBack(double input, string op)
{
try
{
if (op.StartsWith("+")) return input - double.Parse(op[1..]);
if (op.StartsWith("-")) return input + double.Parse(op[1..]);
if (op.StartsWith("*"))
{
double factor = double.Parse(op[1..]);
if (factor != 0) return input / factor;
}
if (op.StartsWith("/"))
{
double factor = double.Parse(op[1..]);
return input * factor;
}
if (op.StartsWith("^"))
{
double power = double.Parse(op[1..]);
if (input < 0 && power % 1 != 0)
{
Console.WriteLine($"警告:负数底数 {input} 与非整数指数 {power} 会产生 NaN");
return double.NaN;
}
if (power != 0) return Math.Pow(input, 1 / power);
}
if (op == "√") return input * input;
if (op == "neg") return -input;
if (op == "abs" || op == "floor" || op == "ceil" || op == "round")
{
Console.WriteLine($"警告:操作 '{op}' 不可逆,返回当前值");
return input;
}
// 三角函数逆运算(度数模式)
if (op == "sin") return Math.Asin(input) * 180 / Math.PI;
if (op == "cos") return Math.Acos(input) * 180 / Math.PI;
if (op == "tan")
{
double angleRad = Math.Atan(input);
double angleDeg = angleRad * 180 / Math.PI;
return angleDeg;
}
if (op == "asin") return Math.Sin(input * Math.PI / 180);
if (op == "acos") return Math.Cos(input * Math.PI / 180);
if (op == "atan") return Math.Tan(input * Math.PI / 180);
// 对数与指数逆运算
if (op == "ln") return Math.Exp(input);
if (op.StartsWith("log"))
{
double baseVal = 10;
if (op.Length > 3) double.TryParse(op[3..], out baseVal);
return Math.Pow(baseVal, input);
}
if (op == "exp") return Math.Log(input);
}
catch (Exception ex)
{
Console.WriteLine($"警告:ApplyOperationBack错误信息为{ex.Message}");
}
return input;
}
}
}
XamlAnimation.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="300" Width="300" x:Class="AvaloniaUI.XamlAnimation" Title="XamlAnimation" Name="window"> <Window.Resources> <ArithmeticConverter x:Key="converter"/> </Window.Resources> <Window.Styles> <Style Selector="Button.Animation1"> <Setter Property="Height" Value="40"/> <Setter Property="Width" Value="160"/> <Style Selector="^:pointerover"> <Style.Animations> <Animation Duration="0:0:5" FillMode="None"> <KeyFrame Cue="100%"> <Setter Property="Width" Value="{Binding #window.Width, Converter={StaticResource converter}, ConverterParameter=-100}"/> <Setter Property="Height" Value="{Binding #window.Height, Converter={StaticResource converter}, ConverterParameter=-100;/3}"/> </KeyFrame> </Animation> </Style.Animations> </Style> </Style> <Style Selector="Button.Animation2"> <Setter Property="Height" Value="40"/> <Setter Property="Width" Value="160"/> <Setter Property="Transitions"> <Transitions> <DoubleTransition Property="Width" Duration="0:0:5"/> <DoubleTransition Property="Height" Duration="0:0:5"/> </Transitions> </Setter> <Style Selector="^:pointerover"> <Setter Property="Width" Value="{Binding #window.Width, Converter={StaticResource converter}, ConverterParameter=-100}"/> <Setter Property="Height" Value="{Binding #window.Height, Converter={StaticResource converter}, ConverterParameter=-100;/3}"/> </Style> </Style> </Window.Styles> <Button Padding="10" Classes="Animation2" HorizontalAlignment="Center" VerticalAlignment="Center" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="Pointer over and Make Me Grow"> </Button> </Window>
XamlAnimation.axaml.cs代码
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace AvaloniaUI;
public partial class XamlAnimation : Window
{
public XamlAnimation()
{
InitializeComponent();
}
}
运行效果

浙公网安备 33010602011771号