Задержка при попытке создать анимацию

#c# #wpf

#c# #wpf

Вопрос:

Я пытаюсь создать падающий шар с некоторой физической логикой, когда я запускаю программу, мяч движется медленно и неожиданно.

Я использую класс DispatcherTimer для рисования шара в каждом кадре.

это код

         private void Draw(MyEllipse s)
        {
            var h = Canvas.GetTop(s.Ellipse);
            if (h >= cnvs.ActualHeight) //reachs the bottom
            {
                s.Velocity = s.Velocity * -1;
                s.YLoc = cnvs.ActualHeight - 1;

            }
            else //go down
            {
                s.YLoc = s.YLoc   s.Velocity;
                s.Velocity  = PhysicsClass.Gravity;
            }
        }
  

Мой таймер отправки:

         private DispatcherTimer SetTimer()
        {
            var timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(16);
            timer.Start();
            return timer;
        }
  

Свойства MyElipse

        private Ellipse ellipse;
        public Ellipse Ellipse
        {
            get { return ellipse; }
            private set { ellipse = value; }
        }

        public double XLoc
        {
            get { return Canvas.GetLeft(Ellipse); }
            set { Canvas.SetLeft(Ellipse, value); }
        }


        public double YLoc
        {
            get { return Canvas.GetTop(Ellipse); }
            set { Canvas.SetTop(Ellipse, value); }
        }


        public double Velocity { get; set; }
  

Может ли быть так, что wpf не может отображать достаточно быстро?

Комментарии:

1. Вы должны написать пользовательскую анимацию. Сократите свои вычисления, выполнив одно получение свойств, таких как actualheight .

2. Я думаю, что, возможно, может сработать пользовательская функция смягчения. Один из стандартных, таких как exponentialease, может подойти достаточно близко, если вы просто хотите, чтобы анимация выглядела правдоподобно.

3. @Andy Я пытался сократить вычисления, но это не помогает, что странно, я помню, как делал более сложные анимации, и это все еще работало. Я мог бы попробовать функцию ослабления, но я все еще хочу понять, почему происходит это отставание

Ответ №1:

Хорошо, я исправил это, добавив к приоритету DispatcherTimer вот так

 var timer = new DispatcherTimer(DispatcherPriority.Send);
  

Комментарии:

1. Dispatchertimer на самом деле не подходит для такого рода целей. Он не предназначен для использования в качестве часов анимации. Точность каждого тика слишком приблизительна.

Ответ №2:

Вот альтернативная реализация.

Это быстрое и грязное доказательство концепции.

Mainwindow:

         SizeToContent="WidthAndHeight" 
        ContentRendered="Window_ContentRendered"
        >
    <Window.DataContext>
        <local:MainWindowViewmodel/>
    </Window.DataContext>
    <Grid>
        <Canvas x:Name="canvas"  Height="800" Width="800">
            <Ellipse Height="20" Width="20" Fill="Blue"
                     Canvas.Left="{Binding X}"
                     Canvas.Top="{Binding Y}"
                     />
        </Canvas>
    </Grid>
</Window>
  

Очень хакерский запуск потока:

     private void Window_ContentRendered(object sender, EventArgs e)
    {
        var mvm = this.DataContext as MainWindowViewmodel;
        mvm.SpinUp();
    }
  

Viewmodel ( nuget ядра prism).

 public class MainWindowViewmodel : BindableBase
{
    private double x = 200;
    public double X
    {
        get => x;
        set => SetProperty(ref x, value, nameof(X));
    }
    private double y = 0;
    public double Y
    {
        get => y;
        set => SetProperty(ref y, value, nameof(Y));
    }
    private const double MAX_HEIGHT = 790;
    private double speed = 0;
    private double acceleration = 0.0982;
    private async void animateBall()
    {
        while(1==1)
        {
            while (y < MAX_HEIGHT)
            {
                speed  = acceleration;
                Y  = speed;
                await Task.Delay(20);
            }
            Y = 0;
            await Task.Delay(1000);
            speed = 0;
        }
    }
    public void SpinUp()
    {
        new Thread(() =>
        {
            animateBall();
        }).Start();
    }
}
  

Я использую асинхронное ожидание, а не таймер диспетчера. Это должно дать более согласованные тайминги, поскольку используемый таймер является более точным, чем таймер диспетчеризации. Это также легче читать.

Обратите внимание, что уведомление об изменении значения привязки автоматически отправляется в поток пользовательского интерфейса.

Я жестко запрограммировал некоторые вещи, которые вы хотели бы вычислить один раз, например, ускорение силы тяжести.

Как я упоминал выше, это довольно грязный код, но он дает достаточно плавные результаты. При каждом повторении шарик зависает наверху из-за ожидания в течение 1 секунды.