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 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 Color Background
@@ -39,15 +36,33 @@ public class ZoomPanImage : Control
get => GetValue(ImageSourceProperty);
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()
: base()
{
DoubleTapped += (_, _) =>
{
_zoom = 1f;
_panX = 0f;
_panY = 0f;
Zoom = 1f;
PanX = 0f;
PanY = 0f;
};
}
@@ -65,26 +80,26 @@ public class ZoomPanImage : Control
var position = ea.GetPosition(this);
// Рассчитываем позицию в координатах изображения (до масштабирования)
var imageX = (position.X - Bounds.Width / 2 - _panX) / _zoom;
var imageY = (position.Y - Bounds.Height / 2 - _panY) / _zoom;
var imageX = (position.X - Bounds.Width / 2 - PanX) / Zoom;
var imageY = (position.Y - Bounds.Height / 2 - PanY) / Zoom;
// Применяем масштаб
float zoomFactor = ea.Delta.Y > 0 ? 1.1f : 1 / 1.1f;
_zoom *= zoomFactor;
_zoom = Math.Clamp(_zoom, 1f, 10f);
_zoom = (float)Math.Round(_zoom, 2);
Zoom *= zoomFactor;
Zoom = Math.Clamp(Zoom, 1f, 10f);
Zoom = (float)Math.Round(Zoom, 2);
if (_zoom != 1f)
if (Zoom != 1f)
{
// Пересчитываем смещение так, чтобы указатель остался на месте
_panX = (float)(position.X - Bounds.Width / 2 - imageX * _zoom);
_panY = (float)(position.Y - Bounds.Height / 2 - imageY * _zoom);
PanX = (float)(position.X - Bounds.Width / 2 - imageX * Zoom);
PanY = (float)(position.Y - Bounds.Height / 2 - imageY * Zoom);
}
else
{
// Если зум == 1, то возвращаем изображение к исходному смещению
_panX = 0f;
_panY = 0f;
PanX = 0f;
PanY = 0f;
}
}
base.OnPointerWheelChanged(ea);
@@ -98,9 +113,9 @@ public class ZoomPanImage : Control
{
if (ea.ClickCount == 2) // По двойному клику возвращаем изображение к смещение и зум к стандартным значениям
{
_zoom = 1f;
_panX = 0f;
_panY = 0f;
Zoom = 1f;
PanX = 0f;
PanY = 0f;
}
else
_lastMousePosition = ea.GetPosition(this);
@@ -138,14 +153,14 @@ public class ZoomPanImage : Control
{
if (ea.Pointer.Type == PointerType.Mouse)
{
if (_lastMousePosition is null || _zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
if (_lastMousePosition is null || Zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
{
base.OnPointerMoved(ea);
return;
}
var position = ea.GetPosition(this);
_panX -= (float)(_lastMousePosition.Value.X - position.X);
_panY -= (float)(_lastMousePosition.Value.Y - position.Y);
PanX -= (float)(_lastMousePosition.Value.X - position.X);
PanY -= (float)(_lastMousePosition.Value.Y - position.Y);
_lastMousePosition = position;
}
else if (ea.Pointer.Type == PointerType.Touch)
@@ -163,11 +178,11 @@ public class ZoomPanImage : Control
return;
}
if (_lastTouchPositions.Count() == 1 && _zoom != 1) // Если есть только одно прикосновение и зум != 1, то только перемещение
if (_lastTouchPositions.Count() == 1 && Zoom != 1) // Если есть только одно прикосновение и зум != 1, то только перемещение
{
var position = ea.GetPosition(this);
_panX -= (float)(_lastTouchPositions[ea.Pointer.Id].X - position.X);
_panY -= (float)(_lastTouchPositions[ea.Pointer.Id].Y - position.Y); // Ось Y инвертирована
PanX -= (float)(_lastTouchPositions[ea.Pointer.Id].X - position.X);
PanY -= (float)(_lastTouchPositions[ea.Pointer.Id].Y - position.Y); // Ось Y инвертирована
_lastTouchPositions[ea.Pointer.Id] = position;
}
else if (_lastTouchPositions.Count() == 2) // Если два прикосновения, то перемещение с приближением
@@ -185,24 +200,24 @@ public class ZoomPanImage : Control
var newCenter = PointHelper.Center(newPosition1, newPosition2);
var zoomFactor = newDistance / lastDistance;
var newZoom = (float)(_zoom * zoomFactor);
var newZoom = (float)(Zoom * zoomFactor);
newZoom = (float)Math.Clamp(newZoom, 1f, 10f);
var imageX = (newCenter.X - Bounds.Width / 2 - _panX) / _zoom;
var imageY = (newCenter.Y - Bounds.Height / 2 - _panY) / _zoom;
var imageX = (newCenter.X - Bounds.Width / 2 - PanX) / 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);
_panY = (float)(newCenter.Y - Bounds.Height / 2 - imageY * _zoom);
PanX = (float)(newCenter.X - Bounds.Width / 2 - imageX * Zoom);
PanY = (float)(newCenter.Y - Bounds.Height / 2 - imageY * Zoom);
}
else
{
_zoom = 1f;
_panX = 0f;
_panY = 0f;
Zoom = 1f;
PanX = 0f;
PanY = 0f;
}
}
}
@@ -222,14 +237,14 @@ public class ZoomPanImage : Control
}
var (sx, sy) = ComputeStretchScale(StretchMode, ImageSource.Size.Width, ImageSource.Size.Height);
sx *= _zoom;
sy *= _zoom;
sx *= Zoom;
sy *= Zoom;
var drawWidth = ImageSource.Size.Width * sx;
var drawHeight = ImageSource.Size.Height * sy;
var destRect = new Rect(
(Bounds.Width - drawWidth) / 2 + _panX,
(Bounds.Height - drawHeight) / 2 + _panY,
(Bounds.Width - drawWidth) / 2 + PanX,
(Bounds.Height - drawHeight) / 2 + PanY,
drawWidth, drawHeight
);