Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Enhancement] Better animations #6999

Closed
adrianknight89 opened this issue Jul 29, 2019 · 14 comments
Closed

[Enhancement] Better animations #6999

adrianknight89 opened this issue Jul 29, 2019 · 14 comments

Comments

@adrianknight89
Copy link
Contributor

Summary

The current Animation implementation seems complex and performs poorly when multiple non-trivial views are animated (such as translating two ListViews at the same time). There should be a new way for us to animate objects by taking advantage of hardware acceleration and avoiding things like a timer.

I can't find a good substitute for the current implementation. I've looked at Xamarin Transitions, but the project is not actively being maintained and doesn't seem to work as of now.

@jfversluis
Copy link
Member

Would #4233 help with that?

@adrianknight89
Copy link
Contributor Author

It's not clear to me if Storyboard is building on top of the existing animation implementation. It sounds like that's the case. If it is true, then Storyboard will have the same issues. We need to be able to leverage hardware acceleration (see this post) and use native animation APIs as much as possible. The current implementation was written back in 2012 and needs to be re-thought, imo.

@samhouts samhouts added the a/animation 🎬 Animation stuff label Jul 29, 2019
@jrahma
Copy link

jrahma commented Aug 3, 2019

I would say it should apply to all form navigation so it will look like this:

Navigation.PushModalAsync(new NavigationPage(new MyForm()), Transition.SlideFromTop, 2000);

Where 2000 is the time to take in milliseconds

The Transition can be:

  • SlideFropTop
  • SlideFropButtom
  • SlideFropRight
  • SlideFropLeft
  • Scale
  • Zoom
  • Fade

The same thing can e applied to OnDisappearing,

for example below I am opening the page using SlideFromLeft:

Navigation.PushModalAsync(new NavigationPage(new MyForm()), Transition.SlideFromLeft(2000));

and closing it using Fade:

protected override void OnDisappearing()
{
     base.OnDisappearing();

     Transition.Fade(2000);
}

Thanks,
Jassim

@adrianknight89
Copy link
Contributor Author

@jrahma I believe there is an existing issue / PR for what you need. This issue is different in that I want a better animation framework that hands off as much work as possible to the native layers to do basic translate, scale, fade, etc. So, when I do something like:

await Task.WhenAll
(
      view1.TranslateTo (-100, 0),
      view2.TranslateTo (0, 0)
);

this animation should be smooth when view1 and view2 are complex instead of simple box views or buttons.

I've recently created a simple, hacky dependency service that animates views for me. The animations are 10x better compared to using Xamarin Forms' animation framework. I directly use ObjectAnimator on Android and UIViewPropertyAnimator on iOS. It does the job for me for my use case, but it skips a lot of visual element tracking Forms does, so it's not for production use. :)

@raindeer
Copy link

raindeer commented Oct 1, 2019

@adrianknight89 would you care to share your hacky solution?

@adrianknight89
Copy link
Contributor Author

@raindeer I'll get back to you on this later. I'm a bit busy right now with work and other things. I hope to update you back this weekend.

@adrianknight89
Copy link
Contributor Author

adrianknight89 commented Oct 16, 2019

@raindeer Hi, sorry I forgot about this...

Here's the Android implementation (assuming TranslateViewAsync(View view) is set up as a dependency service method):

public async Task TranslateViewAsync(View view)
 {
      var renderer = view.GetRenderer();

      renderer.View.SetLayerType(LayerType.Hardware, null);

      ObjectAnimator objectAnimator;

      objectAnimator = ObjectAnimator.OfFloat(renderer.View, "translationX", 0);
      objectAnimator.SetInterpolator(new DecelerateInterpolator());

      var animatorSet = new AnimatorSet();
      var animators = new List<Animator> { objectAnimator };

     animatorSet.SetDuration(300);
     animatorSet.PlayTogether(animators);
     animatorSet.AnimationEnd += (sender, args) =>
     {
          renderer.View.SetLayerType(LayerType.None, null);

          view.TranslationX = 0;
     };

    await animatorSet.StartAsync();
}
public static class AnimatorExtensions
{
   public static Task StartAsync(this Animator animator)
   {
       var taskAnimationListener = new TaskAnimationListener();

       animator.AddListener(taskAnimationListener);
       animator.Start();

       return taskAnimationListener.Task;
   }
}

This one translates the view on the x axis to point 0. For other properties, you'll need to swap the corresponding Android property names. Make sure to notify Forms of the final change in the AnimationEnd event.

iOS is a bit tricky. I'm sure there is a better way to do this.

public async Task TranslateViewAsync(View view)
{
    var finished = false;

    CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(() =>
     {
        var renderer = Platform.GetRenderer(view);
        renderer.NativeView.Transform = CGAffineTransform.MakeTranslation(375, 0);

        var uiViewPropertyAnimator = new UIViewPropertyAnimator(0.3, UIViewAnimationCurve.EaseOut, () =>
        {
             renderer.NativeView.Transform = CGAffineTransform.MakeTranslation(0, 0);
        });

        uiViewPropertyAnimator.AddCompletion(p =>
        {
             finished = true;
        });

        uiViewPropertyAnimator.StartAnimation();
    });

    while (!finished)
       await Task.Delay(10);
 }

On iOS, you need to first translate the transform to its start position. Then, inside UIViewPropertyAnimator, you need to specify the final transform so iOS can carry out the animation. Forms should not be aware of native property changes during the animation begin and end phases.

In essence, all of this hacky code is trying to bypass layers of Forms code that degrade animation performance.

@raindeer
Copy link

@adrianknight89 interesting, will have a look at it. thanks!

@GiampaoloGabba
Copy link
Contributor

I was trying to do some animations on complex views and i had to stop it immediately :(
So i found this issue and investigated the animation layer in forms: i had no idea that was implemented with an internal timer, completely skipping the native implementations :(

So i tried to compare some small animations with the forms engine and the native ones....
Ouch, the results are quite noticeable: even small animations are much more smooth using the native framework.

Maybe the community can help to create a more modern and performant engine for animations, lets see if the forms teams will ever approve this request :)

@adrianknight89
Copy link
Contributor Author

@GiampaoloGabba

Maybe the community can help to create a more modern and performant engine for animations, lets see if the forms teams will ever approve this request :)

Honestly, this should not be left to the community. While I understand Forms is an open source project, big corporations like Microsoft should be expected to hire people and develop a full-fledged cross-platform framework, especially considering Microsoft needs to attract more startups to the Azure ecosystem.

I'm extremely worried about the future of Xamarin Forms and recently decided to give up after many many years of contributing to this project. Right now, I'm migrating my code base to Xamarin.Android and Xamarin.iOS and noticing visible changes with respect to performance and layouting. With nearly 2000 open issues (some of them are very serious indeed) and a botched CollectionView implementation, I don't see how anyone can deliver Forms production code and go to bed at night.

I've implemented my own UICollectionView and RecyclerView instead of using CollectionView and also started using SDWebImage and Glide instead of FFImageLoading, and I'm never going back. Finally, no more terrible animations. :)

Cheers.

@davidortinau
Copy link
Contributor

@adrianknight89 I’ve sent you an email. Let’s talk. I’d really like to see your performance comparisons and get to the bottom of this. We should be able to achieve the same performance since all those same controls you note are either being used or may be substituted.

@GiampaoloGabba
Copy link
Contributor

GiampaoloGabba commented Jan 26, 2020

I have to say that in ios the animations are quite smooth (not as native, but they are good), in android on the other hand... they are a bit jittering, not fluid at all.
Maybe its just me, 'cause it seems that no one else is interested in this issue :(

Lately i was experimenting some simple UI with other cross-platforms frameworks. The custom animations in Xamarin Forms doesnt felt "natural" and fluid. I dont know... now that i noted this, I cant stop to see it in every app i developed :) But, again, maybe its just me...

@samhouts
Copy link
Member

Duplicate of #6033

@samhouts samhouts marked this as a duplicate of #6033 Feb 13, 2020
@raindeer
Copy link

Is this really a duplicate of #6033? This is about hw accelerated animations while #6033 just mentions more advanced page transitions.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants