質問

Today I started porting the page turn sample created here for Windows Phone 7 to WinRT (XAML, C#) for helping this question posted in Stack Overflow. But during porting I got stuck with the clipping portion of page. In the windows phone sample they are using Path Geometry clipping for clipping the page. But in WinRT It seems only rectangle geometry is supporting for clipping option.

How can I achieve similar functionality like Path geometry clipping in WinRT?

You can download the sample windows phone code from here

Please find the Source code i Tried please download

In that Please find the class PageTurn.cs, I commented the code with issues, in:

void left_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    //_workingOdd.Clip = _oddClipRegion;       
    //_workingEven.Clip = _evenClipRegion;
}
役に立ちましたか?

解決 3

I'm a bit late to give a more complete answer, but I got inspired and started working on a reusable control/page transition. The current prototype is working quite nicely with RenderTransforms and Clip Transforms for nice independent animation support.

enter image description here

I'll work on that reusable control for WinRT XAML Toolkit, but meanwhile you can check this code:

XAML

<Page
    x:Class="FlipControls.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:FlipControls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid
        x:Name="ManipulationGrid"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        ManipulationMode="TranslateX,TranslateY,TranslateInertia"
        ManipulationStarted="ManipulationGrid_OnManipulationStarted"
        ManipulationDelta="ManipulationGrid_OnManipulationDelta"
        ManipulationCompleted="ManipulationGrid_OnManipulationCompleted">
        <Grid
            x:Name="Page1">
            <Grid.Clip>
                <RectangleGeometry
                    Rect="0,0,80000,80000">
                    <RectangleGeometry.Transform>
                        <TransformGroup>
                            <TranslateTransform
                                x:Name="Page1ClipTranslateTransform" />
                            <RotateTransform
                                x:Name="Page1ClipRotateTransform" />
                        </TransformGroup>
                    </RectangleGeometry.Transform>
                </RectangleGeometry>
            </Grid.Clip>
            <Grid
                x:Name="Page1ContentGrid">
                <Image
                    VerticalAlignment="Stretch"
                    HorizontalAlignment="Stretch"
                    Stretch="UniformToFill"
                    Source="http://bigbackground.com/wp-content/uploads/2013/07/tropical-beach-screensaver.jpg" />
            </Grid>
        </Grid>
        <Grid
            x:Name="Page2"
            Opacity="0">
            <Grid.Clip>
                <RectangleGeometry
                    Rect="0,0,80000,80000">
                    <RectangleGeometry.Transform>
                        <TransformGroup>
                            <TranslateTransform
                                x:Name="Page2ClipTranslateTransform" />
                            <RotateTransform
                                x:Name="Page2ClipRotateTransform" />
                        </TransformGroup>
                    </RectangleGeometry.Transform>
                </RectangleGeometry>
            </Grid.Clip>
            <Grid
                x:Name="Page2ContentGrid">
                <Image
                    x:Name="Page2SampleContentImage"
                    VerticalAlignment="Stretch"
                    HorizontalAlignment="Stretch"
                    Stretch="UniformToFill"
                    Source="http://www.photography-match.com/views/images/gallery/Tropical_Lagoon.jpg" />
            </Grid>
        </Grid>
        <Grid
            x:Name="TransitionGridContainer">
            <Grid
                x:Name="TransitionGrid"
                Opacity="0">
                <Grid.RenderTransform>
                    <CompositeTransform
                        x:Name="TransitionGridContainerTransform" />
                </Grid.RenderTransform>
                <Grid.Clip>
                    <RectangleGeometry
                        Rect="0,0,80000,80000">
                        <RectangleGeometry.Transform>
                            <TransformGroup>
                                <TranslateTransform
                                    x:Name="TransitionGridClipTranslateTransform" />
                                <RotateTransform
                                    x:Name="TransitionGridClipRotateTransform" />
                            </TransformGroup>
                        </RectangleGeometry.Transform>
                    </RectangleGeometry>
                </Grid.Clip>
                <Image
                    x:Name="TransitionImage"
                    Stretch="None" />
            </Grid>
        </Grid>
    </Grid>
</Page>

C#

using System;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Media.Imaging;

namespace FlipControls
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            PreloadTransitionGridContentAsync();
        }

        private async Task PreloadTransitionGridContentAsync()
        {
            #region Waiting for page 2 content to load
            var bi = Page2SampleContentImage.Source as BitmapImage;

            if (bi.PixelWidth == 0)
            {
                bi.ImageFailed += (s, e) => new MessageDialog("Need a different sample image.").ShowAsync();
                bi.ImageOpened += (s, e) => PreloadTransitionGridContentAsync();
                return;
            }

            if (Page2ContentGrid.ActualWidth == 0)
            {
                SizeChangedEventHandler sizeChangedEventHandler = null;
                sizeChangedEventHandler = (s, e) =>
                {
                    PreloadTransitionGridContentAsync();
                    Page2ContentGrid.SizeChanged -= sizeChangedEventHandler;
                };

                Page2ContentGrid.SizeChanged += sizeChangedEventHandler;

                return;
            } 
            #endregion

            var rtb = new RenderTargetBitmap();
            await rtb.RenderAsync(Page2ContentGrid);
            TransitionImage.Source = rtb;
            await Task.Delay(40000);
        }

        private bool isCancellationRequested;

        private enum FlipDirections
        {
            Left,
            Right
        }

        private FlipDirections flipDirection;
        private Point manipulationStartPosition;
        private double rotationCenterX;
        private double rotationCenterY;

        private void ManipulationGrid_OnManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
            if (TransitionImage.Source == null)
            {
                CancelManipulation(e);
                return;
            }

            manipulationStartPosition = e.Position;

            if (Page1.Opacity == 1)
            {
                flipDirection = FlipDirections.Left;
                Page2ClipTranslateTransform.X = ManipulationGrid.ActualWidth;
                Page2.Opacity = 1;
                TransitionGridClipTranslateTransform.X = -80000;
                TransitionGridContainerTransform.TranslateX = ManipulationGrid.ActualWidth;
                TransitionGrid.Opacity = .975;
            }
            else
            {
                if (manipulationStartPosition.X >= this.ManipulationGrid.ActualWidth / 2)
                {
                    // Can't flip left since there is no page after the current one
                    CancelManipulation(e);
                    return;
                }

                flipDirection = FlipDirections.Right;

                Page1.Opacity = 1;
            }
        }

        private void ManipulationGrid_OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            if (this.isCancellationRequested)
            {
                return;
            }

            if (flipDirection == FlipDirections.Left)
            {
                var w = this.ManipulationGrid.ActualWidth;
                var h = this.ManipulationGrid.ActualHeight;

                var cx = Math.Min(0, Math.Max(e.Position.X - w, -w));
                var cy = e.Cumulative.Translation.Y;
                var angle = (Math.Atan2(cx + manipulationStartPosition.Y - w, -cy) * 180 / Math.PI + +90) % 360;

                this.rotationCenterX = w + cx / 2;

                if (cy < 0)
                {
                    this.rotationCenterY = h;
                }
                else
                {
                    this.rotationCenterY = 0;
                }

                Page2ClipTranslateTransform.X = w + cx / 2;
                Page2ClipTranslateTransform.Y = -40000 + h / 2;
                Page2ClipRotateTransform.CenterX = this.rotationCenterX;
                Page2ClipRotateTransform.CenterY = this.rotationCenterY;
                Page2ClipRotateTransform.Angle = angle;

                TransitionGridClipTranslateTransform.X = -80000 - (cx / 2);
                TransitionGridClipTranslateTransform.Y = -40000 + h / 2;
                TransitionGridClipRotateTransform.CenterX = -cx / 2;
                TransitionGridClipRotateTransform.CenterY = this.rotationCenterY;
                TransitionGridClipRotateTransform.Angle = -angle;

                TransitionGridContainerTransform.TranslateX = w + cx;
                TransitionGridContainerTransform.CenterX = -cx / 2;
                TransitionGridContainerTransform.CenterY = this.rotationCenterY;
                TransitionGridContainerTransform.Rotation = 2 * angle;
            }
        }

        private void ManipulationGrid_OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            if (this.isCancellationRequested)
            {
                this.isCancellationRequested = false;
                return;
            }

            var w = this.ManipulationGrid.ActualWidth;
            var h = this.ManipulationGrid.ActualHeight;

            var sb = new Storyboard();
            AddAnimation(sb, Page2ClipTranslateTransform, "X", w / 2);
            AddAnimation(sb, Page2ClipRotateTransform, "CenterX", w / 2);
            AddAnimation(sb, Page2ClipRotateTransform, "Angle", 0);

            AddAnimation(sb, TransitionGridClipTranslateTransform, "X", -80000 + (w / 2));
            AddAnimation(sb, TransitionGridClipRotateTransform, "CenterX", w / 2);
            AddAnimation(sb, TransitionGridClipRotateTransform, "Angle", 0);

            AddAnimation(sb, TransitionGridContainerTransform, "TranslateX", 0);
            AddAnimation(sb, TransitionGridContainerTransform, "CenterX", w / 2);
            AddAnimation(sb, TransitionGridContainerTransform, "Rotation", 0);
            sb.Begin();
        }

        private static void AddAnimation(Storyboard sb, DependencyObject dob, string path, double to)
        {
            var da = new DoubleAnimation();
            Storyboard.SetTarget(da, dob);
            Storyboard.SetTargetProperty(da, path);
            da.To = to;
            da.Duration = TimeSpan.FromSeconds(.2);
            sb.Children.Add(da);
        }

        private void CancelManipulation(ManipulationStartedRoutedEventArgs e)
        {
            this.isCancellationRequested = true;
            e.Complete();
        }
    }
}

他のヒント

Hope this helps to you.

Here is the XAML to get exactly the look of the demo you linked to:

Path Calcualtion enter image description here

Image1

 <Grid Background="Black">
    <Grid Height="145" Width="210" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Border Margin="-3" BorderThickness="3" BorderBrush="White" Grid.ColumnSpan="2"></Border>
        <Image Grid.Column="0" Source="Images/1.jpg"></Image>
        <Image Grid.Column="1" Source="Images/2.jpg" ></Image>
        <Image Grid.Column="1" HorizontalAlignment="Left"  Source="Images/8.jpg" >
            <Image.Clip>
                <RectangleGeometry Rect="0,0,49,150"></RectangleGeometry>
            </Image.Clip>
        </Image>
        <Path Grid.ColumnSpan="2"  Data="M 180,0 L 153,145 L 85 120 L 160,-12"   >
            <Path.Fill>
                <ImageBrush ImageSource="Images/4.jpg"/>
            </Path.Fill>
        </Path>
        <!--polyline used for path image border-->
        <Polyline Points="180,0,160,-11,85,120,153,145" Margin="0,-2,0,0"  Stroke="White" StrokeThickness="3"  Grid.ColumnSpan="2"/>
    </Grid>
</Grid>

Image2

    <Grid Background="Black">
    <Grid Width="290" Height="180" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0"  BorderThickness="2,0,0,0" BorderBrush="White">
            <Image Stretch="Fill"  Source="Images/4.jpg"></Image>
        </Border>
        <Image Grid.Column="1" Source="Images/3.jpg" ></Image>
        <Path Grid.ColumnSpan="2"  Data="M 200,0 L 170,180 L 82.5 160 L 130 0 "   >
            <Path.Fill>
                <ImageBrush ImageSource="Images/6.jpg"/>
            </Path.Fill>
        </Path>
        <!--polyline used for path image border-->
        <Polyline Points="130,0,82.5,160,170,180"   Stroke="White" StrokeThickness="2"  Grid.ColumnSpan="2"/>
    </Grid>
</Grid>

OutPut

enter image description here

You can enlarge the size of image by putting image1 and image2 grid in viewbox like below

 <Viewbox Width="500" Height="350">
    <Grid Width="290" Height="180"/>        
 </Viewbox>

So, let's be clear of the status of things.

  1. WinRT XAML is now supported on WP 8.1
  2. WP Silverlight XAML can do more than WinRT XAML
  3. For example, WP SL XAML can use custom paths for clipping
  4. WinRT XAML can only use rectangles for clipping
  5. Reason: Rectangles out-perform any custom shape, period
  6. The XAML team lets performance trump capability
  7. Today, the XAML team has no plans to change clipping

Now we understand the problem. Do we have other options?

If you want to clip an image, you are in luck. You can create a path element of any shape and paint the background with an image brush. This is not technically clipping, but you have the same effect.

<Path Data="M540,394 C544.208,422.053 538.553,441.447 514,466 C490.615,489.385 
485.625,493.625 448,456 C423.947,431.947 425.435,394.188 486,374 C457.465,374 
452,353.019 452,312 C452,280.568 446.005,289.33 478,268 C514.938,243.374 
496.654,264 536,264 C538.338,275.69 546,280.948 546,294 C540.421,280.052 
545.708,255.719 564,242 C577.778,231.667 594.285,223.858 610,216 C617.244,212.378 
619.853,219.804 626,228 C630.353,233.805 671.625,322.65 620,302 C660.196,302 
680,306.666 680,374 C680,393.824 652.592,424.592 614,386 C614,403.28 
621.284,411.789 614,430 C607.693,445.768 601.833,454 580,454 C550.466,454 
548.934,443.082 534,414" HorizontalAlignment="Left" Height="269.872" 
Margin="433.483,215.058,0,0" Stretch="Fill" Stroke="Black" UseLayoutRounding="False" 
VerticalAlignment="Top" Width="247.517">
    <Path.Fill>
        <ImageBrush Stretch="None" ImageSource="Assets/SplashScreen.png"/>
    </Path.Fill>
</Path>

That would render this:

enter image description here

But wait there's more. The ImageBrush itself can be transformed. Meaning you can perform a Translate on the image moving the image around within the path. Moreover you can also apply a rotate on the image.

First, consider an animation like the one FlipBoard does. That gives you a simple page flip animation that looks freaking awesome without actually requiring a complex clipping. Check out this demo: http://blog.jerrynixon.com/2012/12/walkthough-use-xamls-3d-planeprojection.html

Then again, you might simply just want to replicate that Demo. That's fine. The XAML below will give you exactly the effect you want. And it is easy to animate:

<Grid>
    <Rectangle Stroke="Black" Width="800" Height="400">
        <Rectangle.Fill>
            <ImageBrush Stretch="UniformToFill" ImageSource="Assets/car.jpg"/>
        </Rectangle.Fill>
    </Rectangle>
    <Grid Width="800" Height="400">
        <Grid.Clip>
            <RectangleGeometry Rect="0,-400,800,800" />
        </Grid.Clip>
        <Grid Width="800" Height="400" RenderTransformOrigin="0.5,0.5">
            <Grid.RenderTransform>
                <CompositeTransform ScaleX="-1" Rotation="30.957" TranslateX="527" TranslateY="108"/>
            </Grid.RenderTransform>
            <Grid.Background>
                <ImageBrush Stretch="UniformToFill" ImageSource="Assets/car.jpg"/>
            </Grid.Background>
            <Rectangle Fill="Purple" Opacity=".5" />
        </Grid>
    </Grid>
</Grid>

It would look like this:

Here's the XAML to get exactly the look of the demo you linked to:

<Grid Width="800" Height="200" Margin="283,283,283,285">
    <Grid.Clip>
        <RectangleGeometry Rect="-200,-200,1000,400" />
    </Grid.Clip>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="400" />
        <ColumnDefinition Width="400" />
    </Grid.ColumnDefinitions>
    <Path x:Name="Page2" Grid.Column="1" Data="M0.5,0.5 L495,-1 L399.5,199.5 L0.5,199.5 z" Stretch="Fill" Margin="0,-1.5,-95.5,0" UseLayoutRounding="False" RenderTransformOrigin="0.5,0.5" >
        <Path.RenderTransform>
            <CompositeTransform TranslateX="-200"/>
        </Path.RenderTransform>
        <Path.Fill>
            <ImageBrush Stretch="UniformToFill" ImageSource="Assets/Car2.jpg"/>
        </Path.Fill>
    </Path>
    <Path x:Name="Page4" Data="M94,2 L495,-1 L495,201 L0.5,199.5 z" Stretch="Fill" Margin="400,-3.5,-495.5,0.5" UseLayoutRounding="False" RenderTransformOrigin="0.5,0.5" >
        <Path.Fill>
            <ImageBrush Stretch="UniformToFill" ImageSource="Assets/Car4.jpg"/>
        </Path.Fill>
        <Path.RenderTransform>
            <CompositeTransform TranslateX="200"/>
        </Path.RenderTransform>
    </Path>
    <Image x:Name="Page1" Source="Assets/Car1.jpg" Grid.Column="0" Stretch="UniformToFill" />
    <Grid x:Name="Page3" Grid.Column="1" RenderTransformOrigin="0.5,0.5">
        <Grid.RenderTransform>
            <CompositeTransform TranslateX="-156" Rotation="25" TranslateY="-94" ScaleX="1.1" ScaleY="1.1"/>
        </Grid.RenderTransform>
        <Grid.Clip>
            <RectangleGeometry Rect="0,0,400,200" />
        </Grid.Clip>
        <Border BorderThickness="5,5,5,0" BorderBrush="White">
            <Border.RenderTransform>
                <CompositeTransform TranslateX="175"/>
            </Border.RenderTransform>
            <Image x:Name="Page3Image" Source="Assets/Car3.jpg" Stretch="UniformToFill"/>
        </Border>
    </Grid>
</Grid>

Would look like this:

enter image description here

I am not sure how much tweaking this would take to get right, Stephan. My guess is... plenty. The good news is: you can animate transforms on the GPU so most of this should be accelerated. :)

// Jerry

For images - you can simply use an ImageBrush and use it to fill a Path that has the desired geometry. For anything more complicated - use RenderTargetBitmap.Render() to turn your XAML into a bitmap and go back to answer 1.

I feel it needs to be said that for the "Path with ImageBrush Fill" technique it is basically impossible to get the image to align exactly as you want using transforms, because the exact position of the image (of the imagebrush) inside the path clip depends on the extents of the path including Stroke.

So for example if you have a Bezier Path, you would need to calculate the actual extents of that Bezier to be then able to determine the exact scale/translate transform to apply to the image.

Stroke seems innocent, but when you have sharp angles in your poly-line path, the stroke makes a sharp pointy triangle! But also only to an extent, since after the angle gets really sharp, the triangle is not so pointy anymore! So, you would need to replicate the exact algorithm that does the Stroke for a Path.

Finally, I do need to admit there is the option to duplicate your Path for the Stroke, so have one with ImageBrush Fill and the other one without but with Stroke.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top