跳转至

SOUI 属性动画 (PropertyAnimator)

概述

SOUI 属性动画(SPropertyAnimator)是基于 Android ObjectAnimator 设计的动画类,可以直接操作控件的各种属性。它是 SValueAnimator 的子类,专门用于对控件属性进行动画处理,是 SOUI 动画系统中最常用的动画类型之一。

属性动画通过 SPropertyValuesHolder 来持有和计算属性值,使得开发者可以轻松地对控件的任何属性进行动画处理。

核心概念

SPropertyValuesHolder

SPropertyValuesHolder 是属性值持有者,负责:

  • 存储动画的关键帧值
  • 计算特定时间点的属性值
  • 管理属性名称和类型

支持的数据类型

属性动画支持多种数据类型:

  • 整数(INT)
  • 浮点数(FLOAT)
  • 字节(BYTE)
  • 短整型(SHORT)
  • 颜色(COLORREF)
  • 布局尺寸(LAYOUTSIZE)
  • 位置(POSITION)

主要方法

方法 描述
ofFloat(IWindow pWnd, LPCWSTR propertyName, const float values, int valueCount) 创建浮点属性动画
ofInt(IWindow pWnd, LPCWSTR propertyName, const int values, int valueCount) 创建整数属性动画
ofLayoutSize(IWindow pWnd, LPCWSTR propertyName, const SLayoutSize values, int valueCount) 创建布局尺寸属性动画
ofPosition(IWindow pWnd, LPCWSTR propertyName, const void values, int valueCount, int valueSize) 创建位置属性动画
ofPropertyValuesHolder(IWindow pWnd, IPropertyValuesHolder *propertyHolders, int holderCount) 使用属性值持有者创建动画
GetTarget() 获取动画目标控件
SetPropertyValuesHolder(IPropertyValuesHolder *pHolder) 设置单个属性值持有者
SetPropertyValuesHolders(IPropertyValuesHolder **pHolders, int count) 设置多个属性值持有者

使用示例

基础属性动画

// 透明度动画 - 从完全透明到完全不透明
float alphaValues[] = {0.0f, 1.0f};
IPropertyAnimator* alphaAnimator = SPropertyAnimator::ofFloat(
    pWindow, 
    LayoutProperty::ALPHA, 
    alphaValues, 
    2
);
alphaAnimator->setDuration(500);
alphaAnimator->start(pTimelineHandler);
// 宽度动画 - 从100dp到300dp
SLayoutSize widthValues[] = {
    SLayoutSize(100.0f, dp),
    SLayoutSize(300.0f, dp)
};

IPropertyAnimator* widthAnimator = SPropertyAnimator::ofLayoutSize(
    pWindow, 
    LayoutProperty::WIDTH, 
    widthValues, 
    2
);
widthAnimator->setDuration(1000);
widthAnimator->start(pTimelineHandler);

多属性动画

// 同时动画宽度和高度
SLayoutSize widthValues[] = {
    SLayoutSize(100.0f, dp),
    SLayoutSize(200.0f, dp)
};

SLayoutSize heightValues[] = {
    SLayoutSize(50.0f, dp),
    SLayoutSize(100.0f, dp)
};

// 创建属性值持有者
IPropertyValuesHolder* pWidthHolder = SPropertyValuesHolder::ofLayoutSize(
    LayoutProperty::WIDTH, widthValues, 2);
IPropertyValuesHolder* pHeightHolder = SPropertyValuesHolder::ofLayoutSize(
    LayoutProperty::HEIGHT, heightValues, 2);

IPropertyValuesHolder* holders[] = { pWidthHolder, pHeightHolder };

// 创建多属性动画器
IValueAnimator* pAnimator = SPropertyAnimator::ofPropertyValuesHolder(
    pWindow, holders, 2);

if (pAnimator)
{
    pAnimator->setDuration(1200);
    pAnimator->start(pTimelineHandler);
    pAnimator->Release();
}
pWidthHolder->Release();
pHeightHolder->Release();

路径动画

路径动画是属性动画的重要应用,允许控件沿着指定路径移动:

// 定义路径上的关键点
AnchorPos pos[] = {
    { APT_Left_Top, SLayoutSize(0, SLayoutSize::px), SLayoutSize(0, SLayoutSize::px), 0, 0 },      // 起点
    { APT_Center_Center, SLayoutSize(150, SLayoutSize::px), SLayoutSize(100, SLayoutSize::px), -0.5f, -0.5f },    // 中点
    { APT_Right_Bottom, SLayoutSize(300, SLayoutSize::px), SLayoutSize(0, SLayoutSize::px), 0, 0 }     // 终点
};

// 创建路径动画
IPropertyAnimator* pathAnimator = SPropertyAnimator::ofPosition(
    pWindow,                    // 目标控件
    LayoutProperty::POSITION,   // 属性名
    pos,                        // 关键点数组
    ARRAYSIZE(pos),             // 关键点数量
    sizeof(AnchorPos)           // 数据结构大小
);

pathAnimator->setDuration(2000);    // 设置动画时长为2秒
pathAnimator->start(pTimelineHandler);

复杂路径动画

在实际应用中,我们经常需要同时对多个属性进行动画处理:

// 定义位置关键点
AnchorPos pos[] = {
    { APT_Left_Top, SLayoutSize(0, SLayoutSize::px), SLayoutSize(0, SLayoutSize::px), 0, 0 },
    { APT_Center_Center, SLayoutSize(150, SLayoutSize::px), SLayoutSize(100, SLayoutSize::px), -0.5f, -0.5f },
    { APT_Right_Bottom, SLayoutSize(300, SLayoutSize::px), SLayoutSize(0, SLayoutSize::px), 0, 0 }
};

// 创建位置属性持有者
IPropertyValuesHolder* pPosHolder = SPropertyValuesHolder::ofPosition(
    LayoutProperty::POSITION, 
    pos, 
    ARRAYSIZE(pos), 
    sizeof(AnchorPos)
);

// 定义透明度关键点
BYTE alpha[] = { 255, 128, 0 };  // 从不透明到半透明再到完全透明
IPropertyValuesHolder* pAlphaHolder = SPropertyValuesHolder::ofByte(
    WindowProperty::ALPHA, 
    alpha, 
    3
);

// 将多个属性持有者组合成一个动画
IPropertyValuesHolder* holders[] = { pPosHolder, pAlphaHolder };
IValueAnimator* animator = SPropertyAnimator::ofPropertyValuesHolder(
    pWindow,        // 目标控件
    holders,        // 属性持有者数组
    ARRAYSIZE(holders)  // 属性持有者数量
);

animator->setDuration(2000);
animator->start(pTimelineHandler);

// 记得释放资源
pPosHolder->Release();
pAlphaHolder->Release();

权重动画示例

// 权重动画 - 从0.5到2.0
float values[] = { 0.5f, 2.0f };

IValueAnimator* pAnimator = SPropertyAnimator::ofFloat(
    pWindow, 
    LayoutProperty::WEIGHT, 
    values, 
    2
);

if (pAnimator)
{
    pAnimator->setDuration(1500);
    pAnimator->start(pTimelineHandler);
    pAnimator->Release();
}

propanimator_demo 示例

在 propanimator_demo 示例中,我们可以看到多种属性动画的使用:

void CMainDlg::OnBtnAnimation()
{
    SWindow * pWnd_left_top = FindChildByName(L"win_left_top");
    SWindow * pWnd_right_top = FindChildByName(L"win_right_top");
    SWindow * pWnd_left_bottom = FindChildByName(L"win_left_bottom");
    SWindow * pWnd_right_bottom = FindChildByName(L"win_right_bottom");
    SWindow * pWnd_center = FindChildByName(L"win_center");

    AnchorPos anchorPos[5];
    _GetWindowAnchorPos(pWnd_left_top, anchorPos+0);
    _GetWindowAnchorPos(pWnd_right_top, anchorPos+1);
    _GetWindowAnchorPos(pWnd_left_bottom, anchorPos+2);
    _GetWindowAnchorPos(pWnd_right_bottom, anchorPos+3);
    _GetWindowAnchorPos(pWnd_center, anchorPos+4);

    // 动画:左上角窗口到中心再到左下角
    AnchorPos pos[] = {
        anchorPos[0],
        anchorPos[4],
        anchorPos[2]
    };

    IPropertyAnimator *pAnimator = SPropertyAnimator::ofPosition(
        pWnd_left_top, 
        LayoutProperty::POSITION, 
        pos, 
        ARRAYSIZE(pos), 
        sizeof(AnchorPos)
    );

    // 设置权重,控制关键帧的动画时间分布
    float weights[] = { 1.0f, 5.0f };  // 第二段动画比第一段慢5倍
    pAnimator->GetPropertyValuesHolderByIndex(0)->SetKeyFrameWeights(weights, ARRAYSIZE(weights));

    pAnimator->setDuration(2000);
    pAnimator->start(this);
    pAnimator->Release();
}

关键帧权重

通过 SetKeyFrameWeights 方法可以设置关键帧的权重,控制动画在不同关键帧之间的过渡时间:

// 设置关键帧权重,让动画在第二个关键帧停留更长时间
float weights[] = { 1.0f, 5.0f };  // 第二段动画比第一段慢5倍
pAnimator->GetPropertyValuesHolderByIndex(0)->SetKeyFrameWeights(weights, ARRAYSIZE(weights));

最佳实践

  1. 使用合适的属性名称:确保属性名称与控件支持的属性匹配
  2. 合理设置关键帧:根据动画需求设置适当数量的关键帧
  3. 控制动画时长:确保动画时长符合用户体验需求
  4. 使用权重优化动画:通过权重调整动画在关键帧间的过渡
  5. 及时释放资源:使用完毕后及时释放动画对象
  6. 避免过度动画:不要对太多属性同时进行动画,以免影响性能

总结

SPropertyAnimator 是 SOUI 中最常用的动画类型,它直接操作控件的属性,使得动画实现变得简单而强大。通过 SPropertyValuesHolder,可以灵活地控制动画的关键帧和过渡效果。开发者可以使用它来实现各种复杂的属性动画,包括位置、尺寸、透明度等的动画效果。