Я делаю в своем приложении функцию просмотра изображений, которая позволяет пользователю масштабировать и панорамировать.
Я использовал следующую документацию, чтобы добиться этого с помощью SkiaSharp.
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/touch
Сейчас я пытаюсь ограничить пользователя от уменьшения масштаба или панорамирования за края растрового изображения, загруженного в SKCanvasView.
Я ограничил масштабирование, но проблема в том, что я не могу понять, как ограничить панорамирование, я не могу найти ЛЮБОЙ пример того, как это сделать онлайн. Есть ли гуру SkiaSharp, достигший этого?
Вот код ...
Соответствующий XAML:
<Grid BackgroundColor="#141d3d">
<Grid.Effects>
<tt:TouchEffect Capture="True" TouchAction="OnTouchEffectAction" />
</Grid.Effects>
<skia:SKCanvasView x:Name="canvasView" PaintSurface="OnCanvasViewPaintSurface" />
<Button Text="CLOSE"
TextColor="White"
VerticalOptions="Start"
HorizontalOptions="End"
BackgroundColor="Transparent"
Command="{Binding CmdCloseFullImg}" />
</Grid>
XAML.cs
public partial class AePage : ContentPage
{
private AeViewModel aeViewModel = new AeViewModel();
private TouchManipulationBitmap bitmap = new TouchManipulationBitmap();
private List<long> touchIds = new List<long>();
public static float CanvasWidth { get; set; }
public static float CanvasHeight { get; set; }
public AePage()
{
InitializeComponent();
BindingContext = aeViewModel;
}
private void OnCanvasViewPaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e)
{
SKImageInfo info = e.Info;
SKCanvas canvas = e.Surface.Canvas;
canvas.Clear();
// Display the bitmap
bitmap.Paint(info, canvas);
}
private void OnTouchEffectAction(object sender, TouchActionEventArgs e)
{
CanvasWidth = canvasView.CanvasSize.Width;
CanvasHeight = canvasView.CanvasSize.Height;
// Convert Xamarin.Forms point to pixels
TouchTrackingPoint pt = e.Location;
SKPoint point = new SKPoint((float)(canvasView.CanvasSize.Width * pt.X / canvasView.Width),
(float)(canvasView.CanvasSize.Height * pt.Y / canvasView.Height));
switch (e.Type)
{
case TouchActionType.Pressed:
touchIds.Add(e.Id);
bitmap.ProcessTouchEvent(e.Id, e.Type, point);
break;
case TouchActionType.Moved:
if (touchIds.Contains(e.Id))
{
bitmap.ProcessTouchEvent(e.Id, e.Type, point);
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchIds.Contains(e.Id))
{
bitmap.ProcessTouchEvent(e.Id, e.Type, point);
touchIds.Remove(e.Id);
canvasView.InvalidateSurface();
}
break;
}
}
}
Класс TouchManipulationBitmap:
public class TouchManipulationBitmap
{
public SKBitmap bitmap;
public TouchManipulationManager TouchManager { set; get; }
public static SKMatrix Matrix { set; get; }
private Dictionary<long, TouchManipulationInfo> touchDictionary = new Dictionary<long, TouchManipulationInfo>();
public TouchManipulationBitmap()
{
this.bitmap = ReturnSKBitmap();
Matrix = SKMatrix.MakeIdentity();
TouchManager = new TouchManipulationManager();
}
public SKBitmap ReturnSKBitmap()
{
string resourceId = "MetroAlarmHandlerMobile.Media.David DP.jpg";
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (System.IO.Stream stream = assembly.GetManifestResourceStream(resourceId))
{
return SKBitmap.Decode(stream);
}
}
public void Paint(SKImageInfo info, SKCanvas canvas)
{
canvas.Save();
SKMatrix matrix = Matrix;
canvas.Concat(ref matrix);
float scale = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height);
float x = (info.Width - scale * bitmap.Width) / 2;
float y = (info.Height - scale * bitmap.Height) / 2;
SKRect destRect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);
canvas.DrawBitmap(bitmap, destRect);
canvas.Restore();
Console.WriteLine($"SCALE: {Matrix.ScaleX}");
Console.WriteLine($"TRANSLATION: X = {Matrix.TransX} Y = {Matrix.TransY}");
}
public void ProcessTouchEvent(long id, TouchActionType type, SKPoint location)
{
switch (type)
{
case TouchActionType.Pressed:
touchDictionary.Add(id, new TouchManipulationInfo
{
PreviousPoint = location,
NewPoint = location
});
break;
case TouchActionType.Moved:
TouchManipulationInfo info = touchDictionary[id];
info.NewPoint = location;
Manipulate();
info.PreviousPoint = info.NewPoint;
break;
case TouchActionType.Released:
touchDictionary[id].NewPoint = location;
Manipulate();
touchDictionary.Remove(id);
break;
case TouchActionType.Cancelled:
touchDictionary.Remove(id);
break;
}
}
private void Manipulate()
{
TouchManipulationInfo[] infos = new TouchManipulationInfo[touchDictionary.Count];
touchDictionary.Values.CopyTo(infos, 0);
SKMatrix touchMatrix = SKMatrix.MakeIdentity();
if (infos.Length == 1)
{
SKPoint prevPoint = infos[0].PreviousPoint;
SKPoint newPoint = infos[0].NewPoint;
SKPoint pivotPoint = Matrix.MapPoint(bitmap.Width / 2, bitmap.Height / 2);
touchMatrix = TouchManager.OneFingerManipulate(prevPoint, newPoint, pivotPoint);
}
else if (infos.Length >= 2)
{
int pivotIndex = infos[0].NewPoint == infos[0].PreviousPoint ? 0 : 1;
SKPoint pivotPoint = infos[pivotIndex].NewPoint;
SKPoint newPoint = infos[1 - pivotIndex].NewPoint;
SKPoint prevPoint = infos[1 - pivotIndex].PreviousPoint;
touchMatrix = TouchManager.TwoFingerManipulate(prevPoint, newPoint, pivotPoint);
}
SKMatrix matrix = Matrix;
SKMatrix.PostConcat(ref matrix, touchMatrix);
Matrix = matrix;
}
}
Класс TouchManipulationManager:
public class TouchManipulationManager
{
private float Magnitude(SKPoint point)
{
return (float)Math.Sqrt(Math.Pow(point.X, 2) + Math.Pow(point.Y, 2));
}
public SKMatrix OneFingerManipulate(SKPoint prevPoint, SKPoint newPoint, SKPoint pivotPoint)
{
SKMatrix touchMatrix = SKMatrix.MakeIdentity();
SKPoint delta = newPoint - prevPoint;
// Multiply the rotation matrix by a translation matrix
SKMatrix.PostConcat(ref touchMatrix, SKMatrix.MakeTranslation(delta.X, delta.Y));
return touchMatrix;
}
public SKMatrix TwoFingerManipulate(SKPoint prevPoint, SKPoint newPoint, SKPoint pivotPoint)
{
SKMatrix touchMatrix = SKMatrix.MakeIdentity();
SKPoint oldVector = prevPoint - pivotPoint;
SKPoint newVector = newPoint - pivotPoint;
float scale = Magnitude(newVector) / Magnitude(oldVector);
if (TouchManipulationBitmap.Matrix.ScaleX <= 1 && scale <= 1) return touchMatrix;
if (!float.IsNaN(scale) && !float.IsInfinity(scale))
{
SKMatrix.PostConcat(ref touchMatrix, SKMatrix.MakeScale(scale, scale, pivotPoint.X, pivotPoint.Y));
}
return touchMatrix;
}
}
Класс TouchManipulationInfo:
public class TouchManipulationInfo
{
public SKPoint PreviousPoint { set; get; }
public SKPoint NewPoint { set; get; }
}