同样用两种方式实现动画,各位自行选择。实现了一个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();
        
    }
}

运行效果

image

 

posted on 2025-09-17 13:06  dalgleish  阅读(17)  评论(0)    收藏  举报