1# 使用动画 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @CCFFWW--> 5<!--Designer: @yangfan229--> 6<!--Tester: @lxl007--> 7<!--Adviser: @HelloCrease--> 8 9 10## 使用属性动画 11 12ArkUI开发框架在NDK接口主要提供属性动画,实现组件出现/消失转场。同时,可以通过Node-API桥接ArkTS侧帧动画能力,实现Native侧的动画效果。 13 14> **说明:** 15> 16> - 需要从ArkTS侧获取[this.getUIContext()](../reference/apis-arkui/arkui-ts/ts-custom-component-api.md#getuicontext),传入到Native侧。 17> 18> - 在Native侧通过[OH_ArkUI_GetContextFromNapiValue](../reference/apis-arkui/capi-native-node-napi-h.md)方法获取context。 19> 20> - 需要执行的动画属性变化必须写在[ArkUI_ContextCallback](../reference/apis-arkui/capi-arkui-nativemodule-arkui-contextcallback.md)中callback中。 21> 22> - 需要执行的动画属性,必须在执行动画之前设置过。 23 24提供全局animateTo显式动画接口,来指定由于闭包代码导致的状态变化插入过渡动效。同属性动画,布局类改变宽高的动画,内容都是直接到终点状态。 25 261. 在.ets文件中获取[UIContext](../reference//apis-arkui/arkts-apis-uicontext-uicontext.md),把this.getUIContext()当做参数输出到Native方法中。 27 ```ts 28 // createNativeNode是Native侧暴露的方法 29 nativeNode.createNativeNode("xcomponentId", this.getUIContext()); 30 ``` 31 322. 解析UIContext转换C中的context对象。 33 ``` 34 // 获取ets测传入的context 35 ArkUI_ContextHandle context = nullptr; 36 // 通过code 判断是否获取成功 37 auto code = OH_ArkUI_GetContextFromNapiValue(env, args[1], &context); 38 ``` 39 403. 获取ArkUI_NativeAnimateAPI_1 对象。 41 ``` 42 // 获取ArkUI_NativeAnimateAPI接口 43 ArkUI_NativeAnimateAPI_1 *animateApi = nullptr; 44 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_ANIMATE, ArkUI_NativeAnimateAPI_1, animateApi); 45 ``` 46 474. 设置 ArkUI_AnimateOption参数,通过提供的C方法设置对应的参数。 48 ``` 49 ArkUI_AnimateOption *option = OH_ArkUI_AnimateOption_Create(); 50 OH_ArkUI_AnimateOption_SetDuration(option, 2000); 51 OH_ArkUI_AnimateOption_SetTempo(option, 1.1); 52 OH_ArkUI_AnimateOption_SetCurve(option, ARKUI_CURVE_EASE); 53 OH_ArkUI_AnimateOption_SetDelay(option, 20); 54 OH_ArkUI_AnimateOption_SetIterations(option, 1); 55 OH_ArkUI_AnimateOption_SetPlayMode(option, ARKUI_ANIMATION_PLAY_MODE_REVERSE); 56 ArkUI_ExpectedFrameRateRange *range = new ArkUI_ExpectedFrameRateRange; 57 range->min = 10; 58 range->max = 120; 59 range->expected = 60; 60 OH_ArkUI_AnimateOption_SetExpectedFrameRateRange(option, range); 61 ``` 62 635. 设置回调参数。 64 ``` 65 // 用户自定义参数 66 struct UserData{ 67 int32_t data; 68 }; 69 UserData *onFinishUser = new UserData; 70 onFinishUser->data= 101; 71 // 设置完成的回调 72 ArkUI_AnimateCompleteCallback *completeCallback = new ArkUI_AnimateCompleteCallback; 73 completeCallback->userData = onFinishUser; 74 completeCallback->type = ARKUI_FINISH_CALLBACK_REMOVED; 75 completeCallback->callback = [](void *userData) { 76 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode onFinishCallback %{public}d", 77 reinterpret_cast<AA *>(userData)->a); 78 }; 79 // 用户自定义参数 80 UserData *eventUser = new UserData ; 81 eventUser->data= 201; 82 static bool isback = true; 83 ArkUI_ContextCallback *update = new ArkUI_ContextCallback; 84 update->userData = eventUser; 85 update->callback = [](void *user) { 86 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode animateTo %{public}d", 87 reinterpret_cast<UserData*>(user)->data); 88 // 对应的属性变化 width height 89 if (isback) { 90 ArkUI_NumberValue custom_widthValue[] = {200}; 91 ArkUI_AttributeItem custom_widthItem = {custom_widthValue, 1}; 92 ArkUI_NumberValue custom_heightValue1[] = {80}; 93 ArkUI_AttributeItem custom_heightItem1 = {custom_heightValue1, 1}; 94 nodeAPI->setAttribute(textInput, NODE_WIDTH, &custom_widthItem); 95 nodeAPI->setAttribute(textInput, NODE_HEIGHT, &custom_heightItem1); 96 } else { 97 ArkUI_NumberValue custom_widthValue[] = {100}; 98 ArkUI_AttributeItem custom_widthItem = {custom_widthValue, 1}; 99 ArkUI_NumberValue custom_heightValue1[] = {40}; 100 ArkUI_AttributeItem custom_heightItem1 = {custom_heightValue1, 1}; 101 nodeAPI->setAttribute(textInput, NODE_WIDTH, &custom_widthItem); 102 nodeAPI->setAttribute(textInput, NODE_HEIGHT, &custom_heightItem1); 103 } 104 }; 105 // 执行对应的动画 106 animateApi->animateTo(context, option, update, completeCallback); 107 ``` 108 109  110 111 112 113 114 115## 组件出现/消失转场 116 117组件内转场通过NODE_XX_TRANSITION属性(XX包括:OPACITY、TRANSLATE、SCALE、ROTATE、MOVE)配置转场参数,在组件插入和删除时显示过渡动效(通过NODE_TRANSFORM_CENTER属性设置NODE_SCALE_TRANSITION和NODE_ROTATE_ROTATE动效的中心点坐标)。主要用于容器组件中子组件插入和删除时,提升用户体验。 118 1191. 创建可交互界面,界面中包含Button,点击可以控制转场节点的添加和移除。其中 ArkUI_NodeContentHandle 类型节点的获取与使用可参考[接入ArkTS页面](ndk-access-the-arkts-page.md)。 120 ``` 121 constexpr int32_t BUTTON_CLICK_ID = 1; 122 bool flag = false; 123 ArkUI_NodeHandle parentNode; 124 ArkUI_NodeHandle childNode; 125 ArkUI_NodeHandle buttonNode; 126 127 void mainViewMethod(ArkUI_NodeContentHandle handle) 128 { 129 ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>( 130 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")); 131 ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 132 ArkUI_NumberValue widthValue[] = {{.f32 = 500}}; 133 ArkUI_AttributeItem widthItem = {.value = widthValue, .size = sizeof(widthValue) / sizeof(ArkUI_NumberValue)}; 134 nodeAPI->setAttribute(column, NODE_WIDTH, &widthItem); 135 ArkUI_NumberValue heightValue[] = {{.f32 = 500}}; 136 ArkUI_AttributeItem heightItem = {.value = heightValue, .size = sizeof(heightValue) / sizeof(ArkUI_NumberValue)}; 137 nodeAPI->setAttribute(column, NODE_HEIGHT, &heightItem); 138 ArkUI_NodeHandle buttonShow = nodeAPI->createNode(ARKUI_NODE_BUTTON); 139 ArkUI_NumberValue buttonWidthValue[] = {{.f32 = 200}}; 140 ArkUI_AttributeItem buttonWidthItem = {.value = buttonWidthValue, 141 .size = sizeof(buttonWidthValue) / sizeof(ArkUI_NumberValue)}; 142 nodeAPI->setAttribute(buttonShow, NODE_WIDTH, &buttonWidthItem); 143 ArkUI_NumberValue buttonHeightValue[] = {{.f32 = 50}}; 144 ArkUI_AttributeItem buttonHeightItem = {.value = buttonHeightValue, 145 .size = sizeof(buttonHeightValue) / sizeof(ArkUI_NumberValue)}; 146 nodeAPI->setAttribute(buttonShow, NODE_HEIGHT, &buttonHeightItem); 147 ArkUI_AttributeItem labelItem = {.string = "show"}; 148 nodeAPI->setAttribute(buttonShow, NODE_BUTTON_LABEL, &labelItem); 149 ArkUI_NumberValue buttonOpenTypeValue[] = {{.i32 = static_cast<int32_t>(ARKUI_BUTTON_TYPE_NORMAL)}}; 150 ArkUI_AttributeItem buttonOpenTypeItem = {.value = buttonOpenTypeValue, 151 .size = sizeof(buttonOpenTypeValue) / sizeof(ArkUI_NumberValue)}; 152 nodeAPI->setAttribute(buttonShow, NODE_BUTTON_TYPE, &buttonOpenTypeItem); 153 ArkUI_NumberValue buttonShowMarginValue[] = {{.f32 = 20}}; 154 ArkUI_AttributeItem buttonShowMarginItem = {.value = buttonShowMarginValue, 155 .size = sizeof(buttonShowMarginValue) / sizeof(ArkUI_NumberValue)}; 156 nodeAPI->setAttribute(buttonShow, NODE_MARGIN, &buttonShowMarginItem); 157 nodeAPI->registerNodeEvent(buttonShow, NODE_ON_CLICK, BUTTON_CLICK_ID, nullptr); 158 nodeAPI->addNodeEventReceiver(buttonShow, OnButtonShowClicked); 159 parentNode = column; 160 buttonNode = buttonShow; 161 nodeAPI->addChild(column, buttonShow); 162 OH_ArkUI_NodeContent_AddNode(handle, column); 163 } 164 ``` 165 1662. 创建一个设置了Transition属性的节点,当目标节点上下树时会播放转场动画。 167 ``` 168 ArkUI_NodeHandle CreateChildNode() { 169 ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>( 170 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")); 171 ArkUI_NodeHandle image = nodeAPI->createNode(ARKUI_NODE_IMAGE); 172 ArkUI_AttributeItem imageSrcItem = {.string = "/pages/common/scenery.jpg"}; 173 nodeAPI->setAttribute(image, NODE_IMAGE_SRC, &imageSrcItem); 174 ArkUI_NumberValue textWidthValue[] = {{.f32 = 300}}; 175 ArkUI_AttributeItem textWidthItem = {.value = textWidthValue, 176 .size = sizeof(textWidthValue) / sizeof(ArkUI_NumberValue)}; 177 nodeAPI->setAttribute(image, NODE_WIDTH, &textWidthItem); 178 ArkUI_NumberValue textHeightValue[] = {{.f32 = 300}}; 179 ArkUI_AttributeItem textHeightItem = {.value = textHeightValue, 180 .size = sizeof(textWidthValue) / sizeof(ArkUI_NumberValue)}; 181 nodeAPI->setAttribute(image, NODE_HEIGHT, &textHeightItem); 182 ArkUI_NumberValue transformCenterValue[] = {0.0f, 0.0f, 0.0f, 0.5f, 0.5f}; 183 ArkUI_AttributeItem transformCenterItem = {.value = transformCenterValue, 184 .size = sizeof(transformCenterValue) / sizeof(ArkUI_NumberValue)}; 185 nodeAPI->setAttribute(image, NODE_TRANSFORM_CENTER, &transformCenterItem); 186 ArkUI_NumberValue rotateAnimationValue[] = {0.0f, 0.0f, 1.0f, 360.0f, 0.0f, {.i32 = 500}, {.i32 = static_cast<int32_t>(ARKUI_CURVE_SHARP)}}; 187 ArkUI_AttributeItem rotateAnimationItem = {.value = rotateAnimationValue, 188 .size = sizeof(rotateAnimationValue) / sizeof(ArkUI_NumberValue)}; 189 nodeAPI->setAttribute(image, NODE_ROTATE_TRANSITION, &rotateAnimationItem); 190 ArkUI_NumberValue scaleAnimationValue[] = { 191 0.0f, 0.0f, 0.0f, {.i32 = 500}, {.i32 = static_cast<int32_t>(ARKUI_CURVE_SHARP)}}; 192 ArkUI_AttributeItem scaleAnimationItem = {.value = scaleAnimationValue, 193 .size = sizeof(scaleAnimationValue) / sizeof(ArkUI_NumberValue)}; 194 nodeAPI->setAttribute(image, NODE_SCALE_TRANSITION, &scaleAnimationItem); 195 ArkUI_NumberValue translateAnimationValue[] = { 196 200, 200, 0.0f, {.i32 = 500}, {.i32 = static_cast<int32_t>(ARKUI_CURVE_SHARP)}}; 197 ArkUI_AttributeItem translateAnimationItem = {.value = translateAnimationValue, 198 .size = sizeof(translateAnimationValue) / sizeof(ArkUI_NumberValue)}; 199 nodeAPI->setAttribute(image, NODE_TRANSLATE_TRANSITION, &translateAnimationItem); 200 return image; 201 } 202 ``` 203 2043. 在Button的监听回调里添加转场节点上下树逻辑,以此控制转场节点的入场和出场。 205 ``` 206 void OnButtonShowClicked(ArkUI_NodeEvent* event) 207 { 208 if (!event) { 209 return; 210 } 211 if (!childNode) { 212 childNode = CreateChildNode(); 213 } 214 ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>( 215 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")); 216 if (flag) { 217 flag = false; 218 ArkUI_AttributeItem labelItem = {.string = "show"}; 219 nodeAPI->setAttribute(buttonNode, NODE_BUTTON_LABEL, &labelItem); 220 nodeAPI->removeChild(parentNode, childNode); 221 } else { 222 flag = true; 223 ArkUI_AttributeItem labelItem = {.string = "hide"}; 224 nodeAPI->setAttribute(buttonNode, NODE_BUTTON_LABEL, &labelItem); 225 nodeAPI->addChild(parentNode, childNode); 226 } 227 } 228 ``` 229 230  231 232 233 234## 使用关键帧动画 235 236[keyframeAnimateTo](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativeanimateapi-1.md#keyframeanimateto)接口来指定若干个关键帧状态,实现分段的动画。同属性动画,布局类改变宽高的动画,内容都是直接到终点状态。 237 238该示例主要演示如何通过[keyframeAnimateTo](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativeanimateapi-1.md#keyframeanimateto)来设置关键帧动画,NDK接口开发的UI界面挂载到ArkTS主页面的完整流程可参考[接入ArkTS页面](ndk-access-the-arkts-page.md)。 239 240``` 241auto column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 242 243// 创建button,后续创建的关键帧动画作用在button组件上 244auto button = nodeAPI->createNode(ARKUI_NODE_BUTTON); 245ArkUI_NumberValue widthValue0[] = {100}; 246ArkUI_AttributeItem widthItem0 = {widthValue0, 1}; 247ArkUI_NumberValue heightValue0[] = {100}; 248ArkUI_AttributeItem heightItem0 = {heightValue0, 1}; 249nodeAPI->setAttribute(button, NODE_WIDTH, &widthItem0); 250nodeAPI->setAttribute(button, NODE_HEIGHT, &heightItem0); 251ArkUI_NumberValue typeValue[] = {{.i32 = ArkUI_ButtonType::ARKUI_BUTTON_TYPE_CIRCLE}}; 252ArkUI_AttributeItem typeItem = {typeValue, 1}; 253nodeAPI->setAttribute(button, NODE_BUTTON_TYPE, &typeItem); // 设置button的形状为圆形 254 255static ArkUI_NodeHandle buttonSelf = button; 256static ArkUI_NativeNodeAPI_1 *nodeAPISelf = nodeAPI; 257 258// 注册点击事件到button上 259nodeAPI->registerNodeEvent(button, NODE_ON_CLICK, 1, nullptr); 260auto onTouch = [](ArkUI_NodeEvent *event) { 261 262 // 点击button按钮时触发该逻辑 263 if (OH_ArkUI_NodeEvent_GetTargetId(event) == 1) { 264 265 // 获取context对象 266 static ArkUI_ContextHandle context = nullptr; 267 context = OH_ArkUI_GetContextByNode(buttonSelf); 268 269 // 获取ArkUI_NativeAnimateAPI接口 270 ArkUI_NativeAnimateAPI_1 *animateApi = nullptr; 271 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_ANIMATE, ArkUI_NativeAnimateAPI_1, animateApi); 272 273 // 以下代码为创建关键帧动画的关键流程,包括设置关键帧动画参数、开启关键帧动画 274 // 设置ArkUI_KeyframeAnimateOption参数,通过提供的C方法设置对应的参数 275 static ArkUI_KeyframeAnimateOption *option = OH_ArkUI_KeyframeAnimateOption_Create(2); // 关键帧动画状态数 276 OH_ArkUI_KeyframeAnimateOption_SetDuration(option, 1000, 0); // 第一段关键帧动画的持续时间 277 OH_ArkUI_KeyframeAnimateOption_SetDuration(option, 2000, 1); // 第二段关键帧动画的持续时间 278 OH_ArkUI_KeyframeAnimateOption_SetIterations(option, 5); // 关键帧动画播放次数 279 OH_ArkUI_KeyframeAnimateOption_RegisterOnEventCallback(option, nullptr, [](void *user) { 280 ArkUI_NumberValue widthValue0[] = {150}; 281 ArkUI_AttributeItem widthItem0 = {widthValue0, 1}; 282 ArkUI_NumberValue heightValue0[] = {150}; 283 ArkUI_AttributeItem heightItem0 = {heightValue0, 1}; 284 nodeAPISelf->setAttribute(buttonSelf, NODE_WIDTH, &widthItem0); 285 nodeAPISelf->setAttribute(buttonSelf, NODE_HEIGHT, &heightItem0); 286 }, 0); // 第一段关键帧时刻状态的闭包函数 287 OH_ArkUI_KeyframeAnimateOption_RegisterOnEventCallback(option, nullptr, [](void *user) { 288 ArkUI_NumberValue widthValue0[] = {80}; 289 ArkUI_AttributeItem widthItem0 = {widthValue0, 1}; 290 ArkUI_NumberValue heightValue0[] = {80}; 291 ArkUI_AttributeItem heightItem0 = {heightValue0, 1}; 292 nodeAPISelf->setAttribute(buttonSelf, NODE_WIDTH, &widthItem0); 293 nodeAPISelf->setAttribute(buttonSelf, NODE_HEIGHT, &heightItem0); 294 }, 1); // 第二段关键帧时刻状态的闭包函数 295 OH_ArkUI_KeyframeAnimateOption_RegisterOnFinishCallback(option, nullptr, [](void *user) { 296 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "keyframe animate finish"); 297 }); // 关键帧动画结束回调 298 ArkUI_ExpectedFrameRateRange *range = new ArkUI_ExpectedFrameRateRange; 299 range->max = 120; 300 range->expected = 60; 301 range->min = 30; 302 OH_ArkUI_KeyframeAnimateOption_SetExpectedFrameRate(option, range); // 关键帧设置期望帧率 303 304 // 执行对应的动画 305 animateApi->keyframeAnimateTo(context, option); 306 } 307}; 308nodeAPI->registerNodeEventReceiver(onTouch); 309nodeAPI->addChild(column, button); 310``` 311 312