在进行缩放手势时替换 ScrollViewer 中的图像
Posted
技术标签:
【中文标题】在进行缩放手势时替换 ScrollViewer 中的图像【英文标题】:Replacing Image in ScrollViewer while doing a zoom gesture 【发布时间】:2013-01-07 21:16:26 【问题描述】:我正在创建一个照片查看应用程序,我想实现图片的缩放。我创建了一个 ScrollViewer 并在那里放置了一个图像。这一切都是开箱即用的。我可以做一个缩放手势,它会放大图片。现在,我要实现的下一件事是在缩放手势开始时加载图片的高分辨率版本,并在加载时动态交换 Image 控件中的位图。我希望它无缝地发生,以便用户可以继续手势并继续放大并查看更详细的图片。 实现这一目标的最佳方法是什么? 这是我目前拥有的代码。我的代码的问题是,当 Image.Source 被替换时,我的手势被中断并且照片被重置为原始大小。更改 ScrollViewer ZoomFactor 无济于事,因为在替换图像时它似乎会重置。 我有一个带有 Image 属性的 DataModel,该属性最初返回 null,但开始以低分辨率模式从“文件”加载图片,并在加载完成时调用 OnPropertyChanged("Image")。调用 LoadFullImage() 加载全分辨率版本并在完成时调用 OnPropertyChanged("Image")。
以下是 DataModel.cs 的摘录:
public async Task LoadFullImage()
loadFullImageTask = UpdateImage(0);
await loadFullImageTask;
public ImageSource Image
get
if (fullImage != null)
return fullImage;
else if (image != null)
return image;
else
Task loadImageTask = UpdateImage(768);
return null;
public bool FullImageLoading
get return (this.loadFullImageTask != null) && (!this.loadFullImageTask.IsCompleted);
public bool FullImageLoaded
get return this.fullImage != null;
这是我的 MainPage.xaml:
<Grid Background="StaticResource ApplicationPageBackgroundThemeBrush">
<ScrollViewer x:Name="imageViewer" HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Visible" VerticalAlignment="Stretch" MinZoomFactor="1" ZoomMode="Enabled" ViewChanged="imageViewer_ViewChanged">
<Image x:Name="image" Margin="0,0,0,0" Stretch="Uniform" Source="Binding Image" />
</ScrollViewer>
</Grid>
这是我的 MainPage.xaml.cs:
protected override async void OnNavigatedTo(NavigationEventArgs e)
FileOpenPicker filePicker = new FileOpenPicker();
filePicker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
filePicker.FileTypeFilter.Add(".jpg");
StorageFile file = await filePicker.PickSingleFileAsync();
data = new DataModel(file);
imageViewer.DataContext = data;
private async void imageViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
ScrollViewer imageViewer = (ScrollViewer)sender;
if (imageViewer.ZoomFactor > 1)
if (!data.FullImageLoaded && (!data.FullImageLoading))
int oldHeight = ((BitmapImage)data.Image).PixelHeight;
int oldWidth = ((BitmapImage)data.Image).PixelWidth;
double oldHOffset = imageViewer.HorizontalOffset;
double oldVOffset = imageViewer.VerticalOffset;
await data.LoadFullImage();
int newHeight = ((BitmapImage)data.Image).PixelHeight;
int newWidth = ((BitmapImage)data.Image).PixelWidth;
float ratio = (float)oldHeight / (float)newHeight;
imageViewer.MaxZoomFactor = imageViewer.MaxZoomFactor * ratio;
imageViewer.MinZoomFactor = imageViewer.MinZoomFactor * ratio;
imageViewer.ZoomToFactor(imageViewer.ZoomFactor * ratio);
//imageViewer.ScrollToHorizontalOffset(oldHOffset / ratio);
//imageViewer.ScrollToVerticalOffset(oldVOffset / ratio);
正如我已经提到的,这段代码的问题是手势被中断并且新图像没有调整大小/滚动到正确的位置,用户体验不是无缝的。 感谢您提供有关如何解决此问题的任何建议。
【问题讨论】:
【参考方案1】:我不会为你写这段代码,因为它会太多,但这里是步骤:
-
图像需要将 ScaleTransform 应用于其 RenderTransform 属性。当用户捏或拉伸时,您将使用它来使其“缩放”。
使用操作事件检测捏合手势;如果它可以帮助您减少一些代码,您甚至可以仅过滤到捏合手势。这些事件将是最可靠的。
Manipulation 事件将返回发生了多少捏合或拉伸,这需要与您在数字 1 中应用到图像的 ScaleTransform 的缩放程度相对应。
在代码隐藏中,您可以创建一个 BitmapImage 指向并从服务器加载图像。加载后,您可以替换用户正在捏的图像的来源。除非高分辨率图像是数兆字节,否则这对用户来说几乎是无缝的。不要那样做。
请注意,我的步骤不包括 ScrollViewer。 ScrollViewer 当然可以包装图像,但工作本身是作为图像本身的一部分完成的。
我可能还会向您指出 MultiScaleImage
控件,您可能想从中窃取一些逻辑以做到最好:http://msdn.microsoft.com/en-us/library/system.windows.controls.multiscaleimage%28v=vs.95%29.aspx
还有一件事。这可以通过 DeepZoom 轻松完成。由于它在 XAML 中尚不可用,这并不意味着您不能利用 WebView 来完成此操作。它将为您提供一个预先构建的、可疯狂扩展(且免费)的解决方案。但选择权在你。
【讨论】:
杰瑞,我认为当 ScrollViewer 不支持缩放手势时,需要使用 ScaleTransform 的方法。目前它与单张图片完美搭配——我可以非常流畅地放大。此外,我的 ScrollViewer 包含在 FlipView 控件中,来自 ScrollViewer 和 FlipView 控件的手势可以很好地协同工作:我可以在需要时翻转图像并放大。你是说在 ScrollViewer 中无法无缝切换图片?【参考方案2】:有人记得这个帖子吗? =) 好的, 我在my app 上实现了“在缩放期间替换图像”。 正如上面提到的@Vitary,如果“内容”的大小发生变化,滚动查看器会重置缩放比例和 X/Y 偏移量。 为避免这种情况,我的应用 xaml 使用以下结构:
<FlipView>
<DataTemplate>
<ScrollViewer>
<ViewBox> <-- This size is same with original image width/height. No change during zoom in/out.
<Image/> <-- This size is vary.
</ViewBox>
</ScrollViewer>
</DataTemplate>
</FlipView>
通过保持 viewbox 的大小相同,可以避免滚动查看器的重置。 我的实现是...
-
用户选择翻转视图项目。在这个序列中,viewsource 代码将原始图像的 w/h 设置为 viewsource 成员。此成员绑定到 viewbox 的 w/h。
进行低分辨率解码。然后通过绑定设置为图像控制。如您所知,viewbox 可以自动拉伸内容以适应 viewbox 的大小。
当用户做出缩放手势时,代码隐藏通知 ViewModel。 ViewModel 启动 original-res 解码,然后通过绑定将图像设置为图像控件。在这个序列中,滚动查看器可以保持偏移和缩放比例。因为,viewbox 的大小没有改变。从低分辨率到原始分辨率的切换非常流畅,没有故障。
【讨论】:
以上是关于在进行缩放手势时替换 ScrollViewer 中的图像的主要内容,如果未能解决你的问题,请参考以下文章
我们可以在 Android 中使用缩放手势检测器进行缩放吗?
在 iOS 10 上的 WKWebView 中打开 PDF 文件时启用捏合缩放手势