Как включить прокрутку при увеличении и уменьшении масштаба на странице содержимого в формах xamarin

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

XAML

xmlns:helper="clr-namespace:KPGTC.Deals.Mobile.Helpers"
<helper:PinchToZoomContainer>
    <helper:PinchToZoomContainer.Content>
        <Image x:Name="img_Popup"/>
    </helper:PinchToZoomContainer.Content>
</helper:PinchToZoomContainer>

Код:

public class PinchToZoomContainer : ContentView
{
    double MIN_SCALE = 1;
    double MAX_SCALE = 4;
    double startScale = 1;
    double currentScale = 1;
    double xOffset = 0;
    double yOffset = 0;
    bool _isActive = false;

    public PinchToZoomContainer()
    {
        DependencyService.Get<IHelpers>().ShowAlert("Double-tap to zoom");

        //var _pinchGesture = new PinchGestureRecognizer();
        //_pinchGesture.PinchUpdated += OnPinchUpdated;
        //GestureRecognizers.Add(_pinchGesture);

        var _tapGesture = new TapGestureRecognizer { NumberOfTapsRequired = 2 };
        _tapGesture.Tapped += On_Tapped;
        GestureRecognizers.Add(_tapGesture);

        var _panGesture = new PanGestureRecognizer();
        _panGesture.PanUpdated += OnPanUpdated;
        GestureRecognizers.Add(_panGesture);

        TranslationX = 0;
        TranslationY = 0;
        _isActive = false;
    }

    private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
    {
        if (_isActive)
        {
            if (e.TotalX > 0)
            {
                if (e.TotalX > 2)
                {
                    TranslationX += 15;
                }
            }
            else
            {
                if (e.TotalX < -2)
                {
                    TranslationX -= 15;
                }
            }
        }
    }

    private void On_Tapped(object sender, EventArgs e)
    {
        if (Scale > MIN_SCALE)
        {
            _isActive = false;
            this.ScaleTo(MIN_SCALE, 250, Easing.CubicInOut);
            this.TranslateTo(0, 0, 250, Easing.CubicInOut);
        }
        else
        {
            _isActive = true;
            AnchorX = AnchorY = 0.5;
            this.ScaleTo(MAX_SCALE, 250, Easing.CubicInOut);
        }
    }

    void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
    {
        if (e.Status == GestureStatus.Started)
        {
            startScale = Content.Scale;
            Content.AnchorX = 0;
            Content.AnchorY = 0;
        }
        if (e.Status == GestureStatus.Running)
        {
            currentScale += (e.Scale - 1) * startScale;
            currentScale = Math.Max(1, currentScale);

            double renderedX = Content.X + xOffset;
            double deltaX = renderedX / Width;
            double deltaWidth = Width / (Content.Width * startScale);
            double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;

            double renderedY = Content.Y + yOffset;
            double deltaY = renderedY / Height;
            double deltaHeight = Height / (Content.Height * startScale);
            double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;

            double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
            double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);

            Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
            Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);

            Content.Scale = currentScale;
        }
        if (e.Status == GestureStatus.Completed)
        {
            xOffset = Content.TranslationX;
            yOffset = Content.TranslationY;
        }
    }
}

Позвольте нам продолжить обсуждение в чате.

Paul Kertscher 12.04.2018 13:18
0
1
595
1

Ответы 1

Хорошо, это было довольно сложно, и, надо признать, я не совсем понимаю как Я сделал это, но я сделал это.

Некоторые мысли:

  • Вы смешали перевод контейнера и содержимого, с чем довольно сложно справиться - если это вообще возможно
  • При панорамировании вы добавляли 15 каждый раз при возникновении события панорамирования, но есть лучший способ: просто сохранить начальное смещение контента, а затем добавить TotalX и TotalY соответственно к TranslationX и TranslationY контента (это было легкая часть)
  • С панорамированием во время масштабирования было довольно сложно справиться, и мне пришлось выяснить это методом проб и ошибок.
    • По сути, вы должны сохранить исходную точку щипкового жеста при запуске жеста и вычислить разницу между исходной и текущей исходной точкой.
    • Затем вам нужно добавить разницу (умноженную на с и высоту соответственно элемента управления) к целевому переводу.

Вот код панорамирования:

private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
    if (e.StatusType == GestureStatus.Started)
    {
        this.xOffset = this.Content.TranslationX;
        this.yOffset = this.Content.TranslationY;
    }

    if (e.StatusType != GestureStatus.Completed
        && e.StatusType != GestureStatus.Canceled)
    {
        this.Content.TranslationX = this.xOffset + e.TotalX;
        this.Content.TranslationY = this.yOffset + e.TotalY;
    }

    if (e.StatusType == GestureStatus.Completed)
    {
        this.xOffset = this.Content.TranslationX;
        this.yOffset = this.Content.TranslationY;
    }
}

А вот для пощипывания

void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
    if (e.Status == GestureStatus.Started)
    {
        this.startScale = this.Content.Scale;
        this.Content.AnchorX = 0;
        this.Content.AnchorY = 0;

        this.startScaleOrigin = e.ScaleOrigin;
    }

    if (e.Status == GestureStatus.Running)
    {
        var originDiff = PinchToZoomContainer.CalculateDiff(e.ScaleOrigin, this.startScaleOrigin);

        this.currentScale += (e.Scale - 1) * this.startScale;
        this.currentScale = Math.Max(1, this.currentScale);

        double renderedX = this.Content.X + this.xOffset;
        double deltaX = renderedX / this.Width;
        double deltaWidth = this.Width / (this.Content.Width * this.startScale);
        double originX = (this.startScaleOrigin.X - deltaX) * deltaWidth;

        double renderedY = this.Content.Y + this.yOffset;
        double deltaY = renderedY / this.Height;
        double deltaHeight = this.Height / (this.Content.Height * this.startScale);
        double originY = (startScaleOrigin.Y - deltaY) * deltaHeight;

        double targetX = this.xOffset - ((originX) * this.Content.Width) * (this.currentScale - this.startScale) - originDiff.X * this.Content.Width;
        double targetY = this.yOffset - ((originY) * this.Content.Height) * (this.currentScale - this.startScale) - originDiff.Y * this.Content.Height;

        this.Content.TranslationX = targetX.Clamp(-this.Content.Width * (this.currentScale - 1), 0);
        this.Content.TranslationY = targetY.Clamp(-this.Content.Height * (this.currentScale - 1), 0);

        this.Content.Scale = this.currentScale;
    }

    if (e.Status == GestureStatus.Completed)
    {
        this.xOffset = this.Content.TranslationX;
        this.yOffset = this.Content.TranslationY;
    }
}

(Конечно, вы должны добавить Point startScaleOrigin в свой класс).

Наконец, вам нужен метод для расчета расстояния между двумя точками.

private static Point CalculateDiff(Point first, Point second)
{
    return second.Offset(-first.X, -first.Y);
}

К сожалению, мне не удалось получить правильное нажатие, но я думаю, вы сможете понять это отсюда.

Другие вопросы по теме