跳转至

SOUI 动画集合 (AnimatorSet)

概述

SOUI 动画集合(SAnimatorSet)提供了一种机制来组合和控制多个 SValueAnimator 对象的播放顺序和方式。它可以将多个动画按顺序或并行播放,使得复杂的动画序列变得易于管理。

SAnimatorSet 本身也是一个动画器(继承自 IValueAnimator),这意味着它可以像其他动画器一样使用,甚至可以嵌套到其他动画集合中。

核心概念

动画依赖关系

SAnimatorSet 通过建立动画之间的依赖关系来控制它们的播放顺序:

  • 并行(Parallel):多个动画同时播放
  • 串行(Sequential):动画按顺序一个接一个播放

AnimatorSetPlayMode

  • PARALLEL:并行模式,所有动画同时开始
  • SEQUENCE:串行模式,动画按顺序播放

主要方法

方法 描述
addAnimator(IValueAnimator * pAnimator) 添加动画到集合(默认并行播放)
addAnimatorAfter(IValueAnimator * pAnimator, IValueAnimator * pAfterAnimator) 添加在指定动画之后播放的动画
addAnimatorWith(IValueAnimator * pAnimator, IValueAnimator * pWithAnimator) 添加与指定动画并行播放的动画
removeAnimator(IValueAnimator * pAnimator) 从集合中移除动画
removeAllAnimators() 移除所有动画
getAnimatorCount() 获取动画数量
getAnimatorAt(int index) 获取指定索引的动画
setPlayMode(AnimatorSetPlayMode mode) 设置播放模式(并行或串行)
getPlayMode() 获取当前播放模式

使用示例

并行动画

void CMainDlg::OnParallelAnimation()
{
    SWindow * pWnd_left_top = FindChildByName(L"set_left_top");
    SWindow * pWnd_right_top = FindChildByName(L"set_right_top");
    SWindow * pWnd_left_bottom = FindChildByName(L"set_left_bottom");
    SWindow * pWnd_right_bottom = FindChildByName(L"set_right_bottom");

    if (!pWnd_left_top || !pWnd_right_top || !pWnd_left_bottom || !pWnd_right_bottom)
        return;

    // 创建动画集
    SAutoRefPtr<SAnimatorSet> animatorSet = new SAnimatorSet();
    animatorSet->setPlayMode(PARALLEL);  // 设置为并行模式

    // 获取各窗口的当前位置
    AnchorPos anchorPos[4];
    _GetWindowAnchorPos(pWnd_left_top, anchorPos + 0);
    _GetWindowAnchorPos(pWnd_right_top, anchorPos + 1);
    _GetWindowAnchorPos(pWnd_left_bottom, anchorPos + 2);
    _GetWindowAnchorPos(pWnd_right_bottom, anchorPos + 3);

    // 动画1:左上角窗口向右下移动
    {
        AnchorPos pos[] = { 
            anchorPos[0], 
            anchorPos[3]
        };
        IPropertyAnimator *pAnimator = SPropertyAnimator::ofPosition(
            pWnd_left_top, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator)
        {
            animatorSet->addAnimator(pAnimator);
            pAnimator->Release();
        }
    }

    // 动画2:右上角窗口向左下移动
    {
        AnchorPos pos[] = { 
            anchorPos[1], 
            anchorPos[2]
        };
        IPropertyAnimator *pAnimator = SPropertyAnimator::ofPosition(
            pWnd_right_top, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator)
        {
            animatorSet->addAnimator(pAnimator);
            pAnimator->Release();
        }
    }

    // 启动动画集
    if (animatorSet)
    {
        animatorSet->start(this);
    }
}

串行动画

void CMainDlg::OnSequentialAnimation()
{
    SWindow * pWnd_left_top = FindChildByName(L"set_left_top");
    SWindow * pWnd_right_top = FindChildByName(L"set_right_top");
    SWindow * pWnd_left_bottom = FindChildByName(L"set_left_bottom");
    SWindow * pWnd_right_bottom = FindChildByName(L"set_right_bottom");

    if (!pWnd_left_top || !pWnd_right_top || !pWnd_left_bottom || !pWnd_right_bottom)
        return;

    // 创建动画集
    SAutoRefPtr<SAnimatorSet> animatorSet = new SAnimatorSet();
    animatorSet->setPlayMode(SEQUENCE);  // 设置为串行模式

    // 获取各窗口的当前位置
    AnchorPos anchorPos[4];
    _GetWindowAnchorPos(pWnd_left_top, anchorPos + 0);
    _GetWindowAnchorPos(pWnd_right_top, anchorPos + 1);
    _GetWindowAnchorPos(pWnd_left_bottom, anchorPos + 2);
    _GetWindowAnchorPos(pWnd_right_bottom, anchorPos + 3);

    // 创建四个动画,按顺序执行
    SAutoRefPtr<IPropertyAnimator> pAnimator1 = NULL;
    SAutoRefPtr<IPropertyAnimator> pAnimator2 = NULL;
    SAutoRefPtr<IPropertyAnimator> pAnimator3 = NULL;
    SAutoRefPtr<IPropertyAnimator> pAnimator4 = NULL;

    AnchorPos centerPos = {4, 0, 0, -0.5f, -0.5f };  // 中心位置

    // 动画1:左上窗口向中心移动
    {
        AnchorPos pos[] = {
            anchorPos[0],
            centerPos
        };
        pAnimator1 = SPropertyAnimator::ofPosition(
            pWnd_left_top, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator1)
        {
            pAnimator1->setDuration(500);
            animatorSet->addAnimator(pAnimator1);
        }
    }

    // 动画2:右上窗口向中心移动(在动画1后)
    {
        AnchorPos pos[] = {
            anchorPos[1],
            centerPos
        };
        pAnimator2 = SPropertyAnimator::ofPosition(
            pWnd_right_top, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator2)
        {
            pAnimator2->setDuration(500);
            animatorSet->addAnimatorAfter(pAnimator2, pAnimator1);
        }
    }

    // 启动动画集
    if (animatorSet)
    {
        animatorSet->start(this);
    }
}

复杂动画集合

void CMainDlg::OnComplexAnimatorSetAnimation()
{
    SWindow * pWnd_left_top = FindChildByName(L"set_left_top");
    SWindow * pWnd_right_top = FindChildByName(L"set_right_top");
    SWindow * pWnd_left_bottom = FindChildByName(L"set_left_bottom");
    SWindow * pWnd_right_bottom = FindChildByName(L"set_right_bottom");

    if (!pWnd_left_top || !pWnd_right_top || !pWnd_left_bottom || !pWnd_right_bottom)
        return;

    // 创建动画集
    SAutoRefPtr<SAnimatorSet> animatorSet = new SAnimatorSet();

    // 获取各窗口的当前位置
    AnchorPos anchorPos[4];
    _GetWindowAnchorPos(pWnd_left_top, anchorPos + 0);
    _GetWindowAnchorPos(pWnd_right_top, anchorPos + 1);
    _GetWindowAnchorPos(pWnd_left_bottom, anchorPos + 2);
    _GetWindowAnchorPos(pWnd_right_bottom, anchorPos + 3);

    SAutoRefPtr<IPropertyAnimator> pAnimator1 = NULL;
    SAutoRefPtr<IPropertyAnimator> pAnimator2 = NULL;
    SAutoRefPtr<IPropertyAnimator> pAnimator3 = NULL;
    SAutoRefPtr<IPropertyAnimator> pAnimator4 = NULL;

    // ========== 第一阶段 ==========
    // 动画1:左上窗口向右移动
    {
        AnchorPos toPos = {2, 300, anchorPos[0].y, -1, -1 };  // 向右移动
        AnchorPos pos[] = {
            anchorPos[0],
            toPos
        };
        pAnimator1 = SPropertyAnimator::ofPosition(
            pWnd_left_top, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator1)
        {
            pAnimator1->setDuration(600);
            animatorSet->addAnimator(pAnimator1);
        }
    }

    // ========== 第二阶段 ==========
    // 动画2:右上窗口向下移动(在动画1后,与动画3并行)
    {
        AnchorPos toPos = {8, anchorPos[1].x, 300, -1, -1 };  // 向下移动
        AnchorPos pos[] = {
            anchorPos[1],
            toPos
        };
        pAnimator2 = SPropertyAnimator::ofPosition(
            pWnd_right_top, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator2)
        {
            pAnimator2->setDuration(600);
            animatorSet->addAnimatorAfter(pAnimator2, pAnimator1);
        }
    }

    // 动画3:左下窗口向上移动(在动画1后,与动画2并行)
    {
        AnchorPos toPos = {0, anchorPos[2].x, 100, -1, -1 };  // 向上移动
        AnchorPos pos[] = {
            anchorPos[2],
            toPos
        };
        pAnimator3 = SPropertyAnimator::ofPosition(
            pWnd_left_bottom, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator3)
        {
            pAnimator3->setDuration(600);
            animatorSet->addAnimatorAfter(pAnimator3, pAnimator1);
        }
    }

    // ========== 第三阶段 ==========
    // 动画4:右下窗口向左移动(在动画2后)
    {
        AnchorPos toPos = {6, 100, anchorPos[3].y, -1, -1 };  // 向左移动
        AnchorPos pos[] = {
            anchorPos[3],
            toPos
        };
        pAnimator4 = SPropertyAnimator::ofPosition(
            pWnd_right_bottom, 
            LayoutProperty::POSITION, 
            pos, 
            ARRAYSIZE(pos), 
            sizeof(AnchorPos)
        );
        if (pAnimator4)
        {
            pAnimator4->setDuration(600);
            animatorSet->addAnimatorAfter(pAnimator4, pAnimator2);
        }
    }

    // 启动动画集
    if (animatorSet)
    {
        animatorSet->start(this);
    }
}

基础动画集合示例

// 创建简单的动画集合
SAnimatorSet* animatorSet = new SAnimatorSet();

// 创建透明度动画
float alphaValues[] = {0.0f, 1.0f};
IValueAnimator* alphaAnimator = SPropertyAnimator::ofFloat(
    pWindow, 
    LayoutProperty::ALPHA, 
    alphaValues, 
    2
);
alphaAnimator->setDuration(500);

// 创建位置动画
SLayoutSize widthValues[] = {
    SLayoutSize(100.0f, dp),
    SLayoutSize(200.0f, dp)
};
IValueAnimator* widthAnimator = SPropertyAnimator::ofLayoutSize(
    pWindow, 
    LayoutProperty::WIDTH, 
    widthValues, 
    2
);
widthAnimator->setDuration(1000);

// 将动画添加到集合中(并行播放)
animatorSet->addAnimator(alphaAnimator);
animatorSet->addAnimator(widthAnimator);

// 启动动画集合
animatorSet->start(pTimelineHandler);

// 释放资源
alphaAnimator->Release();
widthAnimator->Release();

最佳实践

  1. 合理组织动画顺序:使用 addAnimatorAfter 和 addAnimatorWith 来精确控制动画的播放顺序
  2. 复用动画对象:对于相同的动画效果,可以复用动画对象以节省资源
  3. 管理生命周期:确保在适当的时候启动和结束动画集合
  4. 性能考虑:避免创建过于复杂的动画集合,以免影响性能
  5. 嵌套使用:可以将动画集合嵌套到其他动画集合中,创建更复杂的动画序列
  6. 及时释放资源:动画集合结束后及时释放相关资源

总结

SAnimatorSet 是 SOUI 动画系统中用于组合和控制多个动画的重要组件。通过它,开发者可以轻松创建复杂的动画序列,包括并行和串行播放的动画组合。它提供了灵活的 API 来定义动画之间的依赖关系,使得复杂的 UI 动画变得易于管理和实现。