diff --git a/GraphPage.xaml b/GraphPage.xaml index 0126dfd..8885936 100644 --- a/GraphPage.xaml +++ b/GraphPage.xaml @@ -7,14 +7,15 @@ - + + - + diff --git a/GraphPage.xaml.cs b/GraphPage.xaml.cs index 94357f2..23405e4 100644 --- a/GraphPage.xaml.cs +++ b/GraphPage.xaml.cs @@ -5,6 +5,7 @@ namespace NotesDataAnalyst { public partial class GraphPage : ContentPage { + private Point _initialPanPosition; private PointF _initialNodePosition; private PointF _initialTouchPosition; private SizeF _initialNodeSize; @@ -13,6 +14,11 @@ namespace NotesDataAnalyst private GraphNode _selectedNode; private bool _isScrollingBlocked = false; + private double _scale = 1.0; + private double _initialScale = 1.0; + private bool _isNodeBeingMoved = false; + private const double SmoothFactor = 0.2; // Коэффициент сглаживания + private Point _lastTranslation; public GraphPage() { @@ -29,9 +35,100 @@ namespace NotesDataAnalyst tapGesture.Tapped += OnCanvasTapped; canvasView.GestureRecognizers.Add(tapGesture); + // Добавляем Pinch только один раз + if (!canvasView.GestureRecognizers.OfType().Any()) + { + var pinchGesture = new PinchGestureRecognizer(); + pinchGesture.PinchUpdated += OnPinchUpdated; + canvasView.GestureRecognizers.Add(pinchGesture); + } + } + + // Обработчик жеста перемещения (Pan) для перемещения канвы или ноды + private void OnPanUpdated(object sender, PanUpdatedEventArgs e) + { + if (_selectedNode != null) + { + // Начало перетаскивания + if (e.StatusType == GestureStatus.Started) + { + // Запоминаем начальную позицию касания и ноды + _initialTouchPosition = new Point(e.TotalX, e.TotalY); + _initialNodePosition = _selectedNode.Position; + } + // Перемещение + else if (e.StatusType == GestureStatus.Running) + { + var deltaX = e.TotalX - _initialTouchPosition.X; // Разница с начальной точкой + var deltaY = e.TotalY - _initialTouchPosition.Y; + + // Перемещаем ноду относительно её начальной позиции + _selectedNode.Position = new Point( + _initialNodePosition.X + deltaX, + _initialNodePosition.Y + deltaY + ); + + canvasView.Invalidate(); // Перерисовываем канву + } + // Завершение перетаскивания + else if (e.StatusType == GestureStatus.Completed) + { + // Можно добавить логику на завершение перетаскивания, например, включить прокрутку + //scrollView.IsEnabled = true; + } + } + else // Перемещение канвы при снятии выделения с ноды + { + if (e.StatusType == GestureStatus.Started) + { + _initialPanPosition = new Point(canvasView.TranslationX, canvasView.TranslationY); + _lastTranslation = _initialPanPosition; + } + else if (e.StatusType == GestureStatus.Running) + { + var deltaX = e.TotalX; + var deltaY = e.TotalY; + + // Применяем сглаживание к движению + double smoothedX = _lastTranslation.X + SmoothFactor * (deltaX - _lastTranslation.X); + double smoothedY = _lastTranslation.Y + SmoothFactor * (deltaY - _lastTranslation.Y); + + // Обновляем положение канвы + canvasView.TranslationX = _initialPanPosition.X + smoothedX; + canvasView.TranslationY = _initialPanPosition.Y + smoothedY; + + // Сохраняем последнюю смягченную позицию для следующего цикла + _lastTranslation = new Point(smoothedX, smoothedY); + } + else if (e.StatusType == GestureStatus.Completed) + { + _initialPanPosition = new Point(canvasView.TranslationX, canvasView.TranslationY); + canvasView.Invalidate(); + } + + } + } + private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e) + { + if (e.Status == GestureStatus.Started) + { + // Сохраняем начальный масштаб + _initialScale = _scale; + } + else if (e.Status == GestureStatus.Running) + { + // Вычисляем новый масштаб + _scale = Math.Max(0.5, Math.Min(_initialScale * e.Scale, 3.0)); + + // Применяем масштаб к канве + canvasView.Scale = _scale; + } + } + + private async void OnEditTextClicked(object sender, EventArgs e) { // Проверяем, выбрана ли нода @@ -124,6 +221,7 @@ namespace NotesDataAnalyst _nodes.Add(childNode); _connections.Add(new GraphConnection { FromNode = _selectedNode, ToNode = childNode }); canvasView.Invalidate(); + _selectedNode = childNode; } else { @@ -142,44 +240,14 @@ namespace NotesDataAnalyst _selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node)); foreach (var node in _nodes) - node.IsActive = node == _selectedNode; - + node.IsActive = node == _selectedNode; + canvasView.Invalidate(); + } } - private void OnPanUpdated(object sender, PanUpdatedEventArgs e) - { - if (_selectedNode == null) return; - - // Начало перетаскивания - if (e.StatusType == GestureStatus.Started) - { - // Запоминаем начальную позицию касания и ноды - _initialTouchPosition = new Point(e.TotalX, e.TotalY); - _initialNodePosition = _selectedNode.Position; - } - // Перемещение - else if (e.StatusType == GestureStatus.Running) - { - var deltaX = e.TotalX - _initialTouchPosition.X; // Разница с начальной точкой - var deltaY = e.TotalY - _initialTouchPosition.Y; - - // Перемещаем ноду относительно её начальной позиции - _selectedNode.Position = new Point( - _initialNodePosition.X + deltaX, - _initialNodePosition.Y + deltaY - ); - - canvasView.Invalidate(); // Перерисовываем канву - } - // Завершение перетаскивания - else if (e.StatusType == GestureStatus.Completed) - { - // Можно добавить логику на завершение перетаскивания, например, включить прокрутку - scrollView.IsEnabled = true; - } - } + private bool IsPointInsideNode(PointF point, GraphNode node) {