Программная интерполяция значений свойств в WPF с использованием раскадровки, часов и анимации - PullRequest
0 голосов
/ 04 июля 2019

Я работаю над WPF-приложением .NET Core 3.0, которое используется для разработки анимации освещения для пользовательской аппаратной платформы.

Аппаратное обеспечение само принимает пакеты UDP, содержащие сообщения об изменении освещения (представьте, что это данные DMX через IP), а приложение имеет внутреннюю очередь сообщений для обработки всех приоритетов сообщений, дедупликации, управляющих сообщений и т. Д. приложение в основном построено внутри пользовательского элемента управления, который реализует график временной шкалы на основе ключевых кадров.

Одна из вещей, которые мне нужны в этом приложении, - это способ превратить набор данных узла ключевого кадра в реальные значения для отправки. Для простой пошаговой или линейной интерполяции значений это довольно тривиально, но я бы хотел иметь доступ к другим режимам интерполяции. Я обнаружил, что WPF содержит ряд классов, используемых для анимации свойств, в частности AnimationTimeline и его потомков, Storyboard, Clock и DependencyProperty классов.

Я поиграл с AnimationTimeline (в частности, ByteAnimation) и получил работающий код, который успешно анимировал свойство. Однако этот код использовал this.ApplyAnimationClock() из окна WPF, и я хочу просто запустить код независимо от окна и вручную запросить контроллер часов. Кроме того, этот код поддерживает только одну анимацию. Что мне нужно, так это уметь составлять последовательность анимаций, одну за другой, чтобы получать правильные анимированные значения в любой момент времени. К сожалению, я не смог заставить это работать с Storyboard, что из документации, которую я прочитал, кажется правильным способом сделать это.

Вот код, который у меня есть:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ByteAnimation anim1 = new ByteAnimation((byte)0, (byte)255, new Duration(TimeSpan.FromSeconds(10)));
        ByteAnimation anim2 = new ByteAnimation((byte)0, (byte)255, new Duration(TimeSpan.FromSeconds(10)));
        anim1.EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut };
        anim2.EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut };
        anim2.BeginTime = TimeSpan.FromSeconds(10);

        Storyboard.SetTarget(anim1, this);
        Storyboard.SetTargetProperty(anim1, new PropertyPath(ByteValueProperty));
        Storyboard.SetTarget(anim2, this);
        Storyboard.SetTargetProperty(anim2, new PropertyPath(ByteValueProperty));

        var sb = new Storyboard();
        sb.Children.Add(anim1);
        sb.Children.Add(anim2);
        var clock = sb.CreateClock();
        sb.Begin();

        clock.Controller.Begin();
        clock.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(0), TimeSeekOrigin.BeginTime);
        MessageBox.Show("Value: " + ByteValue);
        clock.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(5), TimeSeekOrigin.BeginTime);
        MessageBox.Show("Value: " + ByteValue);
        clock.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(10), TimeSeekOrigin.BeginTime);
        MessageBox.Show("Value: " + ByteValue);
        clock.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(15), TimeSeekOrigin.BeginTime);
        MessageBox.Show("Value: " + ByteValue);
    }

    private static readonly DependencyProperty ByteValueProperty = DependencyProperty.Register("ByteValue", typeof(byte), typeof(MainWindow), new PropertyMetadata((byte)16));

    public byte ByteValue
    {
        get => (byte)GetValue(ByteValueProperty);
        set => SetValue(ByteValueProperty, (byte)value);
    }
}

Я ожидаю, что в окнах сообщений будут отображаться 0, 127, 255, 127. Вместо этого они просто показывают 16 (значение по умолчанию свойства). Я не могу понять, почему анимация здесь не работает.

Я тоже пытался использовать NamedScope с Storyboard.SetTargetName, но это либо вызвало исключения из-за несуществующего свойства, либо оно не сработало так же, как указано выше.

Возможно, это проблема X / Y, и я вообще не хочу раскадровку. Я не знаю. По сути, я хочу, чтобы у меня была возможность создавать набор AnimationTimeline экземпляров для каждого канала освещения, с каждой анимацией, бегущей вплотную, чтобы создать последовательность, таким образом, чтобы я мог определить правильное значение освещения для любой точки. во время. В качестве примера того, какой код будет идеальным:

var sb = SomeMagicStoryboard<ByteAnimation>();

var anim1 = new ByteAnimation((byte)0, (byte)255, new Duration(TimeSpan.FromSeconds(10)));
var anim2 = new ByteAnimation((byte)0, (byte)255, new Duration(TimeSpan.FromSeconds(10)));
anim1.EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut };
anim2.EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut };
anim2.BeginTime = TimeSpan.FromSeconds(10);

sb.AddAnimation(anim1);
sb.AddAnimation(anim2);

byte value = sb.GetValue(1500); /* get animation value at 1500ms */

Есть идеи о том, что я могу делать неправильно или что я могу сделать, чтобы добиться того, чего я хочу?

...