自定义动画

更新时间:

语法

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);
  }
}
复制代码
以上内容对您是否有帮助?
  • 毫无帮助
  • 帮助不大
  • 一般
  • 很好
  • 非常好
意见反馈