AWTK计时器篇

AWTK 计时器篇

一,前序

  在许多的 GUI 或者 3D 引擎中,都会有一个概念这个概念就是计时器,而计时器就是用户开辟一个闹钟来定时定后的来通知告诉玩家时间到了,该干点什么事情了。
  其实计时器是一个很常用的功能来的,例如定时刷新帧率,某个时间点触发事件等,所以接下来我们来介绍一下 AWTK 的计时器,这里我打算通过计时器来简单做一个控件移动动画(当然做其他的效果也是可以的,这里只是为了简单介绍计时器),其实所有 GUI 的动画都是通过计时器来做的(而 AWTK 也有动画系统,主要是方便用户直接使用动画而已,本质也是通过计时器来实现的),话不多说了,我们开始看看计时器用法吧。

二,了解计时器

  其实计时器有三个主要的部分,一个就是触发事件,一个是触发时间和一个是否连续触发,而 AWTK 提供的 API 其实也是很简单的:

/* awtk/src/tkc/timer_info.h */
/**
 * @class timer_info_t
 * @parent object_t
 * @annotation ["scriptable"]
 * 单个定时器的信息。
 *
 */
struct _timer_info_t {
  object_t object;

  /**
   * @property {timer_func_t} on_timer
   * @annotation ["readable"]
   * 定时器回调函数。
   */
  timer_func_t on_timer;

  /**
   * @property {void*} ctx
   * @annotation ["readable", "scriptable"]
   * 定时器回调函数的上下文
   *
   */
  void* ctx;

  /**
   * @property {uint32_t} id
   * @annotation ["readable", "scriptable"]
   * 定时器的ID
   *
   * > 为TK\_INVALID\_ID时表示无效定时器。
   */
  uint32_t id;

  /**
   * @property {uint64_t} now
   * @annotation ["readable", "scriptable"]
   * 当前时间(相对时间,单位为毫秒)。
   *
   */
  uint64_t now;

  /**
   * @property {uint64_t} start
   * @annotation ["readable"]
   * 起始时间(相对时间,单位为毫秒)。
   *
   */
  uint64_t start;

  /**
   * @property {uint32_t} duration
   * @annotation ["readable"]
   * 时间间隔(单位为毫秒)。
   *
   */
  uint32_t duration;

  /**
   * @property {tk_destroy_t} on_destroy
   * @annotation ["readable"]
   * 定时器销毁时的回调函数。
   */
  tk_destroy_t on_destroy;

  /**
   * @property {void*} on_destroy_ctx
   * @annotation ["readable"]
   * 定时器销毁时的回调函数上下文。
   */
  void* on_destroy_ctx;

  /*private*/
  bool_t busy;
  uint64_t last_dispatch_time;
  timer_manager_t* timer_manager;
};

typedef ret_t (*timer_func_t)(const timer_info_t* timer);
/* awtk/src/base/timer.h */
/**
 * @method timer_add
 * 增加一个timer。
 * @annotation ["scriptable:custom", "static"]
 * @param {timer_func_t} on_timer timer回调函数。
 * @param {void*} ctx timer回调函数的上下文。
 * @param {uint32_t} duration 时间。
 *
 * @return {uint32_t} 返回timer的ID,TK_INVALID_ID表示失败。
 */
uint32_t timer_add(timer_func_t on_timer, void* ctx, uint32_t duration);

  可以看到这个函数有三个参数,其中有两个参数是上面说的触发事件(触发函数)和触发时间,但是却没有看到是否连续触发的部分呢?而回调函数的上下文又是什么呢?可以简单的理解为回传参数,在触发回调函数的时候,会把这个上下文通过参数的形式传到回调函数中(回调函数的上下文就是 timer_info_t 类型中的 ctx 成员变量)。
  而我们看到 timer_func_t 的函数类型的返回值为 ret_t ,如果该返回值类型为 RET_REPEAT 则表示还可以执行多一次,如果返回值类型为 RET_REMOVE 表示移除该计时器(但是本质上来说,返回值类型只要不是 RET_REPEAT 都会移除)。

备注:

  1. 这里为什么说返回值类型为 RET_REPEAT 则表示还可以执行多一次,因为可以在回调函数中,根据不同的条件触发不同的返回值类型,来控制是否继续执行下一次,或者退出计时器。
  2. AWTK 的计时器的设计和其他的 GUI 来说有点不太一样,就是因为其他的 GUI 一般都是在创建计时器的时候就会设置是重复的次数,但是 AWTK 却是通过回调函数来实现重复的次数,感觉有利有弊吧。(有点算是一个使用上的坑,主要是和大众使用方法不太像)
  3. AWTK 的计时器还有一个缺点,看到 WPF 或者是 winform 的话,计时器是可控件化才做的,就是计时器的使用,可以通过 xml 来呈现出的(配合可视化编程工具,可以直接把计时器拖出来用,会比较方便),但是考虑到 AWTK 的计时器用法不一样,所以可能没有办法做出可视化控件。

三,编写控件移动动画

  首先我们要理顺动画思路,动画就是在一定时间内持续的移动控件,让控件看起来更加的真的像动起来一样的,这里为了减少篇幅,就跳过如何搭建 AWTK 的编写代码环境了,这里采用伪代码描述不相关的代码。

  1. 通过 c 或者 xml 创建一个控件 label 和一个控件 button。
  2. 然后通过一个 button 按钮来创建计时器,如下面的代码。
/**
 * 开启计时器按钮事件
 */
static ret_t on_click_start_timer(void* ctx, event_t* e)
{
	widget_t* label = WIDGET(ctx);
	if (timer_id == 0)
	{
		/*
		* 每16毫秒触发一次动画,一秒有60帧移动的效果,这样看起就像动画了
		* 这里的上下文 ctx 就是 label 控件。
		*/
		timer_id = timer_add(timer_move_func, ctx, 16);
		start_time = timer_manager()->get_time();

		/* 判断控件在左边还是在右边 */
		is_left = label->x > 0;
		/* 记录控件最开始的 x 坐标 */
		start_widget_x = label->x;
	}

	return RET_OK;
}

  1. 创建 timer_move_func 函数,代码如下:
/**
 * 计时器中移动控件的触发事件
 */
ret_t timer_move_func(const timer_info_t* timer)
{
	easing_func_t easing;
	float_t time_percent = 0.0f;
	widget_t* label = WIDGET(timer->ctx);

	/* 
	* easing_get 函数可以获取 AWTK 内置的数学函数,
	* 通过数学函数来计算动画的移动效果,
	* 这样可以让移动看起来更加舒服一些。
	*/
	easing = easing_get(EASING_QUADRATIC_IN);

	/* 计算时间进度的百分比 */
	time_percent = ((float_t)(timer->now - start_time)) / anim_time;

	if (time_percent > 1.0f)
	{
		time_percent = 1.0f;
	}

	/* 移动控件 */
	if (is_left)
	{
		/* 控件在左边的话,就往右移 */
		widget_move(label, start_widget_x - easing(time_percent) * label_move_x_duration, label->y);
	}
	else
	{
		/* 控件在右边的话,就往左移 */
		widget_move(label, easing(time_percent) * label_move_x_duration, label->y);
	}
	
	/* 时间已经到了 */
	if (time_percent == 1.0f)
	{
		timer_id = 0;
		widget_set_text_utf8(label, "stop");
		/* 当然这里也可以返回常用的 RET_OK 类型 */
		return RET_REMOVE;
	}

	widget_set_text_utf8(label, "runing");
	return RET_REPEAT;
}
  1. 运行程序,可以看到点击按钮,然后 label 控件就开始动起来了。

备注:这里附上项目的 github 地址:https://github.com/WNsACE/CSDN_AWTK_DEMO/tree/master/awtk_timer

四,总结

   AWTK 的计时器使用上和其他的 GUI 有所不同,尤其在是否重复执行上面是采取通过回调函数中的返回值来判断的,所以导致使用上需要注意的,其实也可以换一个思路,就是提高了灵活度,可以让回调函数更加的灵活控制执行的次数等等,而 AWTK 提供一个上下文的传参,提高了数据传输的灵活度,让用户使用计时器的时候更加方便。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值