鸿蒙原生系列之动画效果(关键帧动画)
本文介绍了鸿蒙系统中的关键帧动画实现方法。首先概述了关键帧动画与属性动画的相似性,均需使用ArkUI_NativeAnimateAPI_1指针。文章详细讲解了关键帧动画的实现步骤:创建ArkUI_KeyframeAnimateOption对象、设置动画时长和播放次数、注册动画分段回调及结束回调,最后通过keyframeAnimateTo方法播放动画。在实践部分,展示了如何自定义圆形按钮形状并实现关
鸿蒙原生系列之关键帧动画
〇、前言
上一篇,已经系统性学习了转场动画这种动画效果,接下来,继续学习另一种动画效果:关键帧动画。
关键帧动画的代码,总体上,与最早学习的动画效果——属性动画,更为解决,都是需要用到专门的 API 接口——ArkUI_NativeAnimateAPI_1指针,而在转场动画的实现代码中,就完全不需要这个特殊API,反而像设置组件的尺寸那样写即可。
一、动画分类
3、关键帧动画
3.1、ArkUI_KeyframeAnimateOption
与属性动画的实现流程一样,关键帧动画也是需要先准备好动画参数对象,而在关键帧动画这边,这个关键的动画参对象,就是 ArkUI_KeyframeAnimateOption。这个 ArkUI_KeyframeAnimateOption 对象的原型,SDK中尚未披露,所以 ,暂时无法深入了解它的结构。
ArkUI_KeyframeAnimateOption 对象实例,需要使用 OH_ArkUI_KeyframeAnimateOption_Create方法创建,而该方法接受一个 int32_t 类型的参数,并且参数值与关键帧动画段数密切相关。
3.2、动画时间与播放次数
有了 ArkUI_KeyframeAnimateOption 实例后,就可以借助它去完成关键帧动画的持续时间和播放次数的设置。
设置关键帧动画持续时间,需要使用方法 OH_ArkUI_KeyframeAnimateOption_SetDuration(ArkUI_KeyframeAnimateOption* option, int32_t value, int32_t index),参数 value 传入用于设置具体的持续时间,而 index 则指明此次设置作用在哪段关键帧动画上。
关键帧动画播放次数,用方法 OH_ArkUI_KeyframeAnimateOption_SetIterations(ArkUI_KeyframeAnimateOption* option, int32_t value) 进行设置,播放次数无需分段设置,直接作用在所有关键帧动画段上。
3.3、注册动画分段回调
此前就说过了,动画也是一种事件,动画是支持设置回调函数的。在关键帧动画这边,允许给每一段动画分别设置回调。设置动画回调,统一用 OH_ArkUI_KeyframeAnimateOption_RegisterOnEventCallback(ArkUI_KeyframeAnimateOption* option, void* userData, void (*event)(void* userData), int32_t index) 方法:
在回调函数也即上面的闭包函数中,我们便可以编写动画相关的代码,比如改变目标组件的尺寸或颜色等等。
3.4、注册动画结束回调
动画播放结束,是一个非常关键的事件,所以,少不了设置对应的回调,在关键帧动画这边,是用 OH_ArkUI_KeyframeAnimateOption_RegisterOnFinishCallback(ArkUI_KeyframeAnimateOption* option, void* userData, void (*onFinish)(void* userData)) 方法进行处理的,在闭包函数中,我们便可以进行自定义处理,比如发送一些消息通知什么的,亦或者进行UI回显。
3.5、播放关键帧动画
上面的那些,说到底只是为关键帧动画进行必要的准备,并没有真正进行关键帧动画的播放,要进行播放就少不了用上 ArkUI_NativeAnimateAPI_1 指针。
在 ArkUI_NativeAnimateAPI_1 的函数成员中,有一个专门用于播放关键帧动画的 int32_t (*keyframeAnimateTo)(ArkUI_ContextHandle context, ArkUI_KeyframeAnimateOption* option) 方法。
二、动画实现
3、关键帧动画
既然关键帧动画的相关理论,或者说相关API,都已经认识了,那么,接下来便可以真刀真枪开始代码实践了,毕竟纸上得来终觉浅,躬行方知事原委。
3.1、自定义按钮形状

如图所示,此次关键帧动画的作用对象,是一个按钮、圆形的按钮,然而,无论是 ArkTS 代码还是 C++,按钮组件的默认形状,都不是圆形的,而是胶囊形的,要改变按钮形状,就必须显式地设置按钮形状。
为了支持按钮形状的自定义设置,需要在 ArkUIButtonNode.h 中新增如下的一个公共方法:
void SetButtonType(ArkUI_ButtonType type){
assert(handle_);
ArkUI_NumberValue value[] = {{.i32 = type}};
ArkUI_AttributeItem item = {value, 1};
nativeModule_->setAttribute(handle_, NODE_BUTTON_TYPE, &item);
}
而在 ArkUI_ButtonType 枚举类中,所有的按钮形状有如下:
3.2、实现页面结构
在编写有关关键帧动画的代码之前,先把页面整体结构给实现出来。如上图所示,除去 ArkTS 实现的部分页面结构外,剩下的是一个按钮,而这个按钮实际上可以放在一个看不见的 Column 中:
这里特别提醒一下,在 DevEco Studio 中,实际上是有一个可以专门分析页面结构的 ArkUI Inspector 工具的,工具入口默认位置在 IDE 界面左下角的位置,跟日志工具的入口挨着。
实现这部分C++代码实现的页面结构,不妨放在 testKeyFrameAnimate 方法体中:
std::shared_ptr<ArkUIButtonNode> g_keyFrame_button = nullptr;
std::shared_ptr<ArkUIBaseNode> testKeyFrameAnimate()
{
auto column = std::make_shared<ArkUIColumnNode>();
column -> SetPercentWidth(1.0f);
column->SetPercentHeight(1.0f);
auto button = std::make_shared<ArkUIButtonNode>();
button->SetEnable();
button -> SetButtonType(ARKUI_BUTTON_TYPE_CIRCLE);
button -> SetWidth(150);
button -> SetHeight(150);
button->SetBackgroundColor(0xFF000000);
button->SetLabel("Click");
button -> SetMargin(20.0, 0.0, 0.0,0.0);
g_keyFrame_button = button;
column -> AddChild(button);
return column;
}
很简单的一段代码。
3.3、实现关键帧动画
既然作用对象是一个按钮,不妨将关键帧动画的实现代码,全都放在按钮点击事件的回调处理函数中:
// 注册按钮点击事件
button->RegisterNodeEvent(button->GetHandle(), NODE_ON_CLICK, 1, nullptr);
// 定义按钮点击处理回调函数
auto onClick = [](ArkUI_NodeEvent *event) {
showUITextCallback("关键帧动画", "开始演示");
// 设置ArkUI_KeyframeAnimateOption参数
static ArkUI_KeyframeAnimateOption *option = OH_ArkUI_KeyframeAnimateOption_Create(2); // 关键帧动画状态数
OH_ArkUI_KeyframeAnimateOption_SetDuration(option, 2000, 0); // 第一段关键帧动画的持续时间
OH_ArkUI_KeyframeAnimateOption_SetDuration(option, 2000, 1); // 第二段关键帧动画的持续时间
OH_ArkUI_KeyframeAnimateOption_SetIterations(option, 5); // 关键帧动画播放次数
OH_ArkUI_KeyframeAnimateOption_RegisterOnEventCallback(option, nullptr, [](void *user) {
showUITextCallback("关键帧动画", "放大按钮");
g_keyFrame_button -> SetWidth(200);
g_keyFrame_button -> SetHeight(200);
}, 0); // 第一段关键帧时刻状态的闭包函数
OH_ArkUI_KeyframeAnimateOption_RegisterOnEventCallback(option, nullptr, [](void *user) {
showUITextCallback("关键帧动画", "缩小按钮");
g_keyFrame_button -> SetWidth(80);
g_keyFrame_button -> SetHeight(80);
}, 1); // 第二段关键帧时刻状态的闭包函数
OH_ArkUI_KeyframeAnimateOption_RegisterOnFinishCallback(option, nullptr, [](void *user) {
showUITextCallback("关键帧动画", "结束演示");
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "keyframe animate finish");
}); // 关键帧动画结束回调
ArkUI_ExpectedFrameRateRange *range = new ArkUI_ExpectedFrameRateRange;
range->max = 120;
range->expected = 60;
range->min = 30;
OH_ArkUI_KeyframeAnimateOption_SetExpectedFrameRate(option, range); // 关键帧设置期望帧率
// 获取ArkUI_NativeAnimateAPI接口
auto animateApi = NativeModuleInstance::GetInstance()->GetNativeAnimateAPI();
// 获取context对象
static ArkUI_ContextHandle context = nullptr;
context = OH_ArkUI_GetContextByNode(g_keyFrame_button->GetHandle());
// 执行对应的动画
animateApi->keyframeAnimateTo(context, option);
};
// 设置按钮点击事件接收器
button -> RegisterNodeEventReceiver(onClick);
与之前一样,用一个全局的 g_keyFrame_button 变量,去持有按钮组件节点的实例,从而方便在闭包函数中,动态地设置按钮的尺寸——OH_ArkUI_KeyframeAnimateOption_RegisterOnEventCallback的闭包函数体中,以及执行 animateApi->keyframeAnimateTo(context, option) 前获取按钮组件对应的上下文信息。
3.4、真机演示
修改 NativeEntry.cpp 中 CreateNativeRoot 方法中的根节点代码:
napi_value CreateNativeRoot(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取 NodeContent
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
NativeEntry::GetInstance()->SetContentHandle(contentHandle);
// 创建文本列表
// auto list = CreateTextListExample();
// NativeEntry::GetInstance()->SetRootNode(list);
// 创建 Column
// auto column = testGestureExample();
// NativeEntry::GetInstance()->SetRootNode(column);
// 创建 image
// auto image = CreateDragImageExample();
// NativeEntry::GetInstance()->SetRootNode(image);
// 演示动画
// auto column = testFrameAnimate();
// NativeEntry::GetInstance()->SetRootNode(column);
// auto column = testPropertiesAnimate();
// NativeEntry::GetInstance()->SetRootNode(column);
// auto column = testTransitionAnimate();
// NativeEntry::GetInstance()->SetRootNode(column);
auto column = testKeyFrameAnimate();
NativeEntry::GetInstance()->SetRootNode(column);
return nullptr;
}
更多推荐




所有评论(0)