这个教程里,我们将使用自定义控件功能实现一个具有如下功能的定时器:
- 开启定时任务。
- 能够清除当前设置的定时任务。
- 能够动态的增加可执行任务次数上限。
控件定义
基本信息
首先我们创建一个变量叫 TMyTimer。变量名遵循 JS 的命名规范即可,但按照传统,我们习惯以 T 作为开头(T 代表 Type)。
const TMyTimer = {};
接下来我们需要定义控件的基本信息。type 代表控件的类型,按照传统一般为全大写并以 _WIDGET 结尾)。icon 代表控件的图标。这里使用官方提供的一个定时器图标 'icon-widget-timer'。如果需要使用自定义的图标,需要额外上传图片资源(暂不支持)。title 代表控件的名称,会在拖拽图标的下方以及创建控件实例时作为控件的默认名称显示。
const TMyTimer = {type: 'TIMER_WIDGET',icon: 'icon-widget-timer',title: '定时器',};
控件属性
接下来我们需要定义控件的属性。首先添加字段 properties。我们通过一个对象定义属性,具体每个字段的含义请参考 自定义控件开发指南。这里我们定义了一个只读的布尔值属性叫 running,代表当前的定时器是否正在执行定时任务。我们还定义了一个可以读写的属性叫 limit,代表任务的执行次数上限,默认为5次。
const TMyTimer = {type: 'TIMER_WIDGET',icon: 'icon-widget-timer',title: '定时器',properties: [{key: 'running',label: '是否启用',valueType: 'boolean',defaultValue: false,readonly: true,},{key: 'limit',label: '任务执行次数上限',valueType: 'number',defaultValue: 5,},], properties: [{key: 'running',label: '是否启用',valueType: 'boolean',defaultValue: false,readonly: true,},],};
控件方法
接下来我们需要定义控件的方法。首先添加字段 methods。和属性类似,我们通过一个对象定义方法的基本属性,具体每个字段的含义请参考 自定义控件开发指南。这里我们定义了两个方法 setInterval 和 clearInterval,一个用于开启定时器,一个用于清除当前的定时任务。
const TMyTimer = {type: 'TIMER_WIDGET',icon: 'icon-widget-timer',title: '定时器',properties: [{key: 'running',label: '是否启用',valueType: 'boolean',defaultValue: false,readonly: true,},{key: 'limit',label: '任务执行次数上限',valueType: 'number',defaultValue: 5,},],methods: [{key: 'setInterval',label: '设置定时任务',params: [{key: 'time',type: 'number',}],},{key: 'clearInterval',label: '清除定时任务',params: [],},],};
控件事件
接下来我们需要定义控件的事件。首先添加字段 events。和属性、方法类似,我们通过一个对象定义事件的基本属性,具体每个字段的含义请参考 自定义控件开发指南。这里我们定义了一个事件 OnTimeIsUp。通过 setInterval 开启定时器后,每隔一定的时间就会触发该事件,我们可以在该事件积木下拼接其他积木来实现回调函数的效果。我们还定义了事件 onLimitChange,用于监听属性 limit 的变化。当通过积木改变 limit 的值时,我们可以通过该事件的返回参数拿到最新的 limit 数值。
const TMyTimer = {type: 'TIMER_WIDGET',icon: 'icon-widget-timer',title: '定时器',properties: [{key: 'running',label: '是否启用',valueType: 'boolean',defaultValue: false,readonly: true,},{key: 'limit',label: '任务执行次数上限',valueType: 'number',defaultValue: 5,},],methods: [{key: 'setInterval',label: '设置定时任务',params: [{key: 'time',type: 'number',}],},{key: 'clearInterval',label: '清除定时任务',params: [],},],events: [{key: 'onTimeIsUp',label: '时间到了',params: [],},{key: 'onLimitChange',label: '上限改变',params: [{key: 'limit',label: '任务执行次数上限',type: ['number'],},],},],};
有了这部分的定义,我们就能够生成编辑态下的控件定义,包括控件选择界面,属性面板和积木外观。
接下来我们需要定义定时器在运行时的行为表现,也就是调用了上面的积木后,控件会做出的响应。这里我们需要实现一个叫 MyTimer 的 JS 类。
运行态定义
自定义的控件类首先需要继承 InvisibleWidget。在定义构造函数 constructor 的时候,我们可以通过 props 拿到控件在编辑态被设置的属性,我们需要调用 super(props) 初始化 InvisibleWidget 里自带的一些属性。对于那些没有暴露给外部设置的属性,我们需要在初始化时给一个默认值。
class MyTimer extends InvisibleWidget {// 编辑态可以修改的属性通过 props 传给运行态的控件实例constructor(props) {super(props);this.timerId = undefined;this.running = false;this._limit = props.limit;}}
这里的 limit 属性值可以通过积木进行读取和设置,在某些情况下,我们希望能够监听到 limit 属性值的变化。这时,我们可以结合 JS 语法中的 setter 和 getter 以及事件发送来实现上述的效果。
对于需要设置 getter 和 setter 的属性,我们有特殊的命名规范:在原名字前加上下划线 _。limit 的 getter 很简单,只是存粹的返回 _limit 的属性值。在 setter 中,我们首先判断了新的 limit 数值是否与当前相等且是否大于0,如果两个条件都满足,则更新 limit 的值并且发送 onLimitChange 事件。
class MyTimer extends InvisibleWidget {constructor(props) {super(props);this.timerId = undefined;this.running = false;this._limit = props.limit;}get limit() {return this._limit;}set limit(newLimit) {if (this._limit !== newLimit && newLimit > 0) {this._limit = newLimit;this.emit('onLimitChange', this._limit);}}}
接下来我们需要定义两个方法 setInterval 和 clearInterval 的代码实现。这两个方法会在执行相应积木的时候被调用。setInterval 方法调用了 JS 原生的 setInterval 方法,根据用户传入每隔一段时间发送 onTimeIsUp 事件。重复调用 setInterval 会清空上一次设置的定时任务,并开启一个新的定时任务。当任务执行次数达到上限时,会自动清除当前设置的定时任务。clearInterval 则是清除当前设置的定时任务。
class MyTimer extends InvisibleWidget {constructor(props) {super(props);this.timerId = undefined;this.running = false;this._limit = props.limit;}get limit() {return this._limit;}set limit(newLimit) {if (this._limit !== newLimit && newLimit > 0) {this._limit = newLimit;this.emit('onLimitChange', this._limit);}}setInterval(time) {if (this.timerId) {clearInterval(this.timerId);}this.running = true;let count = 0;this.timerId = setInterval(() => {if (count < this._limit) {this.emit('onTimeIsUp');count++;} else {this.clearInterval();}}, time * 1000);}clearInterval() {if (this.timerId) {clearInterval(this.timerId);this.timerId = undefined;this.running = false;}}}
最后我们需要将这些定义通过两个变量导出,types 对应控件的基本定义,widget 对应用于生成运行态实例的控件类。
...exports.types = TMyTimer;exports.widget = MyTimer;
将写好的内容保存到 timer.js 中,通过 Web 编辑器导入,我们就可以正常的使用自定义的定时器控件啦!
参考资料
- 定时器控件 JS 源码:timer.js
