feat: ZoomPanImage make zoom and pans public

This commit is contained in:
2026-05-13 12:18:05 +03:00
parent 469fc7b8aa
commit c7a798457d
+54 -39
View File
@@ -11,9 +11,6 @@ public class ZoomPanImage : Control
{ {
private Point? _lastMousePosition = null; private Point? _lastMousePosition = null;
private Dictionary<int, Point>? _lastTouchPositions = null; private Dictionary<int, Point>? _lastTouchPositions = null;
private float _zoom = 1;
private float _panX = 0;
private float _panY = 0;
public static readonly StyledProperty<Color> BackgroundProperty = AvaloniaProperty.Register<ZoomPanImage, Color>(nameof(Background), defaultValue: Colors.Transparent); public static readonly StyledProperty<Color> BackgroundProperty = AvaloniaProperty.Register<ZoomPanImage, Color>(nameof(Background), defaultValue: Colors.Transparent);
public Color Background public Color Background
@@ -39,15 +36,33 @@ public class ZoomPanImage : Control
get => GetValue(ImageSourceProperty); get => GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value); set => SetValue(ImageSourceProperty, value);
} }
public static readonly StyledProperty<float> ZoomProperty = AvaloniaProperty.Register<ZoomPanImage, float>(nameof(Zoom), defaultValue: 1);
public float Zoom
{
get => GetValue(ZoomProperty);
set => SetValue(ZoomProperty, value);
}
public static readonly StyledProperty<float> PanXProperty = AvaloniaProperty.Register<ZoomPanImage, float>(nameof(PanX), defaultValue: 0);
public float PanX
{
get => GetValue(PanXProperty);
set => SetValue(PanXProperty, value);
}
public static readonly StyledProperty<float> PanYProperty = AvaloniaProperty.Register<ZoomPanImage, float>(nameof(PanY), defaultValue: 0);
public float PanY
{
get => GetValue(PanYProperty);
set => SetValue(PanYProperty, value);
}
public ZoomPanImage() public ZoomPanImage()
: base() : base()
{ {
DoubleTapped += (_, _) => DoubleTapped += (_, _) =>
{ {
_zoom = 1f; Zoom = 1f;
_panX = 0f; PanX = 0f;
_panY = 0f; PanY = 0f;
}; };
} }
@@ -65,26 +80,26 @@ public class ZoomPanImage : Control
var position = ea.GetPosition(this); var position = ea.GetPosition(this);
// Рассчитываем позицию в координатах изображения (до масштабирования) // Рассчитываем позицию в координатах изображения (до масштабирования)
var imageX = (position.X - Bounds.Width / 2 - _panX) / _zoom; var imageX = (position.X - Bounds.Width / 2 - PanX) / Zoom;
var imageY = (position.Y - Bounds.Height / 2 - _panY) / _zoom; var imageY = (position.Y - Bounds.Height / 2 - PanY) / Zoom;
// Применяем масштаб // Применяем масштаб
float zoomFactor = ea.Delta.Y > 0 ? 1.1f : 1 / 1.1f; float zoomFactor = ea.Delta.Y > 0 ? 1.1f : 1 / 1.1f;
_zoom *= zoomFactor; Zoom *= zoomFactor;
_zoom = Math.Clamp(_zoom, 1f, 10f); Zoom = Math.Clamp(Zoom, 1f, 10f);
_zoom = (float)Math.Round(_zoom, 2); Zoom = (float)Math.Round(Zoom, 2);
if (_zoom != 1f) if (Zoom != 1f)
{ {
// Пересчитываем смещение так, чтобы указатель остался на месте // Пересчитываем смещение так, чтобы указатель остался на месте
_panX = (float)(position.X - Bounds.Width / 2 - imageX * _zoom); PanX = (float)(position.X - Bounds.Width / 2 - imageX * Zoom);
_panY = (float)(position.Y - Bounds.Height / 2 - imageY * _zoom); PanY = (float)(position.Y - Bounds.Height / 2 - imageY * Zoom);
} }
else else
{ {
// Если зум == 1, то возвращаем изображение к исходному смещению // Если зум == 1, то возвращаем изображение к исходному смещению
_panX = 0f; PanX = 0f;
_panY = 0f; PanY = 0f;
} }
} }
base.OnPointerWheelChanged(ea); base.OnPointerWheelChanged(ea);
@@ -98,9 +113,9 @@ public class ZoomPanImage : Control
{ {
if (ea.ClickCount == 2) // По двойному клику возвращаем изображение к смещение и зум к стандартным значениям if (ea.ClickCount == 2) // По двойному клику возвращаем изображение к смещение и зум к стандартным значениям
{ {
_zoom = 1f; Zoom = 1f;
_panX = 0f; PanX = 0f;
_panY = 0f; PanY = 0f;
} }
else else
_lastMousePosition = ea.GetPosition(this); _lastMousePosition = ea.GetPosition(this);
@@ -138,14 +153,14 @@ public class ZoomPanImage : Control
{ {
if (ea.Pointer.Type == PointerType.Mouse) if (ea.Pointer.Type == PointerType.Mouse)
{ {
if (_lastMousePosition is null || _zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем if (_lastMousePosition is null || Zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
{ {
base.OnPointerMoved(ea); base.OnPointerMoved(ea);
return; return;
} }
var position = ea.GetPosition(this); var position = ea.GetPosition(this);
_panX -= (float)(_lastMousePosition.Value.X - position.X); PanX -= (float)(_lastMousePosition.Value.X - position.X);
_panY -= (float)(_lastMousePosition.Value.Y - position.Y); PanY -= (float)(_lastMousePosition.Value.Y - position.Y);
_lastMousePosition = position; _lastMousePosition = position;
} }
else if (ea.Pointer.Type == PointerType.Touch) else if (ea.Pointer.Type == PointerType.Touch)
@@ -163,11 +178,11 @@ public class ZoomPanImage : Control
return; return;
} }
if (_lastTouchPositions.Count() == 1 && _zoom != 1) // Если есть только одно прикосновение и зум != 1, то только перемещение if (_lastTouchPositions.Count() == 1 && Zoom != 1) // Если есть только одно прикосновение и зум != 1, то только перемещение
{ {
var position = ea.GetPosition(this); var position = ea.GetPosition(this);
_panX -= (float)(_lastTouchPositions[ea.Pointer.Id].X - position.X); PanX -= (float)(_lastTouchPositions[ea.Pointer.Id].X - position.X);
_panY -= (float)(_lastTouchPositions[ea.Pointer.Id].Y - position.Y); // Ось Y инвертирована PanY -= (float)(_lastTouchPositions[ea.Pointer.Id].Y - position.Y); // Ось Y инвертирована
_lastTouchPositions[ea.Pointer.Id] = position; _lastTouchPositions[ea.Pointer.Id] = position;
} }
else if (_lastTouchPositions.Count() == 2) // Если два прикосновения, то перемещение с приближением else if (_lastTouchPositions.Count() == 2) // Если два прикосновения, то перемещение с приближением
@@ -185,24 +200,24 @@ public class ZoomPanImage : Control
var newCenter = PointHelper.Center(newPosition1, newPosition2); var newCenter = PointHelper.Center(newPosition1, newPosition2);
var zoomFactor = newDistance / lastDistance; var zoomFactor = newDistance / lastDistance;
var newZoom = (float)(_zoom * zoomFactor); var newZoom = (float)(Zoom * zoomFactor);
newZoom = (float)Math.Clamp(newZoom, 1f, 10f); newZoom = (float)Math.Clamp(newZoom, 1f, 10f);
var imageX = (newCenter.X - Bounds.Width / 2 - _panX) / _zoom; var imageX = (newCenter.X - Bounds.Width / 2 - PanX) / Zoom;
var imageY = (newCenter.Y - Bounds.Height / 2 - _panY) / _zoom; var imageY = (newCenter.Y - Bounds.Height / 2 - PanY) / Zoom;
_zoom = newZoom; Zoom = newZoom;
if (_zoom > 1f) if (Zoom > 1f)
{ {
_panX = (float)(newCenter.X - Bounds.Width / 2 - imageX * _zoom); PanX = (float)(newCenter.X - Bounds.Width / 2 - imageX * Zoom);
_panY = (float)(newCenter.Y - Bounds.Height / 2 - imageY * _zoom); PanY = (float)(newCenter.Y - Bounds.Height / 2 - imageY * Zoom);
} }
else else
{ {
_zoom = 1f; Zoom = 1f;
_panX = 0f; PanX = 0f;
_panY = 0f; PanY = 0f;
} }
} }
} }
@@ -222,14 +237,14 @@ public class ZoomPanImage : Control
} }
var (sx, sy) = ComputeStretchScale(StretchMode, ImageSource.Size.Width, ImageSource.Size.Height); var (sx, sy) = ComputeStretchScale(StretchMode, ImageSource.Size.Width, ImageSource.Size.Height);
sx *= _zoom; sx *= Zoom;
sy *= _zoom; sy *= Zoom;
var drawWidth = ImageSource.Size.Width * sx; var drawWidth = ImageSource.Size.Width * sx;
var drawHeight = ImageSource.Size.Height * sy; var drawHeight = ImageSource.Size.Height * sy;
var destRect = new Rect( var destRect = new Rect(
(Bounds.Width - drawWidth) / 2 + _panX, (Bounds.Width - drawWidth) / 2 + PanX,
(Bounds.Height - drawHeight) / 2 + _panY, (Bounds.Height - drawHeight) / 2 + PanY,
drawWidth, drawHeight drawWidth, drawHeight
); );