LuckyDucko / Mopups

Popups For MAUI
BSD 3-Clause "New" or "Revised" License
274 stars 49 forks source link

PanGestureRecognizer jitters #12

Open IeuanWalker opened 2 years ago

IeuanWalker commented 2 years ago

Not sure if this is a use case you'd support, but I'm trying to implement a drag to dismiss. Logic is all working but the popup jitters when being dragged -

https://user-images.githubusercontent.com/6544051/176905230-b6967bdb-416e-4351-a81f-57d97a397a84.mp4

public partial class SearchPopup :  PopupPage
{
    double _closeYPosition, _y = 0;
    readonly Color _backgroundColour;
    public SearchPopup()
    {
        InitializeComponent();
        _backgroundColour = BackgroundColor;
    }

    protected override async void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);

        if (height < 0)
        {
            return;
        }

        //! IMPORTANT - Delay needed as height isn't set properly when the device is rotated 
        await Task.Delay(100);      

        // Set pop up to 80% of the screen height
        PopupContent.HeightRequest = 0.8 * height;

        // Set close Y position to 20% of the height of the pop up
        _closeYPosition = 0.2 * PopupContent.HeightRequest;
    }

    async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
    {
        await MopupService.Instance.PopAsync();
    }

    async void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType)
        {
            case GestureStatus.Running:
                draggingBar.SetDynamicResource(BackgroundColorProperty, "BlueDarkColour");

                // Don't allow dragging up
                if (e.TotalY < 0)
                {
                    break;
                }

                // Calculate percentage pop up has been dragged down
                double percentage = ((PopupContent.HeightRequest - (PopupContent.HeightRequest - e.TotalY)) / PopupContent.HeightRequest) * 100;

                // Animate background colour opacity
                this.CancelAnimation();
                await this.ColorTo(this.BackgroundColor, _backgroundColour.WithAlpha(Convert.ToSingle(_backgroundColour.Alpha * ((100 - percentage) / 100))), c => BackgroundColor = c, 100);

                // Move pop up to y pan position
                PopupContent.TranslationY = e.TotalY;
                _y = e.TotalY;
                break;

            case GestureStatus.Completed:
                draggingBar.SetDynamicResource(BackgroundColorProperty, "BackgroundColourSecondary");

                if (_y > _closeYPosition)
                {
                    await MopupService.Instance.PopAsync();
                }
                else
                {
                    await PopupContent.TranslateTo(0, 0);
                    await this.ColorTo(this.BackgroundColor, _backgroundColour, c => BackgroundColor = c);
                }

                break;
        }
    }
}

public static class ViewExtensions
{
    public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing? easing = null)
    {
        Func<double, Color> transform = (t) => Color.FromRgba(
            fromColor.Red + t * (toColor.Red - fromColor.Red),
            fromColor.Green + t * (toColor.Green - fromColor.Green),
            fromColor.Blue + t * (toColor.Blue - fromColor.Blue),
            fromColor.Alpha + t * (toColor.Alpha - fromColor.Alpha));

        return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
    }

    public static void CancelAnimation(this VisualElement self)
    {
        self.AbortAnimation("ColorTo");
    }

    static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing? easing)
    {
        easing = easing ?? Easing.Linear;
        var taskCompletionSource = new TaskCompletionSource<bool>();

        element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
        return taskCompletionSource.Task;
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<Mopups:PopupPage x:Class="FitNote.Popups.SearchPopup"
                  xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  xmlns:Animations="clr-namespace:Mopups.Animations;assembly=Mopups"
                  xmlns:Mopups="clr-namespace:Mopups.Pages;assembly=Mopups"
                  Title="Aswin"
                  BackgroundColor="#99404040"
                  BackgroundInputTransparent="False"
                  CloseWhenBackgroundIsClicked="True">
    <Mopups:PopupPage.Animation>
        <Animations:MoveAnimation DurationIn="400"
                                  DurationOut="300"
                                  EasingIn="SinOut"
                                  EasingOut="SinIn"
                                  HasBackgroundAnimation="True"
                                  PositionIn="Bottom"
                                  PositionOut="Bottom" />
    </Mopups:PopupPage.Animation>

    <Border x:Name="PopupContent"
            Margin="-2"
            Padding="0"
            BackgroundColor="{DynamicResource BackgroundColourPrimary}"
            HorizontalOptions="FillAndExpand"
            VerticalOptions="End">
        <Border.StrokeShape>
            <RoundRectangle CornerRadius="20,20,0,0" />
        </Border.StrokeShape>
        <Grid RowDefinitions="auto,auto,auto">
            <BoxView x:Name="draggingBar"
                     Grid.Row="0"
                     Margin="0,10,0,0"
                     BackgroundColor="{DynamicResource BackgroundColourSecondary}"
                     CornerRadius="5"
                     HeightRequest="10"
                     WidthRequest="100">
                <BoxView.GestureRecognizers>
                    <PanGestureRecognizer PanUpdated="PanGestureRecognizer_PanUpdated" />
                </BoxView.GestureRecognizers>
            </BoxView>
            <Label Grid.Row="1"
                   Margin="20"
                   Text="CANCEL">
                <Label.GestureRecognizers>
                    <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
                </Label.GestureRecognizers>
            </Label>

            <Label Grid.Row="2"
                   Margin="20"
                   Text="{Binding Source={RelativeSource AncestorType={x:Type Border}}, Path=HeightRequest}" />
        </Grid>
    </Border>
</Mopups:PopupPage>
LuckyDucko commented 2 years ago

Don't won't to rule this out as it is important, I love the idea behind it

AswinPG commented 2 years ago

If it's working fine on iOS then this could be a bug on the maui side. The values that pan gesture returns are different on both android and iOS. I did not check your code but I have seen this before... If I get time I'll check your code

SagarPanwala commented 1 year ago

@IeuanWalker : You can try this. I remember I had same issue and little line of code change fix this same problem. https://github.com/SagarPanwala/ContentSheet