这个教程里,我们将使用自定义控件功能实现一个具有如下功能的定时器:
- 开启定时任务。
- 能够清除当前设置的定时任务。
- 能够动态的增加可执行任务次数上限。
控件定义
基本信息
首先我们创建一个变量叫 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