自定义动画
更新时间:
语法
const element = this.$element('elementIdName')
const animation = element.animate(keyframes, options)
animation.play()
复制代码
参数
名称 | 类型 | 默认值 | 必填 | 描述 |
---|---|---|---|---|
keyframes | <array> |
[] | 是 | 代表关键帧的一个数组集合。keyframes 在这个场景下没有意义直接传空数组 |
options | Options | - | 否 | 自定义动画配置项 |
Options
自定义动画配置项
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
animatecontrol | Animatecontrol | 否 | 自定义动画时的控制项 |
Animatecontrol
自定义动画控制项
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
init | InitControl | 否 | 表示本次调用是初始化 |
update | UpdateControl | 否 | 表示是动画过程中的数据更新 |
InitControl
自定义动画初始控制
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
duration | Number | 否 | 动画持续时间 |
onAnimateUpdate | Function | 否 | 接收到动画过程的 callback,返回值为AnimateUpdateProgress |
UpdateControl
自定义动画更新控制
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
scale | Number | 否 | 指定当前动画帧的放缩大小,默认返回的是 0-1 之间的小数。 |
translate | Number | 否 | 水平位移量,单位 px |
translateX | Number | 否 | 水平位移量,单位 px |
translateY | Number | 否 | 垂直位移量,单位 px |
opacity | Number | 否 | 透明度,0 - 1 之间 |
rotate | Number | 否 | 旋转角度,单位 deg。 |
AnimateUpdateProgress
自定义动画 onAnimateUpdate 返回值
属性 | 类型 | 描述 |
---|---|---|
progress | Number | 动画执行进度,动画开始 progress = 0,画结束时 progress = 1 |
开发设计图

内置动画
内置动画为框架默认支持的三种动画特效。 即Options 中 animateId 不为空时的取值。
<template>
<cellular-list
content="{{content}}"
@iconclick="iconClickHandler"
id="cellular-list"
></cellular-list>
</template>
<script>
export default {
data() {
return {
content: Array.from(
{ length: 7 },
(v, i) =>
(v = {
image: `/assets/images/icon/icon${i}.png`,
name: `文本${i}`,
})
),
}
},
iconClickHandler(evt) {
console.log(`iconclick index=${evt.index}, name=${evt.name}`)
const animation = this.$element('cellular-list').animate([], {
animateId: 3,
beehiveIndex: evt.index,
})
animation.play()
},
}
</script>
复制代码
常规动画
适用于可通过动画参数即可完成的动画能力。
<template>
<image id="image" src="/assets/images/3004.png"></image>
</template>
<script>
export default {
onShow() {
const cImage = this.$element('image')
const animation = cImage.animate([], {
animatecontrol: {
init: {
duration: 5000,
onAnimateUpdate(data) {
cImage.animate([], {
animatecontrol: {
update: {
scale: 1 - data.progress,
translate: data.progress * 100,
opacity: 1 - data.progress,
rotate: 360 * data.progress,
},
},
})
},
},
},
})
animation.play()
},
}
</script>
复制代码
定制进度
由组件控制动画进度,如拖拽过程。此时 init 时不要指定 duration, 因为拖拽过程时间不定。也不要 animation.play(),因为动画开始是拖拽时产生。
<template>
<drag_component id="id_drag_component">
<input class="btn" id="btn1" type="button" value="button1" />
<input class="btn" id="btn2" type="button" value="button2" />
<input class="btn" id="btn3" type="button" value="button3" />
</drag_component>
</template>
<script>
export default {
onShow() {
const cDrag = this.$element('id_drag_component')
const animation = cDrag.animate([], {
init: {
onAnimateUpdate(data) {
const cBtn1 = this.$element('btn1')
cBtn1.animate([], {
animatecontrol: {
update: { scale: 1 - data.progress },
},
})
const cBtn2 = this.$element('btn2')
cBtn2.animate([], {
animatecontrol: {
update: {
translate: data.progress * 100,
opacity: 1 - data.progress,
rotate: 360 * data.progress,
},
},
})
const cBtn3 = this.$element('btn3')
cBtn3.animate([], {
animatecontrol: {
update: {
scale: 1 - data.progress,
rotate: 360 * data.progress,
},
},
})
},
},
})
},
}
</script>
复制代码
//drag_render_element.cc
//触屏事件处理
bool DragRenderElement::deliverTouchEvent(shared_ptr<TouchEvent> event_)
{
if (child_list_->size() <= 0) {
return false;
}
int action = event_->getAction();
switch (action) {
case TOUCH_EVENT_ACTION_MOVE: { //move事件
float x = event_->getX();
float y = event_->getY();
float deltax = last_motion_x_ - x;
float deltay = last_motion_y_ - y;
float distance = abs(deltax);
if (distance > getSize().width) {
distance = distance - getSize().width;
}
float progress = distance / getSize().width;
updateAnimateToApp(progress); //调用组件基类中的公共方法, 传递动画进度到RPK 的 callback
break;
}
case TOUCH_EVENT_ACTION_DOWN: { //down事件
if (last_motion_x_ <= 0 && last_motion_y_ <= 0) {
float x = event_->getX();
float y = event_->getY();
last_motion_x_ = x;
last_motion_y_ = y;
}
break;
}
case TOUCH_EVENT_ACTION_CANCEL:
case TOUCH_EVENT_ACTION_UP: {
break;
}
}
return true;
}
复制代码
定制 onAnimateUpdate
如果说 onAnimateUpdate 想返回其他值,比如水平位置。可以对 onAnimateUpdate 进行定制。
<!-- 自定义callback 数据 CustomDataShow -->
<template>
<custom_data_component id="custom_data_component">
<input class="btn" id="btn1" type="button" value="button1" />
<input class="btn" id="btn2" type="button" value="button2" />
<input class="btn" id="btn3" type="button" value="button3" />
</custom_data_component>
</template>
<script>
export default {
onShow() {
const cCutom = this.$element('custom_data_component')
const animation = cCutom.animate([], {
animatecontrol: {
init: {
duration: 5000,
onAnimateUpdate(data) {
const cBtn1 = this.$element('btn1')
cBtn1.animate([], { animatecontrol: { update: { scale: 1 - data.x } } }) //callback自定义数据 x, y
const cBtn2 = this.$element('btn2')
cBtn2.animate([], {
animatecontrol: {
update: { translate: data.x * 100, opacity: 1 - data.y, rotate: 360 * data.y },
},
})
const cBtn3 = this.$element('btn3')
cBtn3.animate([], {
animatecontrol: { update: { scale: 1 - data.x, rotate: 360 * data.y } },
})
},
},
},
})
animation.play()
},
}
</script>
复制代码
//native组件重写updateAnimateToApp 方法即可
class CustomDataRenderElement : public BoxRenderElement
{
public:
//.....
void updateAnimateToApp(float progress) override;
};
void CustomDataRenderElement::updateAnimateToApp(float progress)
{
shared_ptr<JsThread> js_thread = JsThread::getInstance();
if (js_thread == nullptr || component_ == nullptr ||
component_->js_element_ == nullptr) {
return;
}
auto rt = js_thread->getJsRuntime();
quickruntime::Task f = [rt, this, e = component_->js_element_,
progress](void* arg) {
quickapp::jsi::Object* params = new quickapp::jsi::Object(*rt);
// params->setProperty(*rt, "progress", progress);
params->setProperty(*rt, "x", progress); //将自己所需要的数据放到callback里面
params->setProperty(*rt, "y", progress); //
e->dispatchEvent(Event("onAnimateUpdate", params)); //调用rpk的onAnimateUpdate callback将数据传递过去
};
js_thread->postTask(f);
}
复制代码
定制 UpdateControl
如果UpdateControl中类型不能满足需要,可以自己定制解析数据。
<!-- native组件自己处理rpk的callback数据 NativeShow -->
<template>
<native_anim_update_component id="native_anim_update_component">
<input class="btn" id="btn1" type="button" value="button1" />
<input class="btn" id="btn2" type="button" value="button2" />
<input class="btn" id="btn3" type="button" value="button3" />
</native_anim_update_component>
</template>
<script>
export default {
onShow() {
const cNative = this.$element('native_anim_update_component')
const animation = cNative.animate([], {
animatecontrol: {
init: {
duration: 5000,
onAnimateUpdate(data) {
cNative.animate([], {
animatecontrol: {
update: { scale: 1 - data.progress, rotate: 360 * data.progress },
},
})
},
},
},
})
animation.play()
},
}
</script>
复制代码
//重写native组件基类的动画数据接收方法
void updateAnimateToNative(string options) override;
class NativeUpdateRenderElement : public BoxRenderElement
{
public:
// ...
void updateAnimateToNative(string options) override;
};
//重写数据接收方法,根据传递过来的自定义数据,自主处理, 这里给的还是标准的scale, rotate数据, 可以传任意数据,然后自己解析实现自由的功能
void NativeUpdateRenderElement::updateAnimateToNative(string options)
{
float value = 0;
const char* p = options.c_str();
cJSON* root = cJSON_Parse(p);
do {
if (root == nullptr) {
break;
}
for (cJSON* item = root->child; item != nullptr; item = item->next) {
if (item->string == nullptr) {
continue;
}
if (!strcmp(item->string, "animatecontrol")) {
for (cJSON* sub_item = item->child; sub_item != nullptr;
sub_item = sub_item->next) {
if (!strcmp(sub_item->string, "update")) {
for (cJSON* update_item = sub_item->child; update_item != nullptr;
update_item = update_item->next) {
if (!strcmp(update_item->string, "scale")) {
value = pickFloatValue(update_item);
// VLOGD("set scale: %.2f", value);
setScaleX(value);
setScaleY(value);
} else if (!strcmp(update_item->string, "scaleX")) {
value = pickFloatValue(update_item);
setScaleX(value);
} else if (!strcmp(update_item->string, "scaleY")) {
value = pickFloatValue(update_item);
setScaleY(value);
} else if (!strcmp(update_item->string, "translate")) {
value = pickFloatValue(update_item);
setTranslateX(std::pair<bool, float>{false, value});
setTranslateY(std::pair<bool, float>{false, value});
// VLOGD("set translate: %.2f", value);
} else if (!strcmp(update_item->string, "translateX")) {
value = pickFloatValue(update_item);
setTranslateX(std::pair<bool, float>{false, value});
} else if (!strcmp(update_item->string, "translateY")) {
value = pickFloatValue(update_item);
setTranslateY(std::pair<bool, float>{false, value});
} else if (!strcmp(update_item->string, "opacity")) {
value = pickFloatValue(update_item);
setOpacity(value);
// VLOGD("set opcity: %.2f", value);
} else if (!strcmp(update_item->string, "rotate")) {
value = pickFloatValue(update_item);
setRotate(value);
// VLOGD("set rotate: %.2f", value);
}
}
} else {
break;
}
}
}
}
} while (0);
if (root) {
cJSON_Delete(root);
}
}
复制代码
定制进度和 UpdateControl
此例为上述进度和 UpdateControl 定制的集合体。
<!-- 由native组件控制进度以及接收数据 NativeDragShow -->
<template>
<native_drag_update_component id="native_drag_update_component">
<input class="btn" id="btn1" type="button" value="button1" />
<input class="btn" id="btn2" type="button" value="button2" />
<input class="btn" id="btn3" type="button" value="button3" />
</native_drag_update_component>
</template>
<script>
export default {
onShow() {
const cNative = this.$element('native_drag_update_component')
const animation = cNative.animate([], {
animatecontrol: {
init: {
onAnimateUpdate(data) {
cNative.animate([], {
animatecontrol: {
update: {
translate: data.progress * 100,
opacity: 1 - data.progress,
rotate: 360 * data.progress,
},
},
})
},
},
},
})
},
}
</script>
复制代码
class DragNativeUpdateRenderElement : public BoxRenderElement
{
public:
// ...
void updateAnimateToNative(string options) override;
};
bool DragNativeUpdateRenderElement::deliverTouchEvent(
shared_ptr<TouchEvent> event_)
{
if (child_list_->size() <= 0) {
return false;
}
int action = event_->getAction();
switch (action) {
case TOUCH_EVENT_ACTION_MOVE: {
float x = event_->getX();
float y = event_->getY();
float deltax = last_motion_x_ - x;
float deltay = last_motion_y_ - y;
float distance = abs(deltax);
if (distance > getSize().width) {
distance = distance - getSize().width;
}
float progress = distance / getSize().width;
updateAnimateToApp(progress); //调用 rpk的 callback方法
break;
}
case TOUCH_EVENT_ACTION_DOWN: {
if (last_motion_x_ <= 0 && last_motion_y_ <= 0) {
float x = event_->getX();
float y = event_->getY();
last_motion_x_ = x;
last_motion_y_ = y;
}
break;
}
case TOUCH_EVENT_ACTION_CANCEL:
case TOUCH_EVENT_ACTION_UP: {
break;
}
}
return true;
}
//自主处理相关数据
void DragNativeUpdateRenderElement::updateAnimateToNative(string options) {
// VLOGD("updateAnimateToNative option: %s", options.c_str());
float value = 0;
const char* p = options.c_str();
cJSON* root = cJSON_Parse(p);
do {
if (root == nullptr) {
break;
}
for (cJSON* item = root->child; item != nullptr; item = item->next) {
if (item->string == nullptr) {
continue;
}
if (!strcmp(item->string, "animatecontrol")) {
for (cJSON* sub_item = item->child; sub_item != nullptr;
sub_item = sub_item->next) {
if(!strcmp(sub_item->string, "update")) {
for (cJSON* update_item = sub_item->child; update_item != nullptr;
update_item = update_item->next) {
if (!strcmp(update_item->string, "scale")) {
value = pickFloatValue(update_item);
// VLOGD("set scale: %.2f", value);
setScaleX(value);
setScaleY(value);
} else if (!strcmp(update_item->string, "scaleX")) {
value = pickFloatValue(update_item);
setScaleX(value);
} else if (!strcmp(update_item->string, "scaleY")) {
value = pickFloatValue(update_item);
setScaleY(value);
} else if (!strcmp(update_item->string, "translate")) {
value = pickFloatValue(update_item);
setTranslateX(std::pair<bool, float>{false, value});
setTranslateY(std::pair<bool, float>{false, value});
// VLOGD("set translate: %.2f", value);
} else if (!strcmp(update_item->string, "translateX")) {
value = pickFloatValue(update_item);
setTranslateX(std::pair<bool, float>{false, value});
} else if (!strcmp(update_item->string, "translateY")) {
value = pickFloatValue(update_item);
setTranslateY(std::pair<bool, float>{false, value});
} else if (!strcmp(update_item->string, "opacity")) {
value = pickFloatValue(update_item);
setOpacity(value);
// VLOGD("set opcity: %.2f", value);
} else if (!strcmp(update_item->string, "rotate")) {
value = pickFloatValue(update_item);
setRotate(value);
// VLOGD("set rotate: %.2f", value);
}
}
} else {
break;
}
}
}
}
} while (0);
if (root) {
cJSON_Delete(root);
}
}
复制代码