Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Begin animation in styles #4673

Closed
SKProCH opened this issue Sep 15, 2020 · 5 comments
Closed

Begin animation in styles #4673

SKProCH opened this issue Sep 15, 2020 · 5 comments
Labels

Comments

@SKProCH
Copy link
Contributor

SKProCH commented Sep 15, 2020

I have the following style:

<Style Selector="RadioButton:pressed /template/ Ellipse#InteractionEllipse">
    <Style.Animations>
        <Animation Duration="0:0:0.3">
            <KeyFrame Cue="0%">
                <Setter Property="Opacity" Value="0.3"></Setter>
            </KeyFrame>
            <KeyFrame Cue="100%">
                <Setter Property="Opacity" Value="0"></Setter>
            </KeyFrame>
        </Animation>
    </Style.Animations>
</Style>

It works, but it interrupts as soon as I stop pressing the button.
It would be nice to be able not to interrupt the animation when the style removed, but to let it run to the end. Is there such a possibility?

@SKProCH
Copy link
Contributor Author

SKProCH commented Sep 16, 2020

Ok, apparently it won't be possible to implement this by standard means. I wrote a workaround.
First, the base class:

/// <summary>
/// Tracks the progress of an animation. This class should act the same as <see cref="Animation"/>
/// </summary>
public class ControllableAnimationBase : AvaloniaObject, IAnimation {
    /// <summary>
    /// Defines the <see cref="Animation"/> property.
    /// </summary>
    public static readonly DirectProperty<ControllableAnimationBase, Animation> AnimationProperty =
        AvaloniaProperty.RegisterDirect<ControllableAnimationBase, Animation>(
            nameof(_animation),
            o => o._animation,
            (o, v) => o._animation = v);

    private Animation _animation;

    [Content]
    public Animation Animation {
        get => _animation;
        set => SetAndRaise(AnimationProperty, ref _animation, value);
    }

    public virtual IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete = null) {
        var previous = false;
        var observable = new Subject<bool>();
        match.Subscribe(b => {
            OnNext(observable, previous, b);
            previous = b;
        });
        return Animation.Apply(control, clock, observable, onComplete);
    }

    internal virtual void OnNext(Subject<bool> match, bool previous, bool obj) {
        match.OnNext(obj);
    }

    public virtual Task RunAsync(Animatable control, IClock clock) {
        return Animation.RunAsync(control, clock);
    }
}

It acts as a proxy between calling OnNext on the original Observable and passing it to our animation. Next, we can override OnNext and modify it as we like.
After that we creating target class:

public class BeginAnimation : ControllableAnimationBase {
    internal override void OnNext(Subject<bool> match, bool previous, bool obj) {
        if (obj) return;
        // "Turning" off
        match.OnNext(false);
        
        // Then "turning" on
        match.OnNext(true);
    }
}

Then we modifying target style:

    <Style.Animations>
        <animations:BeginAnimation>
            <Animation>
                    ...
            </Animation>
        </animations:BeginAnimation>
    </Style.Animations>

But nevertheless, I believe that this is a workaround and it would be nice to implement it directly in.

@danwalmsley
Copy link
Member

@jmacato

@maxkatz6
Copy link
Member

@SKProCH you also can add some class or pseudoclass to your button after click and update style selector to run animation only for buttons with this class.
And I am not sure about details, when animation should be running in your case, but RadioButton has ":checked" pseudoclass, which you can use to run animation only when it is checked. It also has ":unchecked" and ":indeterminate".

@SKProCH
Copy link
Contributor Author

SKProCH commented Sep 18, 2020

And I am not sure about details, when animation should be running in your case, but RadioButton has ":checked" pseudoclass, which you can use to run animation only when it is checked. It also has ":unchecked" and ":indeterminate".

Yes, I know, but here it was necessary to start the animation with any click on the button. And if using :checked, the animation would not react to repeated (if you click on an already checked button again, it will still remain checked) clicks (which is correct).

@maxkatz6
Copy link
Member

@SKProCH so you can implement some kind of mixin with custom pseudoclass.
Add and remove it on specific events.
Example:
https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Mixins/PressedMixin.cs

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

No branches or pull requests

3 participants