5

写真表示アプリを作成しており、写真のズームを実装したいと考えています。ScrollViewer を作成し、そこに Image を配置しました。それはすべて箱から出して動作します。ズーム ジェスチャを実行すると、画像が拡大されます。次に実装したいのは、ズーム ジェスチャの開始時に画像の高解像度バージョンを読み込み、読み込まれたときに Image コントロールのビットマップを動的に交換することです。ユーザーがジェスチャーを続けてズームインし続け、より詳細な画像を表示できるように、シームレスに実行したいと考えています。これを達成するための最良のアプローチは何ですか?これは私が現在持っているコードです。私のコードの問題は、 Image.Source が置き換えられると、ジェスチャーが中断され、写真が元のサイズにリセットされることです。ScrollViewer ZoomFactor を変更しても、イメージが置き換えられるときにリセットされるように見えるため、役に立ちません。最初は null を返しますが、低解像度モードで「ファイル」から画像の読み込みを開始し、読み込みが終了すると OnPropertyChanged("Image") を呼び出す Image プロパティを持つ DataModel があります。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);
            }
        }
    }

既に述べたように、このコードの問題は、ジェスチャが中断され、新しい画像が正しい位置にサイズ変更/スクロールされないことです。ユーザー エクスペリエンスはシームレスではありません。これを解決する方法についての提案をありがとう。

4

2 に答える 2

0

このスレ覚えてる人いる?=) OK、アプリ
に「ズーム中の画像の置き換え」を実装しました。 上記の @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>

ビューボックスのサイズを同じに保つことで、スクロール ビューアーのリセットを回避できます。私の実装は...

  1. ユーザーがフリップビューのアイテムを選択します。このシーケンスで、viewsource コードは元の画像の w/h を viewsource メンバーに設定します。このメンバーは、viewbox の w/h にバインドされています。
  2. 低解像度デコードを行います。次に、バインドによってイメージ コントロールに設定します。ご存知のように、viewbox は自動的にビューボックスのサイズに合わせてコンテンツを引き伸ばすことができます。
  3. ユーザーがズーム ジェスチャを行うと、分離コードが ViewModel に通知します。ViewModel は original-res のデコードを開始し、バインディングによって画像を画像コントロールに設定します。このシーケンスの間、スクロール ビューアはオフセットとズーム率を維持できます。ビューボックスのサイズは変更されないためです。ローレゾからオリジナルレゾへの切り替えはスムーズでグリッチがありません。
于 2015-02-09T12:08:18.460 に答える
0

このコードは長すぎるので書きませんが、手順は次のとおりです。

  1. 画像の RenderTransform プロパティに ScaleTransform を適用する必要があります。これを使用して、ユーザーがつまんだり伸ばしたりしているときに「ズーム」します。
  2. Manipulation イベントを使用してピンチ ジェスチャを検出します。コードを減らすのに役立つ場合は、ピンチ ジェスチャのみにフィルター処理することもできます。これらのイベントは最も信頼できます。
  3. Manipulation イベントは、発生しているピンチまたはストレッチの量を返します。これは、1 番目の画像の ScaleTransform に適用するズームの量に対応する必要があります。
  4. コード ビハインドでは、サーバーから画像を参照して読み込む BitmapImage を作成できます。読み込まれたら、ユーザーがピンチしている画像のソースを置き換えることができます。高解像度画像が数メガバイトでない限り、これはユーザーにとってほぼシームレスです。そうしないでください。

私の手順には ScrollViewer が含まれていないことに注意してください。ScrollViewer は確かに画像をラップできますが、作業自体は Image 自体の一部として行われます。

また、MultiScaleImageこれを最善の方法で実行するために、いくつかのロジックを盗むことができるコントロールを紹介するかもしれません: http://msdn.microsoft.com/en-us/library/system.windows.controls.multiscaleimage%28v= vs.95%29.aspx

もう一つあります。これは、DeepZoom を使用すると非常に簡単に実現できます。XAML ではまだ利用できないため、これを実現するために WebView を利用できないわけではありません。それは、事前に構築された非常にスケーラブルな (そして無料の) ソリューションを提供します。しかし、選択はあなた次第です。

于 2013-01-24T19:30:49.950 に答える