块定义描述了块的外观和行为,包括文本、颜色、形状以及块可以连接到的其他块。
JSON 格式与 JavaScript API
Blockly 有两种定义块的方式:JSON 对象和 JavaScript 函数。JSON 格式旨在简化针对具有不同词序的语言开发应用的本地化过程。JSON 格式是定义块的首选方法。
但是,JSON 格式无法直接定义更改器或验证器等高级功能。这些必须使用 JavaScript(通常作为扩展程序)编写。
使用 Blockly 原始 JavaScript 实现的应用也可以将块定义直接写入较低级别的 Blockly API 函数调用,如下面的各种 JavaScript 示例所示。
JSON
Blockly.defineBlocksWithJsonArray([{
"type": "string_length",
"message0": 'length of %1',
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": "String"
}
],
"output": "Number",
"colour": 160,
"tooltip": "Returns number of letters in the provided text.",
"helpUrl": "http://www.w3schools.com/jsref/jsref_length_string.asp"
}]);
JavaScript
Blockly.Blocks['string_length'] = {
init: function() {
this.appendValueInput('VALUE')
.setCheck('String')
.appendField('length of');
this.setOutput(true, 'Number');
this.setColour(160);
this.setTooltip('Returns number of letters in the provided text.');
this.setHelpUrl('http://www.w3schools.com/jsref/jsref_length_string.asp');
}
};
init
函数用于创建块的形状。在此函数的上下文中,关键字 this
是正在创建的实际块。
两个示例都加载了相同的“string_length”块。
在 Web 上,JSON 格式是使用 initJson
函数加载的。这也允许在 Blockly 网页中混合使用这两种格式。最好尽可能使用 JSON 定义块,并且只对 JSON 不支持的块定义部分使用 JavaScript。
下例中的块主要使用 JSON 定义,但使用 JavaScript API 进行了扩展,以提供动态提示。
JavaScript
var mathChangeJson = {
"message0": "change %1 by %2",
"args0": [
{"type": "field_variable", "name": "VAR", "variable": "item", "variableTypes": [""]},
{"type": "input_value", "name": "DELTA", "check": "Number"}
],
"previousStatement": null,
"nextStatement": null,
"colour": 230
};
Blockly.Blocks['math_change'] = {
init: function() {
this.jsonInit(mathChangeJson);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return 'Add a number to variable "%1".'.replace('%1',
thisBlock.getFieldValue('VAR'));
});
}
};
方块颜色
块的主要颜色通过 JSON colour
属性、block.setColour(..)
函数或使用主题并定义块样式来定义。
JSON
{
// ...,
"colour": 160,
}
JavaScript
init: function() {
// ...
this.setColour(160);
}
如需了解详情,请参阅块颜色指南。
语句连接
用户可以使用 nextStatement
和 previousStatement
连接器创建序列块。在 Blockly 的标准布局中,这些连接位于顶部和底部,并且块垂直堆叠。
具有前一个连接器的块不能有输出连接器,反之亦然。术语语句块是指没有值输出的块。语句块通常既有上一个连接,又有下一个连接。
nextStatement
和 previousStatement
连接可以是类型化连接,但标准块不使用此功能。
下次连接
在代码块底部创建一个点,以便在其下方堆叠其他语句。具有下一个连接但没有上一个连接的块通常表示事件,可以配置为带有帽子进行呈现。
JSON
非类型化:
{
...,
"nextStatement": null,
}
类型化(罕见):
{
"nextStatement": "Action",
...
}
JavaScript
非类型化:
this.setNextStatement(true); // false implies no next connector, the default
类型化(罕见):
this.setNextStatement(true, 'Action');
之前的连接
在代码块顶部创建一个凹口,以便将其连接为语句堆栈。
具有先前连接的块不能具有输出连接。
JSON
非类型化:
{
...,
"previousStatement": null,
}
类型化(罕见):
{
"previousStatement": "Action",
...
}
JavaScript
非类型化:
this.setPreviousStatement(true); // false implies no previous connector, the default
类型化(罕见):
this.setPreviousStatement(true, 'Action');
块输出
一个块可能具有单个输出,表示为前缘的公形拼图连接器。输出连接到值输入。带有输出的块通常称为值块。
JSON
非类型化:
{
// ...,
"output": null,
}
输入:
{
// ...,
"output": "Number",
}
JavaScript
非类型化:
init: function() {
// ...
this.setOutput(true);
}
输入:
init: function() {
// ...
this.setOutput(true, 'Number');
}
带有输出连接器的块不能同时具有前面的语句凹口。
屏蔽输入内容
一个块具有一个或多个输入,其中每个输入具有一系列字段,并且可能以连接结尾。内置输入有多种类型。
- 值输入:连接到值块的输出连接。
math_arithmetic
块(加法、减法)是包含两个值输入的块的示例。 - 语句输入:连接到语句块的上一个连接。when 循环的嵌套部分是语句输入的一个示例。
- 虚拟输入:没有块连接。当块配置为使用外部值输入时,则充当换行符。
- 结束行输入:没有块连接,并且始终充当换行符。
JSON 格式和 JavaScript API 使用略有不同的模型来描述其输入。
JSON 中的输入和字段
JSON 定义的块的结构为一系列插值消息字符串(message0
、message1
...),其中每个插值令牌(%1
、%2
...)都是匹配的 JSON argsN
数组中的一个字段或输入端(因此,输入连接器在消息中渲染)。此格式旨在简化国际化。
JSON
{
"message0": "set %1 to %2",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "item",
"variableTypes": [""]
},
{
"type": "input_value",
"name": "VALUE"
}
]
}
插值令牌必须与 args0
数组完全匹配:不重复,没有遗漏。令牌可能会以任何顺序存在,这允许不同的语言更改块的布局。
插值令牌两侧的文本会去掉空格。使用 %
字符的文本(例如,引用百分比时)应使用 %%
,以免将其解释为插值令牌。
参数和参数类型的顺序决定了块的形状。更改其中某个字符串可能会完全更改代码块的布局。这在词序不同于英语的语言中尤为重要。假设有一种语言,其中 "set %1 to %2"
(如上例中使用)需要反转为 "put %2 in %1"
。更改这一个字符串(并保持 JSON 的其余部分不变)会生成以下代码块:
块式自动更改字段顺序、创建虚拟输入,并从外部输入切换到内部输入。
Blockly 还会自动将消息字符串中的所有换行符 (\n
) 替换为末尾行输入。
JSON
{
"message0": "set %1\nto %2",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "item",
"variableTypes": [""]
},
{
"type": "input_value",
"name": "VALUE"
}
]
}
参数
每个消息字符串都与一个相同编号的 args
数组配对。例如,message0
搭配 args0
。插值令牌(%1
、%2
...)引用 args
数组的项。每个对象都有一个 type
字符串。其余参数因类型而异:
您还可以定义自己的自定义字段和自定义输入,并将其作为参数传递。
每个对象还可以有一个 alt
字段。如果 Blockly 无法识别对象的 type
,则会改用 alt
对象。例如,如果将名为 field_time
的新字段添加到 Blockly 中,则使用此字段的块可以使用 alt
为旧版 Blockly 定义 field_input
回退:
JSON
{
"message0": "sound alarm at %1",
"args0": [
{
"type": "field_time",
"name": "TEMPO",
"hour": 9,
"minutes": 0,
"alt":
{
"type": "field_input",
"name": "TEMPOTEXT",
"text": "9:00"
}
}
]
}
alt
对象可能有自己的 alt
对象,因此允许链接。最后,如果 Blockly 无法在 args0
数组中创建对象(在尝试任何 alt
对象之后),则会直接跳过该对象。
如果 message
字符串以输入中未包含的文本或字段结尾,系统会自动将虚拟输入添加到块的末尾。因此,如果块上的最后一个输入是虚拟输入,则可以从 args
数组中将其省略,并且无需插值到 message
中。通过自动添加尾随虚拟输入,翻译人员可以更改 message
,而无需修改 JSON 的其余部分。请参阅本页前面的示例 "set %1 to %2"
(无虚拟输入)和 "put %2 in %1"
(添加了虚拟输入)。
implicitAlign0
在极少数情况下,自动创建的尾随虚拟输入需要与 "RIGHT"
或 "CENTRE"
对齐。如果未指定,则默认值为 "LEFT"
。
在下面的示例中,message0
为 "send email to %1 subject %2 secure %3"
,Blockly 会自动为第三行添加虚拟输入。将 implicitAlign0
设置为 "RIGHT"
会强制此行右对齐。这种对齐方式适用于 JSON 块定义中未明确定义的所有输入,包括用于替换消息中换行符 ('\n'
) 的结束行输入。此外,已弃用的属性 lastDummyAlign0
的行为与 implicitAlign0
相同。
针对 RTL(阿拉伯语和希伯来语)设计块时,左、右互反。因此,"RIGHT"
会向左对齐字段。
message1
、args1
、implicitAlign1
一些代码块自然分为两个或更多个独立部分。我们来考虑下面这个重复块,它有两行:
如果使用一条消息来描述此块,则 message0
属性将为 "repeat %1 times %2 do %3"
。这个字符串对翻译人员来说比较尴尬,很难解释 %2
替代的含义。在某些语言中,甚至可能不需要 %2
虚拟输入。并且可能有多个块想要共享第二行的文本。更好的方法是让 JSON 使用多个消息和参数属性:
JSON
{
"type": "controls_repeat_ext",
"message0": "repeat %1 times",
"args0": [
{"type": "input_value", "name": "TIMES", "check": "Number"}
],
"message1": "do %1",
"args1": [
{"type": "input_statement", "name": "DO"}
],
"previousStatement": null,
"nextStatement": null,
"colour": 120
}
可以用 JSON 格式定义任意数量的 message
、args
和 implicitAlign
属性,从 0 开始,依序递增。请注意,块工厂无法将消息拆分为多个部分,但手动执行此操作相当简单。
JavaScript 中的输入和字段
JavaScript API 包含每种输入类型的 append
方法:
JavaScript
this.appendEndRowInput()
.appendField('for each')
.appendField('item')
.appendField(new Blockly.FieldVariable());
this.appendValueInput('LIST')
.setCheck('Array')
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField('in list');
this.appendStatementInput('DO')
.appendField('do');
this.appendDummyInput()
.appendField('end');
每个附加方法都可以接受代码生成器使用的标识符字符串。虚拟和末行输入很少需要引用,并且标识符通常未设置。
JavaScript API 还包含用于附加自定义输入的通用 appendInput
方法。请注意,在这种情况下,标识符应直接传递给自定义输入的构造函数。
JavaScript
this.appendInput(new MyCustomInput('INPUT_NAME'))
.appendField('an example label')
所有 appendInput
方法(包括泛型和非泛型)都会返回输入对象,以便使用方法链接进一步配置它们。有三种内置方法用于配置输入。
setCheck
JavaScript
input.setCheck('Number');
此可选函数用于对已连接的输入进行类型检查。如果给定的参数为 null(默认值),则此输入可以连接到任何块。如需了解详情,请参阅类型检查。
setAlign
JavaScript
input.setAlign(Blockly.inputs.Align.RIGHT);
此可选函数用于对齐字段(见下文)。有三个自描述值可以作为参数传递给此函数:Blockly.inputs.Align.LEFT
、Blockly.inputs.Align.RIGHT
和 Blockly.inputs.Align.CENTER
。
针对 RTL(阿拉伯语和希伯来语)设计块时,左、右互反。因此,Blockly.inputs.Align.RIGHT
会向左对齐字段。
appendField
创建输入并使用 appendInput
附加到块后,您可以选择将任意数量的字段附加到输入。这些字段通常用作标签,以描述每个输入的用途。
JavaScript
input.appendField('hello');
最简单的字段元素是文本。Blockly 的惯例是使用全小写文本,但专有名词(如 Google、SQL)除外。
输入行可以包含任意数量的字段元素。可以将多个 appendField
调用链接在一起,以便高效地向同一输入行添加多个字段。
JavaScript
input.appendField('hello')
.appendField(new Blockly.FieldLabel('Neil', 'person'));
appendField('hello')
调用实际上是使用显式 FieldLabel 构造函数 appendField(new Blockly.FieldLabel('hello'))
的快捷方式。唯一希望使用构造函数的情况是,指定类名称,以便使用 CSS 规则设置文本的样式。
内嵌与外部
块输入可以呈现为外部或内部。
块定义可以指定一个可选的布尔值,用于控制输入是否为内嵌。如果为 false
,则任何值输入都将是外部输入(例如左侧代码块)。如果为 true
,则任何值输入都将是内嵌的(如上面的右侧代码块)。
JSON
{
// ...,
"inputsInline": true
}
JavaScript
init: function() {
// ...
this.setInputsInline(true);
}
如果未定义,Blockly 将利用一些启发法来猜测哪种模式是最佳的。假设 Blockly 做出了正确的选择,最好将此字段保留为未定义状态,因为不同的语言翻译可以自动采用不同的模式。请参阅本页前面的 "set %1 to %2"
(外部输入)和 "put %2 in %1"
(内嵌输入)的 JSON 示例。
当某个块可能包含小型输入(例如数字)时,请使用内嵌输入。如果启用了 collapse
配置,则用户可以通过上下文菜单切换此选项(如果工具箱包含类别,则默认为 true)。
字段
字段定义块中的大部分界面元素。其中包括字符串标签、图片和字面量数据(如字符串和数字)的输入。最简单的示例是 math_number
代码块,它使用 field_input
让用户输入数字。
字段使用 appendField 附加到块。
Blockly 提供了许多内置字段,包括文本输入、颜色选择器和图片。您也可以创建自己的字段。
→ 详细了解内置字段。
→ 详细了解如何创建自定义字段。
图标
图标定义块上的界面元素,用于显示有关块的“元”信息。
图标是使用 addIcon 附加到块的。
Blockly 提供了许多内置图标,包括评论图标和警告图标。您也可以创建自己的图标。
→ 详细了解如何创建自定义图标。
提示
用户将鼠标悬停在木块上时,提示功能可提供即时帮助。 如果文本过长,系统会自动换行。
JSON
{
// ...,
"tooltip": "Tooltip text."
}
JavaScript
init: function() {
this.setTooltip("Tooltip text.");
}
在 JavaScript API 中,提示还可以定义为函数,而不是静态字符串。这样可以提供动态帮助。有关提示示例,该提示会根据所选的下拉菜单选项而变化,请参阅 math_arithmetic
。
JavaScript
Blockly.Blocks['math_arithmetic'] = {
init: function() {
// ...
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('OP');
var TOOLTIPS = {
'ADD': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
'MINUS': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
'MULTIPLY': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
'DIVIDE': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
'POWER': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER
};
return TOOLTIPS[mode];
});
}
};
使用 JavaScript API,块可以指定一个函数,而不是一个返回提示字符串的静态字符串。这样,您就可以使用动态提示。
如需查看示例,请参阅 math_arithmetic
。
自定义
您还可以通过提供自定义渲染函数来自定义提示的外观。创建一个接受两个参数的函数:
- 首先是
<div>
元素,您将在其中渲染内容 - 其上显示的是用户将鼠标悬停在哪个元素上
在函数的正文中,您可以将任何喜欢的内容呈现到 div 中。如需获取在鼠标悬停的块上定义的提示字符串,您可以调用 Blockly.Tooltip.getTooltipOfObject(element);
,其中 element
是上面的第二个参数。
最后,注册此函数,以便 Blockly 可以在适当的时间调用它:
Blockly.Tooltip.setCustomTooltip(yourFnHere);
如需查看示例,请参阅自定义提示演示。
“帮助”网址
组成要素可以有与之关联的帮助页面。Blockly for Web 的用户可通过以下方式使用该功能:右键点击相应代码块,然后从上下文菜单中选择“帮助”。如果此值为 null
,则菜单会显示为灰色。
JSON
{
// ...,
"helpUrl": "https://en.wikipedia.org/wiki/For_loop"
}
JavaScript
init: function() {
// ...
this.setHelpUrl('https://en.wikipedia.org/wiki/For_loop');
}
使用 JavaScript API,代码块可以指定一个函数,而不是一个返回网址字符串的静态字符串,从而允许动态帮助。
更改监听器和验证器
块可以具有更改监听器函数,当对工作区(包括与块无关的函数)进行任何更改时,就会调用这些函数。它们主要用于在工作区之外设置代码块的警告文本或类似用户通知。
通过使用函数调用 setOnChange 来添加该函数。如果您计划在所有平台上使用该函数,则可以在初始化期间或通过 JSON 扩展程序添加该函数。
JSON
{
// ...,
"extensions":["warning_on_change"],
}
Blockly.Extensions.register('warning_on_change', function() {
// Example validation upon block change:
this.setOnChange(function(changeEvent) {
if (this.getInput('NUM').connection.targetBlock()) {
this.setWarningText(null);
} else {
this.setWarningText('Must have an input block.');
}
});
});
JavaScript
Blockly.Blocks['block_type'] = {
init: function() {
// Example validation upon block change:
this.setOnChange(function(changeEvent) {
if (this.getInput('NUM').connection.targetBlock()) {
this.setWarningText(null);
} else {
this.setWarningText('Must have an input block.');
}
});
}
}
系统会调用该函数,传入更改事件。
在该函数内,this
是指块实例。
由于系统会在进行任何更改时调用该函数(如果使用的话),开发者应确保监听器快速运行。此外,还应注意工作区更改,这些更改可能会级联或环回监听器。
有关示例,请参阅 controls_flow_statements
、logic_compare
和 procedures_ifreturn
代码块。
请注意,可修改字段有自己的事件监听器,用于验证输入和产生副作用。
赋值函数
借助 Mutator,高级块能够改变形状,最值得注意的是,用户可以通过打开对话框来添加、移除或重新排列组件。可以使用 mutator
键通过 JSON 添加更改器。
JSON
{
// ...,
"mutator":"if_else_mutator"
}
按块配置
块实例有许多属性,用于配置它们对用户的行为方式。这些参数可用于限制工作区以反映域的某些属性(例如,只有一个“start”事件),或专注于用户的操作(例如教程)。
可删除状态
block.setDeletable(false);
如果此政策设为 false,用户将无法删除相应屏蔽设置。在可修改的工作区中,块默认设置为可删除。
所有屏蔽(甚至是不可删除的屏蔽)都可以以编程方式删除:
block.dispose();
可修改的状态
block.setEditable(false);
如果设置为 false,用户将无法更改该块的字段(例如下拉菜单和文本输入)。在可修改的工作区中,块默认处于可修改状态。
移动状态
block.setMovable(false);
如果设置为 false,用户将无法直接移动图块。如果某个不可移动块是另一个块的子块,则不能与该块断开连接,不过,如果移动父级块,该块将会随其父块一起移动。块在可修改的工作区上默认为可移动。
任何块(即使是不可移动的块)在位于工作区中后,可以通过程序化方式移动。
block.moveBy(dx, dy)
块在工作区中的起始位置默认为 (0, 0)。
禁止使用数据流量
this.data = '16dcb3a4-bd39-11e4-8dfc-aa07a5b093db';
data 是附加到块的可选任意字符串。当块序列化时,数据字符串会随之一起序列化。这包括复制或复制/粘贴块的情况。
这通常用于将块与外部资源相关联。
序列化为 JSON 后,数据将作为顶级属性存储在块中:
{
"type": "my_block",
"data": "16dcb3a4-bd39-11e4-8dfc-aa07a5b093db",
// etc..
}
序列化为 XML(旧的冰箱式序列化系统)时,数据字符串将存储在代码块内的 <data></data>
标记中:
<block type="my_block">
<data>16dcb3a4-bd39-11e4-8dfc-aa07a5b093db</data>
<!-- etc... -->
</block>
破坏
块具有 destroy
钩子,当它们从工作区中删除时,系统会调用该钩子。这可用于销毁与分块关联且不再需要的任何后备数据模型/外部资源。
JSON
{
// ...,
"extensions":["destroy"],
}
Blockly.Extensions.registerMixin('destroy', {
destroy: function() {
this.myResource.dispose();
}
});
JavaScript
Blockly.Blocks['block_type'] = {
destroy: function() {
this.myResource.dispose();
}
}
destroy
方法会在处置块的父级之后、其任何子级或字段之前被调用。
上下文菜单
默认情况下,代码块具有右键点击上下文菜单,用户可以执行添加注释或复制代码块等操作。
您可以通过以下方式停用单个块的上下文菜单:
block.contextMenu = false;
您还可以自定义菜单中显示的选项。如需为所有代码块自定义菜单,请参阅上下文菜单文档。如需为单个代码块自定义菜单,您可以实现 customContextMenu
。此函数接受一系列菜单选项并适当修改,这意味着您可以添加和移除项。
每个菜单选项都是一个包含以下三个属性的对象:
text
是显示文本。enabled
是一个布尔值。停用后,该选项会显示,但带有灰色文字。callback
是点击选项时要调用的函数。