private Vector SnapOffset()

in src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs [963:1046]


        private Vector SnapOffset(Vector offset, Vector direction = default, bool snapToNext = false)
        {
            var scrollable = GetScrollSnapPointsInfo(Content);

            if (scrollable is null || (VerticalSnapPointsType == SnapPointsType.None && HorizontalSnapPointsType == SnapPointsType.None))
                return offset;

            var diff = GetAlignmentDiff();

            if (VerticalSnapPointsType != SnapPointsType.None && (_areVerticalSnapPointsRegular || _verticalSnapPoints?.Count > 0) && (!snapToNext || snapToNext && direction.Y != 0))
            {
                var estimatedOffset = new Vector(offset.X, offset.Y + diff.Y);
                double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0;

                if (_areVerticalSnapPointsRegular)
                {
                    previousSnapPoint = (int)(estimatedOffset.Y / _verticalSnapPoint) * _verticalSnapPoint + _verticalSnapPointOffset;
                    nextSnapPoint = previousSnapPoint + _verticalSnapPoint;
                    midPoint = (previousSnapPoint + nextSnapPoint) / 2;
                }
                else if (_verticalSnapPoints?.Count > 0)
                {
                    (previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(_verticalSnapPoints, estimatedOffset.Y);
                    midPoint = (previousSnapPoint + nextSnapPoint) / 2;
                }

                var nearestSnapPoint = snapToNext ? (direction.Y > 0 ? previousSnapPoint : nextSnapPoint) :
                    estimatedOffset.Y < midPoint ? previousSnapPoint : nextSnapPoint;

                offset = new Vector(offset.X, nearestSnapPoint - diff.Y);
            }

            if (HorizontalSnapPointsType != SnapPointsType.None && (_areHorizontalSnapPointsRegular || _horizontalSnapPoints?.Count > 0) && (!snapToNext || snapToNext && direction.X != 0))
            {
                var estimatedOffset = new Vector(offset.X + diff.X, offset.Y);
                double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0;

                if (_areHorizontalSnapPointsRegular)
                {
                    previousSnapPoint = (int)(estimatedOffset.X / _horizontalSnapPoint) * _horizontalSnapPoint + _horizontalSnapPointOffset;
                    nextSnapPoint = previousSnapPoint + _horizontalSnapPoint;
                    midPoint = (previousSnapPoint + nextSnapPoint) / 2;
                }
                else if (_horizontalSnapPoints?.Count > 0)
                {
                    (previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(_horizontalSnapPoints, estimatedOffset.X);
                    midPoint = (previousSnapPoint + nextSnapPoint) / 2;
                }

                var nearestSnapPoint = snapToNext ? (direction.X > 0 ? previousSnapPoint : nextSnapPoint) :
                    estimatedOffset.X < midPoint ? previousSnapPoint : nextSnapPoint;

                offset = new Vector(nearestSnapPoint - diff.X, offset.Y);
            }

            Vector GetAlignmentDiff()
            {
                var vector = default(Vector);

                switch (VerticalSnapPointsAlignment)
                {
                    case SnapPointsAlignment.Center:
                        vector += new Vector(0, Viewport.Height / 2);
                        break;
                    case SnapPointsAlignment.Far:
                        vector += new Vector(0, Viewport.Height);
                        break;
                }

                switch (HorizontalSnapPointsAlignment)
                {
                    case SnapPointsAlignment.Center:
                        vector += new Vector(Viewport.Width / 2, 0);
                        break;
                    case SnapPointsAlignment.Far:
                        vector += new Vector(Viewport.Width, 0);
                        break;
                }

                return vector;
            }

            return offset;
        }