zoukankan      html  css  js  c++  java
  • [UWP]使用CompositionAPI的翻转动画

    1. 运行效果

    使用GetAlphaMask和ContainerVisual制作长阴影(Long Shadow) 这篇文章里我介绍了一个包含长阴影的番茄钟,这个番茄钟在状态切换时用到了翻转动画,效果如上所示,还用到了弹簧动画,可以看到翻转后有点回弹。本来打算自己这个动画效果写的,但火火已经写好了这个FlipSide控件,Github地址在这里,这篇文章就介绍下这个控件的部分原理。

    2. TransformMatrix

    Visual的 TransformMatrix 属性是一个 Matrix4x4 的struct,它是应用于元素的转换矩阵,可以进行动画处理。它的默认值如下:

    这时候动画效果如下:

    要使Visual可以正确旋转需要按以下方式处理:

    private void UpdateTransformMatrix(FrameworkElement element)
    {
        var host = ElementCompositionPreview.GetElementVisual(element);
        var size = element.RenderSize.ToVector2();
        if (size.X == 0 || size.Y == 0) return;
        var n = -1f / size.X;
    
        Matrix4x4 perspective = new Matrix4x4(
            1.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, n,
            0.0f, 0.0f, 0.0f, 1.0f);
    
        host.TransformMatrix =
            Matrix4x4.CreateTranslation(-size.X / 2, -size.Y / 2, 0f) *
            perspective *
            Matrix4x4.CreateTranslation(size.X / 2, size.Y / 2, 0f);
    }
    

    讲真我也不明白为什么要这么写,只知道是从微软的 例子 里抄的。每当SizeChanged事件发生时都需要调用这个函数重新设置TransformMatrix。

    3. RotationAngleInDegrees

    Visual包含两个相似的属性,RotationAngleInDegreesRotationAngle,它们的定义如下:

    //
    // 摘要:
    //     视觉对象的旋转角度(以度为单位)。 可动画处理。
    //
    // 返回结果:
    //     The rotation angle of the visual in degrees.
    public float RotationAngleInDegrees { get; set; }
    //
    // 摘要:
    //     视觉对象的旋转角度(以弧度为单位)。 可动画处理。
    //
    // 返回结果:
    //     The rotation angle in radians of the visual.
    public float RotationAngle { get; set; }
    

    这两个属性都用于控制Visua围绕着RotationAxis和CenterPoint旋转。在FlipSide这个控件里RotationAngleInDegrees比较适用:

    float f1 = 0f, f2 = 0f;
    if (IsFlipped)
    {
        f1 = 180f;
        f2 = 360f;
        VisualStateManager.GoToState(this, "Slide2", false);
    }
    else
    {
        f1 = 0f;
        f2 = 180f;
        VisualStateManager.GoToState(this, "Slide1", false);
    }
    if (springAnimation1 != null && springAnimation2 != null)
    {
        springAnimation1.FinalValue = f1;
        springAnimation2.FinalValue = f2;
        s1Visual.StartAnimation("RotationAngleInDegrees", springAnimation1);
        s2Visual.StartAnimation("RotationAngleInDegrees", springAnimation2);
    }
    

    这段代码用到了SpringAnimatin,所以有弹一下的效果。

    4. RotationAxis

    RotationAxis 用于指定Visual旋转的轴。FlipSide可以通过设置RotationAxis改变翻转的角度,例如火火的Demo里使用根据鼠标改变RotationAxis:

    private void OnFlipSidePointerReleased(object sender, PointerRoutedEventArgs e)
    {
        var position = e.GetCurrentPoint(_FlipSide).Position;
        var v2 = (position.ToVector2() - _FlipSide.RenderSize.ToVector2() / 2);
        _FlipSide.Axis = new Vector2(-v2.Y, v2.X);
    }
    

    5. ExpressionAnimation

    <controls:FlipSide x:Name="FlipSide" IsFlipped="True">
        <controls:FlipSide.Side1>
            <Grid Background="#FFE87A69" x:Name="InworkElement" CornerRadius="1">
                
            </Grid>
        </controls:FlipSide.Side1>
        <controls:FlipSide.Side2>
            <Grid Background="#FF5271c2" x:Name="BreakElement" CornerRadius="1">
                
            </Grid>
        </controls:FlipSide.Side2>
    </controls:FlipSide>
    

    上面XAML为FlipSide的调用代码,它将Side1和Side2(这个命名超让高达迷兴奋)作为内容显示在UI上,当IsFlipped为False时显示Side1的内容,当IsFlipped为True时代表翻转过去,此时显示Side2的内容。在翻转动画的过程中,何时隐藏Side1并显示Side2是个麻烦事。幸好UWP有强大的表达式动画(ExpressionAnimation),FlipSide只用了下面几句代码处理这个问题:

    s1Visual = ElementCompositionPreview.GetElementVisual(Side1Content);
    s2Visual = ElementCompositionPreview.GetElementVisual(Side2Content);
    
    var opacity1Animation = compositor.CreateExpressionAnimation("this.Target.RotationAngleInDegrees > 90 ? 0f : 1f");
    var opacity2Animation = compositor.CreateExpressionAnimation("(this.Target.RotationAngleInDegrees - 180) > 90 ? 1f : 0f");
    
    s1Visual.StartAnimation("Opacity", opacity1Animation);
    s2Visual.StartAnimation("Opacity", opacity2Animation);
    

    这段代码的意思是当Side1的RotationAngleInDegrees大于90度时隐藏,否则显示;Side2则相反。其中,表达式中的this.Target表示使用这个表达式动画的Vsual。

    表达式动画的话题很大,这篇文章就割爱了,可以参考下面给出的链接了解更多内容:

    基于关系的动画 - Windows UWP applications Microsoft Docs

    【Win 10 应用开发】UI Composition 札记(七):基于表达式的动画 - 东邪独孤 - 博客园

    6. 结语

    感谢火火提供了这个控件,让我可以省下了不少功夫。其实我对TransformMatrix真的不理解,所以这部分只是用,没办法详细介绍。而且我以前对UI里使用3D不感兴趣,所以这方面真的没法写更多内容。期待火火为这方面补充一些博客。

    7. 参考

    基于关系的动画 - Windows UWP applications Microsoft Docs

    【Win 10 应用开发】UI Composition 札记(七):基于表达式的动画 - 东邪独孤 - 博客园

    ExpressionAnimation Class (Windows.UI.Composition) - Windows UWP applications Microsoft Docs

    Visual.TransformMatrix Property (Windows.UI.Composition) - Windows UWP applications Microsoft Docs

    合成视觉对象 - Windows UWP applications Microsoft Docs

    XAML 属性动画 - Windows UWP applications Microsoft Docs

    8. 源码

    cnbluefire_FlipSide

  • 相关阅读:
    JavaScript 中继承的一些示例
    测试ASP_NET 生命周期
    Web 设计师的 50 个超便利工具[下]
    关于ASP.NET 中站点地图sitemap 的使用
    早该知道的 7 个JavaScript 技巧[转]
    C# 浅拷贝与深拷贝区别
    HTML 中表格table 的相关知识
    理解值与引用[学习]
    关于CSS 3 及浏览器兼容性问题
    SQL Server 的存储过程[转]
  • 原文地址:https://www.cnblogs.com/dino623/p/create_flip_animation_using_CompositionApi.html
Copyright © 2011-2022 走看看