# 页面过渡

`PageTransitions` 用于在两个视图之间呈现过渡效果，例如在 [Carousel](https://docs.avaloniaui.net/docs/controls/carousel) 或 [TransitioningContentControl](https://docs.avaloniaui.net/docs/controls/TransitioningContentControl) 中。

{% hint style="warning" %}
必须在使用过渡效果之前设置持续时间，并且持续时间必须大于0。否则，会出现错误。
{% endhint %}

## 内置 PageTransitions

### CrossFade

`CrossFade` 通过动画设置透明度来淡出当前视图并淡入新视图。

{% tabs %}
{% tab title="XAML" %}

```markup
<CrossFade Duration="0:00:00.500" />
```

{% endtab %}

{% tab title="C#" %}

```csharp
var transition = new CrossFade(TimeSpan.FromMilliseconds(500));
```

{% endtab %}
{% endtabs %}

#### 源代码

[CrossFade.cs](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Visuals/Animation/CrossFade.cs)

#### 参考

[CrossFade](http://reference.avaloniaui.net/api/Avalonia.Animation/CrossFade/)

### PageSlide

`PageSlide` 水平或垂直地滑动内容。您可以通过 `Orientation` 属性指定滑动轴。默认值为 `Horizontal`。

{% tabs %}
{% tab title="XAML" %}

```markup
<PageSlide Duration="0:00:00.500" Orientation="Vertical" />
```

{% endtab %}

{% tab title="C#" %}

```csharp
var transition = new PageSlide(TimeSpan.FromMilliseconds(500), PageSlide.SlideAxis.Vertical);
```

{% endtab %}
{% endtabs %}

#### 源代码

[PageSlide.cs](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Visuals/Animation/PageSlide.cs)

#### 参考

[PageSlide](http://reference.avaloniaui.net/api/Avalonia.Animation/PageSlide/)

### CompositePageTransition

`CompositePageTransition` 用于创建几个不同过渡效果的组合过渡效果。下面的示例创建了一个过渡效果，将视图对角线滑动（水平和垂直同时）并淡出和淡入视图。

{% tabs %}
{% tab title="XAML" %}

```markup
<CompositePageTransition>
    <CrossFade Duration="0:00:00.500" />
    <PageSlide Duration="0:00:00.500" Orientation="Horizontal" />
    <PageSlide Duration="0:00:00.500" Orientation="Vertical" />
</CompositePageTransition>
```

{% endtab %}

{% tab title="C#" %}

```csharp
var compositeTransition = new CompositePageTransition();
compositeTransition.PageTransitions.Add(new PageSlide(TimeSpan.FromMilliseconds(500), PageSlide.SlideAxis.Vertical));
compositeTransition.PageTransitions.Add(new PageSlide(TimeSpan.FromMilliseconds(500), PageSlide.SlideAxis.Horizontal));
compositeTransition.PageTransitions.Add(new CrossFade(TimeSpan.FromMilliseconds(500)));
```

{% endtab %}
{% endtabs %}

#### 源代码

[CompositePageTransition.cs](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Visuals/Animation/CompositePageTransition.cs)

#### 参考

[CompositePageTransition](http://reference.avaloniaui.net/api/Avalonia.Animation/CompositePageTransition/)

## 自定义页面过渡效果

您还可以通过实现 `IPageTransition` 接口来创建自己的 `PageTransition`。

该接口有一个成员需要实现：

```csharp
public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{
    // 在此设置过渡效果。
}
```

### 示例

以下示例将缩小旧视图并在垂直方向上放大新视图。

```csharp
public class CustomTransition : IPageTransition
{
    /// <summary>
    /// Initializes a new instance of the <see cref="CustomTransition"/> class.
    /// </summary>
    public CustomTransition()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="CustomTransition"/> class.
    /// </summary>
    /// <param name="duration">The duration of the animation.</param>
    public CustomTransition(TimeSpan duration)
    {
        Duration = duration;
    }

    /// <summary>
    /// Gets the duration of the animation.
    /// </summary>
    public TimeSpan Duration { get; set; }

    public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            return;
        }

        var tasks = new List<Task>();
        var parent = GetVisualParent(from, to);
        var scaleYProperty = ScaleTransform.ScaleYProperty;

        if (from != null)
        {
            var animation = new Animation
            {
                FillMode = FillMode.Forward,
                Children =
                {
                    new KeyFrame
                    {
                        Setters = { new Setter { Property = scaleYProperty, Value = 1d } },
                        Cue = new Cue(0d)
                    },
                    new KeyFrame
                    {
                        Setters =
                        {
                            new Setter
                            {
                                Property = scaleYProperty,
                                Value = 0d
                            }
                        },
                        Cue = new Cue(1d)
                    }
                },
                Duration = Duration
            };
            tasks.Add(animation.RunAsync(from, null, cancellationToken));
        }

        if (to != null)
        {
            to.IsVisible = true;
            var animation = new Animation
            {
                FillMode = FillMode.Forward,
                Children =
                {
                    new KeyFrame
                    {
                        Setters =
                        {
                            new Setter
                            {
                                Property = scaleYProperty,
                                Value = 0d
                            }
                        },
                        Cue = new Cue(0d)
                    },
                    new KeyFrame
                    {
                        Setters = { new Setter { Property = scaleYProperty, Value = 1d } },
                        Cue = new Cue(1d)
                    }
                },
                Duration = Duration
            };
            tasks.Add(animation.RunAsync(to, null, cancellationToken));
        }

        await Task.WhenAll(tasks);

        if (from != null && !cancellationToken.IsCancellationRequested)
        {
            from.IsVisible = false;
        }
    }

    /// <summary>
    /// Gets the common visual parent of the two control.
    /// </summary>
    /// <param name="from">The from control.</param>
    /// <param name="to">The to control.</param>
    /// <returns>The common parent.</returns>
    /// <exception cref="ArgumentException">
    /// The two controls do not share a common parent.
    /// </exception>
    /// <remarks>
    /// Any one of the parameters may be null, but not both.
    /// </remarks>
    private static IVisual GetVisualParent(IVisual? from, IVisual? to)
    {
        var p1 = (from ?? to)!.VisualParent;
        var p2 = (to ?? from)!.VisualParent;

        if (p1 != null && p2 != null && p1 != p2)
        {
            throw new ArgumentException("Controls for PageSlide must have same parent.");
        }

        return p1 ?? throw new InvalidOperationException("Cannot determine visual parent.");
    }
}
```

![Custom Transition Example](E:%5CRepos%5CAvaloniaChineseDoc.gitbook%5Cassets%5CTransitioningContentControl_03.webp)

#### Source code

[IPageTransition.cs](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Visuals/Animation/IPageTransition.cs)

#### Reference

[IPageTransition](http://reference.avaloniaui.net/api/Avalonia.Animation/IPageTransition/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pwangs-organization.gitbook.io/avaloniachinese/docs/animations/pagetransitions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
