3 pinchzoom xamarin xamarin.forms
我已经单独实现了pan和pinch,它工作正常.我现在正在尝试使用捏和平移,我看到一些问题.这是我的代码:
XAML:
<AbsoluteLayout x:Name="PinchZoomContainer">
<controls:NavBar x:Name="NavBar" ShowPrevNext="true" ShowMenu="false" IsModal="true" />
<controls:PanContainer x:Name="PinchToZoomContainer">
<Image x:Name="ImageMain" />
</controls:PanContainer>
</AbsoluteLayout>
Run Code Online (Sandbox Code Playgroud)
捏/平移手势添加:
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(panGesture);
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinchGesture);
Run Code Online (Sandbox Code Playgroud)
平移方法:
void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Started:
startX = e.TotalX;
startY = e.TotalY;
Content.AnchorX = 0;
Content.AnchorY = 0;
break;
case GestureStatus.Running:
// Translate and ensure we don't pan beyond the wrapped user interface element bounds.
Content.TranslationX = Math.Max(Math.Min(0, x + e.TotalX), -Math.Abs(Content.Width - App.ScreenWidth));
Content.TranslationY = Math.Max(Math.Min(0, y + e.TotalY), -Math.Abs(Content.Height - App.ScreenHeight));
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
x = Content.TranslationX;
y = Content.TranslationY;
break;
}
}
Run Code Online (Sandbox Code Playgroud)
捏法:
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
//ImageMain.AnchorX = 0;
//ImageMain.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
currentScale = Math.Min(currentScale, 2.5);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);
// Apply scale factor
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我关闭任一手势并仅使用另一手势,那么功能完美无缺.当我添加平移和捏合手势时会出现问题.似乎正在发生的事情是这样的:
1)平底锅实际上似乎按预期工作2)当你最初平移图像时,让我们说,将图像移动到Y中心和X中心,然后你尝试缩放,图像被设置回它的初始状态.然后,当您平移时,它会将您移回到您尝试缩放之前的位置(这就是为什么我说平底锅工作正常).
从我对调试的理解是,当你缩放时,它没有考虑你当前所处的位置.因此,当您先平移,然后进行缩放时,它不会放大您所处的位置,而是放大图像的起始点.然后当你尝试从那里平移时,pan方法仍会记住你的位置,它会让你回到你试图缩放之前的位置.
希望对此有所了解.显然,我的捏合方法存在问题.我只是想(显然无法弄清楚)我需要在其中添加逻辑,考虑到你当前所处的位置.
主要原因可能是每个人似乎都复制并使用此代码(来自dev.xamarin网站),其中包含非常复杂且非常不必要的坐标计算:-).这是不必要的,因为我们可以简单地让视图为我们做繁重的工作,使用正是这个目的的AnchorX
和AnchorY
属性.
我们可以进行双击操作以放大并恢复到原始比例.请注意,因为Xamarin无法为其Tap
事件提供坐标值(实际上是一个非常不明智的决定),我们现在只能从中心放大:
private void OnTapped(object sender, EventArgs e)
{
if (Scale > MIN_SCALE)
{
this.ScaleTo(MIN_SCALE, 250, Easing.CubicInOut);
this.TranslateTo(0, 0, 250, Easing.CubicInOut);
}
else
{
AnchorX = AnchorY = 0.5;
this.ScaleTo(MAX_SCALE, 250, Easing.CubicInOut);
}
}
Run Code Online (Sandbox Code Playgroud)
夹点处理程序同样简单,根本不需要计算任何翻译.我们所要做的就是将锚点设置为捏起点,框架将完成其余的工作,缩放将围绕这一点进行.请注意,我们在这里甚至还有一个额外的功能,弹性反弹缩放比例两端的过冲.
private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
switch (e.Status)
{
case GestureStatus.Started:
StartScale = Scale;
AnchorX = e.ScaleOrigin.X;
AnchorY = e.ScaleOrigin.Y;
break;
case GestureStatus.Running:
double current = Scale + (e.Scale - 1) * StartScale;
Scale = Clamp(current, MIN_SCALE * (1 - OVERSHOOT), MAX_SCALE * (1 + OVERSHOOT));
break;
case GestureStatus.Completed:
if (Scale > MAX_SCALE)
this.ScaleTo(MAX_SCALE, 250, Easing.SpringOut);
else if (Scale < MIN_SCALE)
this.ScaleTo(MIN_SCALE, 250, Easing.SpringOut);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
和平移处理程序,甚至更简单.一开始,我们计算锚点的起点,在平移过程中,我们不断更改锚点.这个锚点相对于视图区域,我们可以很容易地将它夹在0和1之间,这样就可以在极端情况下停止平移而无需任何平移计算.
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Started:
StartX = (1 - AnchorX) * Width;
StartY = (1 - AnchorY) * Height;
break;
case GestureStatus.Running:
AnchorX = Clamp(1 - (StartX + e.TotalX) / Width, 0, 1);
AnchorY = Clamp(1 - (StartY + e.TotalY) / Height, 0, 1);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
使用的常量和变量就是这些:
private const double MIN_SCALE = 1;
private const double MAX_SCALE = 8;
private const double OVERSHOOT = 0.15;
private double StartX, StartY;
private double StartScale;
Run Code Online (Sandbox Code Playgroud)
小智 3
采用完全不同的方法来处理这个问题。对于任何遇到问题的人来说,这都是 100% 有效的。
OnPan更新
void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
var s = (ContentView)sender;
// do not allow pan if the image is in its intial size
if (currentScale == 1)
return;
switch (e.StatusType)
{
case GestureStatus.Running:
double xTrans = xOffset + e.TotalX, yTrans = yOffset + e.TotalY;
// do not allow verical scorlling unless the image size is bigger than the screen
s.Content.TranslateTo(xTrans, yTrans, 0, Easing.Linear);
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
xOffset = s.Content.TranslationX;
yOffset = s.Content.TranslationY;
// center the image if the width of the image is smaller than the screen width
if (originalWidth * currentScale < ScreenWidth && ScreenWidth > ScreenHeight)
xOffset = (ScreenWidth - originalWidth * currentScale) / 2 - s.Content.X;
else
xOffset = System.Math.Max(System.Math.Min(0, xOffset), -System.Math.Abs(originalWidth * currentScale - ScreenWidth));
// center the image if the height of the image is smaller than the screen height
if (originalHeight * currentScale < ScreenHeight && ScreenHeight > ScreenWidth)
yOffset = (ScreenHeight - originalHeight * currentScale) / 2 - s.Content.Y;
else
//yOffset = System.Math.Max(System.Math.Min((originalHeight - (ScreenHeight)) / 2, yOffset), -System.Math.Abs((originalHeight * currentScale - ScreenHeight - (originalHeight - ScreenHeight) / 2)) + (NavBar.Height + App.StatusBarHeight));
yOffset = System.Math.Max(System.Math.Min((originalHeight - (ScreenHeight)) / 2, yOffset), -System.Math.Abs((originalHeight * currentScale - ScreenHeight - (originalHeight - ScreenHeight) / 2)));
// bounce the image back to inside the bounds
s.Content.TranslateTo(xOffset, yOffset, 500, Easing.BounceOut);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
捏合更新
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
var s = (ContentView)sender;
if (e.Status == GestureStatus.Started)
{
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = s.Content.Scale;
s.Content.AnchorX = 0;
s.Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = System.Math.Max(1, currentScale);
currentScale = System.Math.Min(currentScale, 5);
//scaleLabel.Text = "Scale: " + currentScale.ToString ();
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = s.Content.X + xOffset;
double deltaX = renderedX / App.ScreenWidth;
double deltaWidth = App.ScreenWidth / (s.Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = s.Content.Y + yOffset;
double deltaY = renderedY / App.ScreenHeight;
double deltaHeight = App.ScreenHeight / (s.Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * s.Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * s.Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
var transX = targetX.Clamp(-s.Content.Width * (currentScale - 1), 0);
var transY = targetY.Clamp(-s.Content.Height * (currentScale - 1), 0);
s.Content.TranslateTo(transX, transY, 0, Easing.Linear);
// Apply scale factor.
s.Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
// Store the translation applied during the pan
xOffset = s.Content.TranslationX;
yOffset = s.Content.TranslationY;
// center the image if the width of the image is smaller than the screen width
if (originalWidth * currentScale < ScreenWidth && ScreenWidth > ScreenHeight)
xOffset = (ScreenWidth - originalWidth * currentScale) / 2 - s.Content.X;
else
xOffset = System.Math.Max(System.Math.Min(0, xOffset), -System.Math.Abs(originalWidth * currentScale - ScreenWidth));
// center the image if the height of the image is smaller than the screen height
if (originalHeight * currentScale < ScreenHeight && ScreenHeight > ScreenWidth)
yOffset = (ScreenHeight - originalHeight * currentScale) / 2 - s.Content.Y;
else
yOffset = System.Math.Max(System.Math.Min((originalHeight - ScreenHeight) / 2, yOffset), -System.Math.Abs(originalHeight * currentScale - ScreenHeight - (originalHeight - ScreenHeight) / 2));
// bounce the image back to inside the bounds
s.Content.TranslateTo(xOffset, yOffset, 500, Easing.BounceOut);
}
}
Run Code Online (Sandbox Code Playgroud)
OnSizeAllocation(其中大部分您可能不需要,但有些您需要。考虑ScreenWidth
, ScreenHeight
, yOffset
, xOffset
, currentScale
)
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height); //must be called
if (width != -1 && (ScreenWidth != width || ScreenHeight != height))
{
ResetLayout(width, height);
originalWidth = initialLoad ?
ImageWidth >= 960 ?
App.ScreenWidth > 320
? 768
: 320
: ImageWidth / 3
: imageContainer.Content.Width / imageContainer.Content.Scale;
var normalizedHeight = ImageWidth >= 960 ?
App.ScreenWidth > 320 ? ImageHeight / (ImageWidth / 768)
: ImageHeight / (ImageWidth / 320)
: ImageHeight / 3;
originalHeight = initialLoad ?
normalizedHeight : (imageContainer.Content.Height / imageContainer.Content.Scale);
ScreenWidth = width;
ScreenHeight = height;
xOffset = imageContainer.TranslationX;
yOffset = imageContainer.TranslationY;
currentScale = imageContainer.Scale;
if (initialLoad)
initialLoad = false;
}
}
Run Code Online (Sandbox Code Playgroud)
布局(C# 中的 XAML)
ImageMain = new Image
{
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
Aspect = Aspect.AspectFill,
Source = ImageMainSource
};
imageContainer = new ContentView
{
Content = ImageMain,
BackgroundColor = Xamarin.Forms.Color.Black,
WidthRequest = App.ScreenWidth - 250
};
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += OnPanUpdated;
imageContainer.GestureRecognizers.Add(panGesture);
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += OnPinchUpdated;
imageContainer.GestureRecognizers.Add(pinchGesture);
double smallImageHeight = ImageHeight / (ImageWidth / 320);
absoluteLayout = new AbsoluteLayout
{
HeightRequest = App.ScreenHeight,
BackgroundColor = Xamarin.Forms.Color.Black,
};
AbsoluteLayout.SetLayoutFlags(imageContainer, AbsoluteLayoutFlags.All);
AbsoluteLayout.SetLayoutBounds(imageContainer, new Rectangle(0f, 0f, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
absoluteLayout.Children.Add(imageContainer, new Rectangle(0, 0, 1, 1), AbsoluteLayoutFlags.All);
Content = absoluteLayout;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6156 次 |
最近记录: |