手势,允许用户通过在触摸显示屏的同时将两根手指靠近在一起来放大。
我已经分别实现了平移和捏合,并且效果很好。我现在尝试同时使用捏合和平移,但发现了一些问题。这是我的代码: XAML: 我已经分别实现了平移和捏合,效果很好。我现在尝试同时使用捏合和平移,但发现了一些问题。这是我的代码: 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> 捏/平移手势添加: var panGesture = new PanGestureRecognizer(); panGesture.PanUpdated += OnPanUpdated; GestureRecognizers.Add(panGesture); var pinchGesture = new PinchGestureRecognizer(); pinchGesture.PinchUpdated += OnPinchUpdated; GestureRecognizers.Add(pinchGesture); 平移方法: 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; } } 捏法: 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; } } 如果我关闭其中一个手势并仅使用另一个手势,则该功能可以完美运行。当我添加平移和捏合手势时,问题就出现了。似乎发生的事情是这样的: 1)平底锅实际上似乎按预期工作 2) 当您最初平移图像时,例如,将图像移动到 Y 中心和 X 中心,然后尝试缩放,图像会设置回其初始状态。然后,当您平移时,它会将您移回到尝试缩放之前的位置(这就是为什么我说平移工作正常)。 据我从调试中了解到,当您缩放时,它没有考虑您当前所在的位置。因此,当您先平移,然后缩放时,它不会缩放您所在的位置,而是缩放图像的起点。然后,当您尝试从那里平移时,平移方法仍然会记住您所在的位置,并且它会将您移回到尝试缩放之前的位置。 希望对此有所了解。显然我的捏法有问题。我只是认为(显然无法弄清楚)我需要在其中添加考虑到您当前所处位置的逻辑。 主要原因可能是每个人似乎都复制并使用此代码(来自 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); } } 捏合处理程序同样简单,根本不需要计算任何平移。我们所要做的就是将锚点设置为捏合起点,框架将完成其余的工作,缩放将围绕该点进行。请注意,我们甚至在这里还有一个额外的功能,即变焦范围两端过冲时的弹性反弹。 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; } } 还有平移处理程序,甚至更简单。开始时,我们从锚点计算起点,在平移过程中,我们不断更改锚点。该锚点相对于视图区域,我们可以轻松地将其限制在 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; } } 使用的常量和变量就是这些: 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; 采用完全不同的方法来处理这个问题。对于任何遇到问题的人来说,这都是 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; } } 捏合更新 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); } } OnSizeAlulated(其中大部分您可能不需要,但有些您需要。考虑 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; } } 布局(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; 我一直在开发具有平移和缩放功能的图像查看器... 我达到了另一种变化。 我来分享给你。 首先,我们需要一个平移/缩放类控制器: using System; using Xamarin.Forms; namespace Project.Util { public class PanZoom { bool pitching = false; bool panning = false; bool collectFirst = false; double xOffset = 0; double yOffset = 0; //scale processing... double scaleMin; double scaleMax; double scale; double _xScaleOrigin; double _yScaleOrigin; double panTotalX; double panTotalY; ContentPage contentPage; View Content; public void Setup(ContentPage cp, View content) { contentPage = cp; Content = content; PinchGestureRecognizer pinchGesture = new PinchGestureRecognizer(); pinchGesture.PinchUpdated += PinchUpdated; contentPage.Content.GestureRecognizers.Add(pinchGesture); var panGesture = new PanGestureRecognizer(); panGesture.PanUpdated += OnPanUpdated; contentPage.Content.GestureRecognizers.Add(panGesture); contentPage.SizeChanged += (sender, e) => { layoutElements(); }; } public void layoutElements() { if (contentPage.Width <= 0 || contentPage.Height <= 0 || Content.WidthRequest <= 0 || Content.HeightRequest <= 0) return; xOffset = 0; yOffset = 0; double pageW = contentPage.Width; double pageH = contentPage.Height; double w_s = pageW / Content.WidthRequest; double h_s = pageH / Content.HeightRequest; if (w_s < h_s) scaleMin = w_s; else scaleMin = h_s; scaleMax = scaleMin * 3.0; scale = scaleMin; double w = Content.WidthRequest * scale; double h = Content.HeightRequest * scale; double x = pageW / 2.0 - w / 2.0 + xOffset; double y = pageH / 2.0 - h / 2.0 + yOffset; AbsoluteLayout.SetLayoutBounds(Content, new Rectangle(x, y, w, h)); } void fixPosition( ref double x, ref double y, ref double w, ref double h, bool setoffset ) { double pageW = contentPage.Width; double pageH = contentPage.Height; if (w <= pageW) { double new_x = pageW / 2.0 - w / 2.0; if (setoffset) xOffset = new_x - (pageW / 2.0 - w / 2.0); x = new_x; } else { if (x > 0) { double new_x = 0; if (setoffset) xOffset = new_x - (pageW / 2.0 - w / 2.0); x = new_x; } if (x < (pageW - w)) { double new_x = (pageW - w); if (setoffset) xOffset = new_x - (pageW / 2.0 - w / 2.0); x = new_x; } } if (h <= pageH) { double new_y = pageH / 2.0 - h / 2.0; if (setoffset) yOffset = new_y - (pageH / 2.0 - h / 2.0); y = new_y; } else { if (y > 0) { double new_y = 0; if (setoffset) yOffset = new_y - (pageH / 2.0 - h / 2.0); y = new_y; } if (y < (pageH - h)) { double new_y = (pageH - h); if (setoffset) yOffset = new_y - (pageH / 2.0 - h / 2.0); y = new_y; } } } private void PinchUpdated(object sender, PinchGestureUpdatedEventArgs e) { if (sender != contentPage.Content) return; switch (e.Status) { case GestureStatus.Started: { pitching = true; collectFirst = true; double pageW = contentPage.Width; double pageH = contentPage.Height; _xScaleOrigin = e.ScaleOrigin.X * pageW; _yScaleOrigin = e.ScaleOrigin.Y * pageH; } break; case GestureStatus.Running: if (pitching) { double targetScale = scale * e.Scale; targetScale = Math.Min(Math.Max(scaleMin, targetScale), scaleMax); double scaleDelta = targetScale / scale; double pageW = contentPage.Width; double pageH = contentPage.Height; double w_old = Content.WidthRequest * scale; double h_old = Content.HeightRequest * scale; double x_old = pageW / 2.0 - w_old / 2.0 + xOffset; double y_old = pageH / 2.0 - h_old / 2.0 + yOffset; scale = targetScale; //new w and h double w = Content.WidthRequest * scale; double h = Content.HeightRequest * scale; //transform x old and y old // to get new scaled position over a pivot double _x = (x_old - _xScaleOrigin) * scaleDelta + _xScaleOrigin; double _y = (y_old - _yScaleOrigin) * scaleDelta + _yScaleOrigin; //fix offset to be equal to _x and _y double x = pageW / 2.0 - w / 2.0 + xOffset; double y = pageH / 2.0 - h / 2.0 + yOffset; xOffset += _x - x; yOffset += _y - y; x = pageW / 2.0 - w / 2.0 + xOffset; y = pageH / 2.0 - h / 2.0 + yOffset; fixPosition(ref x, ref y, ref w, ref h, true); AbsoluteLayout.SetLayoutBounds(Content, new Rectangle(x, y, w, h)); } break; case GestureStatus.Completed: pitching = false; break; } } public void OnPanUpdated(object sender, PanUpdatedEventArgs e) { if (sender != contentPage.Content) return; switch (e.StatusType) { case GestureStatus.Started: { panning = true; panTotalX = e.TotalX; panTotalY = e.TotalY; collectFirst = true; } break; case GestureStatus.Running: if (panning) { if (collectFirst) { collectFirst = false; panTotalX = e.TotalX; panTotalY = e.TotalY; } double pageW = contentPage.Width; double pageH = contentPage.Height; double deltaX = e.TotalX - panTotalX; double deltaY = e.TotalY - panTotalY; panTotalX = e.TotalX; panTotalY = e.TotalY; xOffset += deltaX; yOffset += deltaY; double w = Content.WidthRequest * scale; double h = Content.HeightRequest * scale; double x = pageW / 2.0 - w / 2.0 + xOffset; double y = pageH / 2.0 - h / 2.0 + yOffset; fixPosition(ref x, ref y, ref w, ref h, true); AbsoluteLayout.SetLayoutBounds(Content, new Rectangle(x, y, w, h)); } break; case GestureStatus.Completed: panning = false; break; } } } } 在内容页面: using System; using FFImageLoading.Forms; using Xamarin.Forms; using Project.Util; namespace Project.ContentPages { public class ContentPage_ImageViewer : ContentPage { AbsoluteLayout al = null; CachedImage image = null; PanZoom panZoom; public ContentPage_ImageViewer(string imageURL) { MasterDetailPage mdp = Application.Current.MainPage as MasterDetailPage; mdp.IsGestureEnabled = false; NavigationPage.SetHasBackButton(this, true); Title = ""; image = new CachedImage() { HorizontalOptions = LayoutOptions.FillAndExpand, VerticalOptions = LayoutOptions.FillAndExpand, Aspect = Aspect.Fill, LoadingPlaceholder = "placeholder_320x322.png", ErrorPlaceholder = "placeholder_320x322.png", Source = imageURL, RetryCount = 3, DownsampleToViewSize = false, IsVisible = false, FadeAnimationEnabled = false }; image.Success += delegate (object sender, CachedImageEvents.SuccessEventArgs e) { Device.BeginInvokeOnMainThread(() => { image.WidthRequest = e.ImageInformation.OriginalWidth; image.HeightRequest = e.ImageInformation.OriginalHeight; image.IsVisible = true; for(int i = al.Children.Count-1; i >= 0; i--) { if (al.Children[i] is ActivityIndicator) al.Children.RemoveAt(i); } panZoom.layoutElements(); }); }; ActivityIndicator ai = new ActivityIndicator() { IsRunning = true, Scale = (Device.RuntimePlatform == Device.Android) ? 0.25 : 1.0, VerticalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill, Color = Color.White }; Content = (al = new AbsoluteLayout() { VerticalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill, BackgroundColor = Color.Black, Children = { image, ai } }); AbsoluteLayout.SetLayoutFlags(image, AbsoluteLayoutFlags.None); AbsoluteLayout.SetLayoutBounds(ai, new Rectangle(0, 0, 1, 1)); AbsoluteLayout.SetLayoutFlags(ai, AbsoluteLayoutFlags.All); panZoom = new PanZoom(); panZoom.Setup(this, image); } } } 我有正在生产中运行的工作代码。您在平移函数中使用的 x 和 y 变量与捏合函数中的 xOffset 和 yOffset 相同。因此,删除 x 和 y 变量,只使用 xOffset 和 yOffset 代替它们。 希望有帮助。 对我来说,它的工作原理如下,只是对问题中给出的代码做了一些更改, 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; Content.AnchorX = 0; Content.AnchorY = 0; } if (e.Status == GestureStatus.Running) { // Calculate the scale factor to be applied. currentScale += (e.Scale - 1) * startScale; currentScale = Math.Max(1, currentScale); // 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; width = Content.Width * currentScale; height = Content.Height * currentScale; } if (e.Status == GestureStatus.Completed) { // Store the translation delta's of the wrapped user interface element. xOffset = Content.TranslationX; yOffset = Content.TranslationY; x = Content.TranslationX; y = Content.TranslationY; } } 泛码 void OnPanUpdated(object sender, PanUpdatedEventArgs e) { if (!width.Equals(Content.Width) && !height.Equals(Content.Height)) { switch (e.StatusType) { case GestureStatus.Started: startX = Content.TranslationX; startY = Content.TranslationY; break; case GestureStatus.Running: if (!width.Equals(0)) { Content.TranslationX = Math.Max(Math.Min(0, x + e.TotalX), -Math.Abs(Content.Width - width));// App.ScreenWidth)); } if (!height.Equals(0)) { Content.TranslationY = Math.Max(Math.Min(0, y + e.TotalY), -Math.Abs(Content.Height - height)); //App.ScreenHeight)); } break; case GestureStatus.Completed: // Store the translation applied during the pan x = Content.TranslationX; y = Content.TranslationY; xOffset = Content.TranslationX; yOffset = Content.TranslationY; break; } } }
Swift 5 / Xcode 12.4 我有一个 png 图像,已下载到 Documents 文件夹中,然后在运行时加载(当前为 UIImage)。该图像必须充当某种类型的地图: 捏动物园...
我找到了这个用于捏缩放的脚本,但这是用于相机放大和缩小的,请告诉我它如何适用于对象,更重要的是对于单个对象,这意味着如果我想缩放c...
我正在开发一个 PWA,我想在整个 UI 上禁用捏缩放,并只允许在页面内的某些元素上使用捏缩放。 相关CSS: * { 触摸动作:pan-y; } 不确定是否相关...
我正在使用几何读取器跟踪矩形网格中所有矩形的位置,以便稍后与它们进行交互。现在我想添加放大和滚动到 g 的选项...
由于似乎没有办法通过js直接设置浏览器的缩放级别,所以我想重写浏览器缩放并编写自己的代码。 当从scale = 1开始时,这段代码可以正常工作,但我...
所以我有一个包含图像的页面,可以单击该页面来显示放大的图像。我想允许用户进一步放大,在图像的各个部分上进行捏合动作/滚轮,就像平常一样......
如何使用 detectorTransformGestures 但不消耗所有指针事件
我正在制作一个全屏照片查看器,其中包含一个寻呼机(使用 HorizontalPager)和每个页面,用户可以放大/缩小和平移图像,但仍然可以滑动页面。 我的想法是刷...
我使用 Hammerjs 进行滑动和点击。然而,一旦我开始在任何元素上使用它 var Hammer = new Hammer(document.getElementById("square")); 默认浏览器的“捏”行为
我正在尝试在我的应用程序中创建一个相机,该相机允许用户在使用相机时捏合预览屏幕。我一直在尝试实现捏合来放大我的相机,但我没有运气......
SwiftUI - zIndex 更改不适用于 LazyVStack 内部的项目
我的应用程序中的 zIndex 与图像集合有问题 我已向图像添加了捏缩放功能,但是当缩放图像时,它不会位于集合中下一个图像的顶部。 ...
我正在开发应用程序中的一项功能,可以进行放大和缩小。我注意到,当我放大时,即使我的两个手指仍在屏幕上,我的应用程序也会收到 onScaleEnd() 。因为...
我正在我的 SwiftUI 应用程序中处理缩放和平移主题。我使用以下 View 结构来显示图像 结构 PictureDetailView: 查看 { @State private var Zoom:双倍 私有变量图像:NSImage 私人...
如何在 Android 上使用“viewport-fit=cover”和“maximum-scale=1”而不禁用缩放
我一直在我的网站上使用以下视口元标记: 在我...
reanimated版本问题(无法确定Reanimated原生部分的版本。)
我正在尝试练习这个视频。 https://youtu.be/R7vyLItMQJw (PinchGestureHandler 与 React Native Reanimated 2 的基础知识) 但我的应用程序无法触摸。 我收到此错误 [复活] 可以...
我正在尝试获得与 Instagram 类似的功能,当您点击或 捏住图像,现在图像覆盖了屏幕上方的所有图像 其他观点,例如 这 我有一个包含 3 个图像的片段...
如何实现Qt3D Camera的FOV的两指捏合手势处理? 有 FirstPersonCameraController 和 OrbitCameraController 相机控制器处理鼠标/触摸板事件。
每当我们尝试在特定位置(纬度、经度)上放大或缩小时,它都不会粘在该位置上,并且会在这里和那里稍微移动。我们希望实现与 Zo 类似的功能...
如何捏放缩放 Android Studio IDE 中嵌入的 Android 模拟器(2020)
在普通模拟器中似乎可以通过按 ctrl 和鼠标左键来工作,但从 Android 4.1.1 开始,模拟器可以嵌入到 IDE 中,我无法执行捏缩放。 任何帮助!谢谢。