Xamarin.Forms 中的简单动画
ViewExtensions 类提供可用于构造简单动画的扩展方法。 本文演示如何使用 ViewExtensions 类创建和取消动画。
ViewExtensions
类提供可用于创建简单动画的以下扩展方法:
CancelAnimations
取消任何动画。FadeTo
对VisualElement
的Opacity
属性进行动画处理。RelScaleTo
将动画递增或递减应用于VisualElement
的Scale
属性。RotateTo
对VisualElement
的Rotation
属性进行动画处理。RelRotateTo
将动画递增或递减应用于VisualElement
的Rotation
属性。RotateXTo
对VisualElement
的RotationX
属性进行动画处理。RotateYTo
对VisualElement
的RotationY
属性进行动画处理。ScaleTo
对VisualElement
的Scale
属性进行动画处理。ScaleXTo
对VisualElement
的ScaleX
属性进行动画处理。ScaleYTo
对VisualElement
的ScaleY
属性进行动画处理。TranslateTo
对VisualElement
的TranslationX
和TranslationY
属性进行动画处理。
默认情况下,每个动画将需要 250 毫秒。 但是,创建动画时可以指定每个动画的持续时间。
注意
ViewExtensions
类提供 LayoutTo
扩展方法。 但是,此方法旨在供布局用来在包含大小和位置更改的布局状态之间设置切换动画。 因此,它应该只能由 Layout
子类使用。
ViewExtensions
类中的动画扩展方法都是异步方法,并返回一个 Task<bool>
对象。 如果动画完成,则返回值为 false
,如果动画取消,则返回值为 true
。 因此,动画方法通常应该与 await
运算符一起使用,这样就可以轻松确定动画何时完成。 此外,还可以创建顺序动画,在前一个动画方法完成后执行后一个动画方法。 有关详细信息,请参阅复合动画。
如果有要求让动画在后台完成,则可以省略 await
运算符。 在此方案中,动画扩展方法将在启动动画后快速返回,动画发生在后台。 创建复合动画时,可以利用此操作。 有关详细信息,请参阅组合动画。
有关 await
运算符的详细信息,请参阅异步支持概述。
单个动画
ViewExtensions
中的每个扩展方法都实现了一个动画操作,在一段时间内将一个属性从一个值逐步变为另一个值。 本部分介绍每个动画操作。
旋转
下面的代码示例演示如何使用 RotateTo
方法对 Image
的 Rotation
属性进行动画处理:
await image.RotateTo (360, 2000);
image.Rotation = 0;
这段代码通过在 2 秒(2000 毫秒)内旋转最多 360 度来对 Image
实例进行动画处理。 RotateTo
方法获取动画开始时的当前 Rotation
属性值,然后从该值旋转到第一个参数 (360)。 动画完成后,图像 Rotation
的属性重置为 0。 这可确保 Rotation
属性在动画结束后不会保持为 360,这将阻止进一步旋转。
以下屏幕截图显示了每个平台上正在进行的旋转:
相对旋转
下面的代码示例演示了使用 RelRotateTo
方法逐步增加或减少 Image
的 Rotation
属性:
await image.RelRotateTo (360, 2000);
这段代码通过在 2 秒(2000 毫秒)内将 Image
实例从起始位置旋转 360 度,对该实例进行动画处理。 RelRotateTo
方法获取动画开始时的当前 Rotation
属性值,然后从该值旋转到该值加上其第一个参数 (360)。 这可确保每个动画将始终从起始位置旋转 360 度。 因此,如果在动画正在进行时调用新动画,则它将从当前位置开始,并且可能在并非 360 度增量的位置结束。
以下屏幕截图显示了每个平台上正在进行的相对旋转:
缩放
下面的代码示例演示如何使用 ScaleTo
方法对 Image
的 Scale
属性进行动画处理:
await image.ScaleTo (2, 2000);
这段代码通过在 2 秒(2000 毫秒)内将 Image
实例缩放为其大小的两倍,对其进行动画处理。 ScaleTo
方法获取动画开始时的当前 Scale
属性值(默认值为 1),然后从该值缩放到第一个参数 (2)。 这会将图像的大小扩展到其大小的两倍。
以下屏幕截图显示了每个平台上正在进行的缩放:
相对缩放
下面的代码示例演示如何使用 RelScaleTo
方法对 Image
的 Scale
属性进行动画处理:
await image.RelScaleTo (2, 2000);
这段代码通过在 2 秒(2000 毫秒)内将 Image
实例缩放为其大小的两倍,对其进行动画处理。 RelScaleTo
方法获取动画开始时的当前 Scale
属性值,然后从该值缩放到该值加上其第一个参数 (2)。 这可确保每个动画将始终从起始位置缩放 2 倍。
使用定位点缩放和旋转
AnchorX
和 AnchorY
属性设置 Rotation
和 Scale
属性的缩放或旋转中心。 因此,它们的值也会影响到 RotateTo
和 ScaleTo
方法。
假设布局的中心放置有 Image
,下面的代码示例展示通过设置 AnchorY
属性围绕布局中心旋转图像:
double radius = Math.Min(absoluteLayout.Width, absoluteLayout.Height) / 2;
image.AnchorY = radius / image.Height;
await image.RotateTo(360, 2000);
若要围绕布局中心旋转 Image
实例,必须将 AnchorX
和 AnchorY
属性设置为相对于 Image
宽度与高度的值。 在此示例中,将 Image
的中心定义为位于布局的中心,因此默认 AnchorX
值 0.5 不需要更改。 但是,AnchorY
属性被重新定义为从 Image
顶部到布局中心点的值。 这样可以确保 Image
围绕布局中心点旋转 360 度,如以下屏幕截图所示:
翻译
下面的代码示例演示如何使用 TranslateTo
方法对 Image
的 TranslationX
和 TranslationY
属性进行动画处理:
await image.TranslateTo (-100, -100, 1000);
这段代码通过在 1 秒(1000 毫秒)内水平和垂直平移 Image
实例来对其进行动画处理。 TranslateTo
方法同时将图像向左平移 100 个像素,向上平移 100 个像素。 这是因为第一个和第二个参数都是负数。 提供正数会将图像向右和向下转换。
以下屏幕截图显示了每个平台上正在进行的平移:
注意
如果元素最初在屏幕外布局,然后转换到屏幕上,则在转换后,该元素的输入布局仍在屏幕外,用户无法与之交互。 因此,建议将视图布局在其最终位置,然后执行任何所需的转换。
淡入淡出
下面的代码示例演示如何使用 FadeTo
方法对 Image
的 Opacity
属性进行动画处理:
image.Opacity = 0;
await image.FadeTo (1, 4000);
这段代码通过在 4 秒(4000 毫秒)内淡入 Image
实例,对其进行动画处理。 FadeTo
方法获取动画开始时的当前 Opacity
属性值,然后从该值淡入到第一个参数 (1)。
以下屏幕截图显示了每个平台上正在进行的淡入:
复合动画
复合动画是动画的顺序组合,可以使用 await
运算符创建,如以下代码示例所示:
await image.TranslateTo (-100, 0, 1000); // Move image left
await image.TranslateTo (-100, -100, 1000); // Move image diagonally up and left
await image.TranslateTo (100, 100, 2000); // Move image diagonally down and right
await image.TranslateTo (0, 100, 1000); // Move image left
await image.TranslateTo (0, 0, 1000); // Move image up
在此示例中,Image
平移了 6 秒(6000 毫秒)。 Image
的转换使用五个动画,await
运算符指示每个动画按顺序执行。 因此,后续动画方法在前一个方法完成后执行。
组合动画
复合动画是两个或多个动画同时运行的动画组合。 可以通过混合等待动画和非等待动画来创建组合动画,如以下代码示例所示:
image.RotateTo (360, 4000);
await image.ScaleTo (2, 2000);
await image.ScaleTo (1, 2000);
在此示例中,Image
缩放并同时旋转了 4 秒(4000 毫秒)。 Image
的缩放使用在旋转的同时发生的两个顺序动画。 RotateTo
方法在不使用 await
运算符的情况下执行,并立即返回,然后开始第一个 ScaleTo
动画。 第一个 ScaleTo
方法调用中的 await
运算符会延迟第二个 ScaleTo
方法调用,直到第一个 ScaleTo
方法调用完成。 此时,RotateTo
动画已完成一半,Image
将旋转 180 度。 在最后 2 秒 (2000 毫秒)期间,第二个 ScaleTo
动画和 RotateTo
动画都完成。
并发运行多个异步方法
和 static
Task.WhenAny
Task.WhenAll
方法用于并发运行多个异步方法,因此可用于创建复合动画。 这两种方法都返回一个 Task
对象,并接受每个方法返回一个 Task
对象的方法集合。 Task.WhenAny
方法在其集合中的任何方法完成执行时完成,如以下代码示例所示:
await Task.WhenAny<bool>
(
image.RotateTo (360, 4000),
image.ScaleTo (2, 2000)
);
await image.ScaleTo (1, 2000);
在此示例中,Task.WhenAny
方法调用包含两个任务。 第一个任务是在 4 秒(4000 毫秒)内旋转图像,第二个任务是在 2 秒(2000 毫秒)内缩放图像。 第二项任务完成后,Task.WhenAny
方法调用完成。 但是,即使 RotateTo
方法仍在运行,第二种 ScaleTo
方法也可以开始。
Task.WhenAll
方法在其集合中的所有方法完成时完成,如以下代码示例所示:
// 10 minute animation
uint duration = 10 * 60 * 1000;
await Task.WhenAll (
image.RotateTo (307 * 360, duration),
image.RotateXTo (251 * 360, duration),
image.RotateYTo (199 * 360, duration)
);
在此示例中,Task.WhenAll
方法调用包含三个任务,每个任务的执行时间为 10 分钟。 每个 Task
旋转 360 度的次数不同 - RotateTo
旋转 307 次,RotateXTo
旋转 251 次,RotateYTo
旋转 199 次。 这些值是质数,因此可确保旋转不同步,进而确保不会导致重复模式。
以下屏幕截图显示了每个平台上正在进行的多个旋转:
取消动画
应用程序可以通过调用 CancelAnimations
扩展方法取消一个或多个动画,如以下代码示例所示:
image.CancelAnimations();
这会立即取消当前在 Image
实例上运行的所有动画。
总结
本文演示了如何使用 ViewExtensions
类创建和取消动画。 此类提供了扩展方法,可用于构造旋转、缩放、转换和淡化 VisualElement
实例的简单动画。