鸿蒙原生系列之动画效果(帧动画)

鸿蒙原生系列之帧动画

〇、前言

之前所说的动画效果,到了这里,就只剩下帧动画这一种动画效果,还未系统学习,下面就开始就鸿蒙 NDK UI 中如何实现帧动画效果,进行详细学习。

一、动画分类

4、帧动画

4.1、ArkUI_AnimatorOption

实现帧动画的第一步,就是先创建一个动画参数,帧动画使用的动画参数对象,与属性动画所用的不是同一个,所以,千万别应用错了 ,虽然两者就差了几个字母,但是在讲究严谨的计算机世界中,失之毫厘,谬以千里

首先,了解一下ArkUI_AnimatorOption对象相关的 API:

一般来说,这上面列出的API都需要调用一遍,因为只有这样,准备的动画参数才结构完整,才不会出现非预期的效果。

4.2、ArkUI_ExpectedFrameRateRange

实现帧动画的步骤中,除了需要设置常见的播放时长、播放延迟等动画参数外,还需要针对每一帧的预期帧率,准备一个 ArkUI_ExpectedFrameRateRange 对象并设置到动画参数中,该对象的结构如下:

而通过 API OH_ArkUI_AnimatorOption_SetExpectedFrameRateRange

可以将预期帧率同动画参数结合起来,也就是将预期帧率追加到动画参数中。

4.3、ArkUI_CurveHandle

由于帧动画对应的动画参数设置动画曲线的方式,比较特殊,不是通过枚举值去设置,而是通过传入一个 ArkUI_CurveHandle 对象的形式进行设置,然而,ArkUI_CurveHandle 对象需要使用专门的动画曲线创建方法去创建,目前,支持的动画曲线类型有如下:

这些 API 都定义在 native_animate.h 文件中。

使用上述 API 创建出来的 ArkUI_CurveHandle 对象,同时可以用在 OH_ArkUI_AnimatorOption_SetKeyframeCurve 方法的参数中。

4.4、Callback

再次强调,动画是一种事件,应该为其设置所支持的回调 ,对于帧动画来说,支持设置的回调类型有如下四种,或者说支持进行回调处理的时机情况有如下四种

1)动画接收到帧时

2)动画完成时

3)动画被取消时

4)动画被重复时

4.5、动画控制

在之前的动画效果案例中,动画效果都是『直肠子』的,一旦触发,就不能中断,直到预设的整个动画效果播放完成,这一次,将结合动画控制去演示帧动画。

动画控制,主要有以下几种控制动作:

1)创建动画

2)播放动画

3)结束动画

4)重置动画

5)暂停动画

6)取消动画

7)反转动画

4.5.1、创建动画

创建动画,就是准备好帧动画的动画参数后,使用 ArkUI_NativeAnimateAPI_1 的特定 API:createAnimator 创建一个 animator 动画对象:

4.5.2、播放动画

播放动画的控制,不是利用 ArkUI_NativeAnimateAPI_1 的成员方法,而是利用OH_ArkUI_Animator_Play 方法:

该方法的参数对象,正是创建动画一步操作中获得的 ArkUI_AnimatorHandle 对象,也正是这个 ArkUI_AnimatorHandle 对象,是我们进行剩下几种动画控制操作所不能欠缺的存在。

4.5.3、结束动画

结束动画,使用 OH_ArkUI_Animator_Finish 方法:

4.5.4、重置动画

重置动画,其实就是创建一个新的帧动画参数,并针对性调整动画参数中的相关参数值,比如使用与之前所不同的动画曲线,然后再通过方法 OH_ArkUI_Animator_ResetAnimatorOption 去更新动画:

4.5.5、暂停动画

暂停动画,使用方法 OH_ArkUI_Animator_Pause() 实现:

4.5.6、取消动画

曲线动画,使用方法 OH_ArkUI_Animator_Cancel 实现功能:

4.5.7、反转动画

所谓反转动画 ,指的是以相反的顺序播放动画 ,比如预设的动画效果是先放大后旋转再平移,那么反转之后就变成先平移后旋转再放大,实现该功能需要使用方法 OH_ArkUI_Animator_Reverse

二、动画实现

4、属性动画

4.1、页面结构

一如既往,从简单到复杂,先将页面结构实现出来。如上图所示,整个页面的由 ArkTS 层和 C++ 层共同实现,其中红色方框圈选的部分为 C++ 实现,都是按钮,没有按钮标题的那一个就是演示动画效果的承载者,而剩下的都是为了控制动画所准备的。

C++ 相关的 UI 代码如下:

cpp 复制代码
std::shared_ptr<ArkUIButtonNode> g_animator_button = nullptr;
ArkUI_AnimatorHandle animatorHandle = nullptr;
std::shared_ptr<ArkUIBaseNode> testFrameAnimate()
{
    // 创建根节点 Column
    auto column = std::make_shared<ArkUIColumnNode>();
    column->SetPercentHeight(1);
    column->SetPercentWidth(1);
    column->SetItemAlign(ARKUI_HORIZONTAL_ALIGNMENT_CENTER);
    column->SetJustifyContent(ARKUI_FLEX_ALIGNMENT_START);
    // 创建button,后续创建的Animator动画作用在button组件上
    auto button = std::make_shared<ArkUIButtonNode>();
    // 设置button初始宽高
    button->SetPercentWidth(0.2);
    button->SetHeight(60);
    button->SetBackgroundColor(0xFF000000);
    // 存储button全局变量,在onTouch注册时需要使用
    g_animator_button = button;
    // 创建createButton,用于初始化Animator参数
    auto createButton = std::make_shared<ArkUIButtonNode>();
    createButton->SetPercentWidth(0.2);
    createButton->SetHeight(40);
    createButton->SetBackgroundColor(0xFF000000);
    createButton->SetMargin(10, 0, 0, 0);
    
    createButton->SetLabel("create");
    // 设置Animator播放按钮
    auto playButton = std::make_shared<ArkUIButtonNode>();
    playButton->SetPercentWidth(0.2);
    playButton->SetHeight(40);
    playButton->SetBackgroundColor(0xFF000000);
    playButton->SetMargin(10, 0, 0, 0);
    playButton->SetLabel("play");
    
    
    // 设置Animator结束按钮
    auto finishButton = std::make_shared<ArkUIButtonNode>();
    finishButton->SetPercentWidth(0.2);
    finishButton->SetHeight(40);
    finishButton->SetBackgroundColor(0xFF000000);
    finishButton->SetMargin(10, 0, 0, 0);
    finishButton->SetLabel("finish");
    
    // 设置Animator更新按钮
    auto resetButton = std::make_shared<ArkUIButtonNode>();
    resetButton->SetPercentWidth(0.2);
    resetButton->SetHeight(40);
    resetButton->SetBackgroundColor(0xFF000000);
    resetButton->SetMargin(10, 0, 0, 0);
    resetButton->SetLabel("reset");
    
    // 设置Animator暂停按钮
    auto pauseButton = std::make_shared<ArkUIButtonNode>();
    pauseButton->SetPercentWidth(0.2);
    pauseButton->SetHeight(40);
    pauseButton->SetBackgroundColor(0xFF000000);
    pauseButton->SetMargin(10, 0, 0, 0);
    pauseButton->SetLabel("pause");
    
    // 设置Animator取消按钮
    auto cancelButton = std::make_shared<ArkUIButtonNode>();
    cancelButton->SetPercentWidth(0.2);
    cancelButton->SetHeight(40);
    cancelButton->SetBackgroundColor(0xFF000000);
    cancelButton->SetMargin(10, 0, 0, 0);
    cancelButton->SetLabel("cancel");
    
    
    // 设置Animator以相反的顺序播放按钮
    auto reverseButton = std::make_shared<ArkUIButtonNode>();
    reverseButton->SetPercentWidth(0.2);
    reverseButton->SetHeight(40);
    reverseButton->SetBackgroundColor(0xFF000000);
    reverseButton->SetMargin(10, 0, 0, 0);
    reverseButton->SetLabel("reverse");
    
    column->AddChild(button);
    column->AddChild(createButton);
    column->AddChild(playButton);
    column->AddChild(finishButton);
    column->AddChild(resetButton);
    column->AddChild(pauseButton);
    column->AddChild(cancelButton);
    column->AddChild(reverseButton);
    return column;
}

虽然代码量很多,但实际上都是相似度很高的代码,如果你为了追求代码简洁,你甚至可以将那些用来设置按钮的宽高、背景色以及标题的代码,抽取成一个公共方法。

4.2、创建动画

接下来,逐一地为页面上的几个动画控制按钮实现交互,首先,实现『create』按钮的点击事件处理功能:

cpp 复制代码
// 注册点击事件到button上
createButton->RegisterNodeEvent(createButton->GetHandle(), NODE_ON_CLICK, 3, nullptr);
auto onTouch = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 3) {
        showUITextCallback("帧动画", "创建");
        // 获取context对象
        static ArkUI_ContextHandle context = nullptr;
        context = OH_ArkUI_GetContextByNode(g_animator_button->GetHandle());

        // 获取ArkUI_NativeAnimateAPI接口
        auto animateApi = NativeModuleInstance::GetInstance()->GetNativeAnimateAPI();

        // 以下代码为创建Animator动画的关键流程,包括设置Animator动画参数、开启Animator动画
        // 设置ArkUI_AnimatorOption参数,通过提供的C方法设置对应的参数
        static ArkUI_AnimatorOption *option = OH_ArkUI_AnimatorOption_Create(0); // Animator动画状态数
        OH_ArkUI_AnimatorOption_SetDuration(option, 2000);                       // 动画播放时长
        OH_ArkUI_AnimatorOption_SetDelay(option, 10);                            // 动画延迟播放时长
        OH_ArkUI_AnimatorOption_SetIterations(option, 3);                        // 动画重复次数
        OH_ArkUI_AnimatorOption_SetFill(option, ARKUI_ANIMATION_FILL_MODE_NONE); // 是否恢复初始状态
        OH_ArkUI_AnimatorOption_SetDirection(option, ARKUI_ANIMATION_DIRECTION_NORMAL); // 动画播放方向
        ArkUI_CurveHandle curve =
            OH_ArkUI_Curve_CreateCubicBezierCurve(0.5f, 4.0f, 1.2f, 0.0f); // 构造三阶贝塞尔曲线对象
        OH_ArkUI_AnimatorOption_SetCurve(option, curve);                   // 动画插值曲线
        OH_ArkUI_AnimatorOption_SetBegin(option, 100);                     // 动画插值起点
        OH_ArkUI_AnimatorOption_SetEnd(option, 150);                       // 动画插值终点
        ArkUI_ExpectedFrameRateRange *range = new ArkUI_ExpectedFrameRateRange;
        range->max = 120;
        range->expected = 60;
        range->min = 30;
        OH_ArkUI_AnimatorOption_SetExpectedFrameRateRange(option, range); // 设置Animator动画帧率
        OH_ArkUI_AnimatorOption_SetKeyframe(option, 0.5, 120.5, 0);       // 设置Animator动画关键帧参数
        OH_ArkUI_AnimatorOption_SetKeyframeCurve(option, curve, 0); // 设置Animator动画关键帧曲线类型
        OH_ArkUI_AnimatorOption_RegisterOnFrameCallback(
            option, nullptr,
            [](ArkUI_AnimatorOnFrameEvent *event)
            {
                showUITextCallback("帧动画事件", "接收到帧");
                OH_ArkUI_AnimatorOnFrameEvent_GetUserData(event); // 获取动画事件对象中的用户自定义对象
                auto value = OH_ArkUI_AnimatorOnFrameEvent_GetValue(event); // 获取动画事件对象中的当前进度
                OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init",
                             "CXX OH_ArkUI_AnimatorOption_RegisterOnFrameCallback  %{public}f", value);
                g_animator_button->SetWidth(value);
            });
        OH_ArkUI_AnimatorOption_RegisterOnFinishCallback(option, reinterpret_cast<void *>(option),
                                                         [](ArkUI_AnimatorEvent *event)
                                                         {
                                                             showUITextCallback("帧动画事件", "动画结束");
                                                             OH_ArkUI_AnimatorEvent_GetUserData(
                                                                 event); // 获取动画事件对象中的用户自定义对象
                                                         });
        OH_ArkUI_AnimatorOption_RegisterOnCancelCallback(
            option, nullptr, [](ArkUI_AnimatorEvent *event) { showUITextCallback("帧动画事件", "动画被取消"); });
        OH_ArkUI_AnimatorOption_RegisterOnRepeatCallback(
            option, nullptr, [](ArkUI_AnimatorEvent *event) { showUITextCallback("帧动画事件", "动画被重复"); });
        // 执行对应的动画
        animatorHandle = animateApi->createAnimator(context, option);
    }
};
// 注册点击事件的回调函数
createButton->RegisterNodeEventReceiver(onTouch);

这里,涵盖了这一篇的关键代码,即帧动画参数的设置以及帧动画的创建。

4.3、动画控制

由于动画的播放、结束、暂停、取消和反转,都是调用一个 API 就能完成,所以,为了节省篇幅,相关实现代码直接集中在这一节里贴出:

cpp 复制代码
playButton->RegisterNodeEvent(playButton->GetHandle(), NODE_ON_CLICK, 4, nullptr);
auto onTouchPlay = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 4) {
        showUITextCallback("帧动画", "播放");
        OH_ArkUI_Animator_Play(animatorHandle);
    }
};
playButton->RegisterNodeEventReceiver(onTouchPlay);
finishButton->RegisterNodeEvent(finishButton->GetHandle(), NODE_ON_CLICK, 5, nullptr);
auto onTouchFinish = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 5) {
        showUITextCallback("帧动画", "结束");
        OH_ArkUI_Animator_Finish(animatorHandle);
    }
};
finishButton->RegisterNodeEventReceiver(onTouchFinish);
pauseButton->RegisterNodeEvent(pauseButton->GetHandle(), NODE_ON_CLICK, 7, nullptr);
auto onTouchPause = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 7) {
        showUITextCallback("帧动画", "暂停");
        OH_ArkUI_Animator_Pause(animatorHandle);
    }
};
pauseButton->RegisterNodeEventReceiver(onTouchPause);
cancelButton->RegisterNodeEvent(cancelButton->GetHandle(), NODE_ON_CLICK, 8, nullptr);
auto onTouchCancel = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 8) {
        showUITextCallback("帧动画", "取消");
        OH_ArkUI_Animator_Cancel(animatorHandle);
    }
};
cancelButton->RegisterNodeEventReceiver(onTouchCancel);
reverseButton->RegisterNodeEvent(reverseButton->GetHandle(), NODE_ON_CLICK, 9, nullptr);
auto onTouchReverse = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 9) {
        showUITextCallback("帧动画", "反转");
        OH_ArkUI_Animator_Reverse(animatorHandle);
    }
};
reverseButton->RegisterNodeEventReceiver(onTouchReverse);

4.4、重置动画

接下来,将剩余的 『reset』按钮的点击事件处理函数完成,便可以进行真机演示:

cpp 复制代码
resetButton->RegisterNodeEvent(resetButton->GetHandle(), NODE_ON_CLICK, 6, nullptr);
auto onTouchReset = [](ArkUI_NodeEvent *event)
{
    // 点击button按钮时触发该逻辑
    if (OH_ArkUI_NodeEvent_GetTargetId(event) == 6) {
        showUITextCallback("帧动画", "重置");
        static ArkUI_AnimatorOption *option = OH_ArkUI_AnimatorOption_Create(0); // Animator动画状态数
        OH_ArkUI_AnimatorOption_SetDuration(option, 1000);
        OH_ArkUI_AnimatorOption_SetDelay(option, 0);
        OH_ArkUI_AnimatorOption_SetIterations(option, 4);
        // 根据自己得需要选择下述两种曲线适合得去设置OH_ArkUI_AnimatorOption_SetCurve
        auto curve =
            OH_ArkUI_Curve_CreateCurveByType(ARKUI_CURVE_EASE); // 动画以低速开始,然后加快,在结束前变慢
        auto stepsCurve = OH_ArkUI_Curve_CreateStepsCurve(20, true); // 构造阶梯曲线对象
        OH_ArkUI_AnimatorOption_SetCurve(option, stepsCurve);
        OH_ArkUI_AnimatorOption_SetBegin(option, 200);
        OH_ArkUI_AnimatorOption_SetEnd(option, 100);
        OH_ArkUI_AnimatorOption_RegisterOnFrameCallback(
            option, nullptr,
            [](ArkUI_AnimatorOnFrameEvent *event)
            {
                showUITextCallback("帧动画事件", "接收到帧");
                OH_ArkUI_AnimatorOnFrameEvent_GetUserData(event); // 获取动画事件对象中的用户自定义对象
                auto value = OH_ArkUI_AnimatorOnFrameEvent_GetValue(event); // 获取动画事件对象中的当前进度
                g_animator_button->SetWidth(value);
            });
        OH_ArkUI_Animator_ResetAnimatorOption(animatorHandle, option);
    }
};
resetButton->RegisterNodeEventReceiver(onTouchReset);

所有按钮的单击处理函数中,代码量第二多的就是 『reset』按钮,因为需要重新准备帧动画参数。

4.5、真机演示

这里给个建议,进行代码的真机运行时,最好是使用鸿蒙6的设备进行,使用鸿蒙5的设备,可能会因为 API 未支持而看不到预期效果。

相关推荐
fqbqrr6 分钟前
2601C++,复制超文本格式
c++
小雨青年7 分钟前
鸿蒙 HarmonyOS 6 | 系统能力 (06) 构建现代化通知体系 从基础消息到实况
华为·harmonyos
m0_561359679 分钟前
基于C++的机器学习库开发
开发语言·c++·算法
2401_8324027517 分钟前
C++中的类型擦除技术
开发语言·c++·算法
2301_7634724627 分钟前
C++网络编程(Boost.Asio)
开发语言·c++·算法
木斯佳43 分钟前
HarmonyOS 6实战(源码解析篇):音乐播放器的音频焦点管理(上)——AudioSession与打断机制
华为·音视频·harmonyos
轩情吖1 小时前
Qt的窗口
开发语言·c++·qt·窗口·工具栏·桌面级开发
L186924547821 小时前
无外设条件下的自动找眼V2
c++
hcnaisd21 小时前
深入理解C++内存模型
开发语言·c++·算法
李老师讲编程1 小时前
C++信息学奥赛练习题-杨辉三角
数据结构·c++·算法·青少年编程·信息学奥赛