本文基于Web Blockly,整理一下可视化编程工具-Blockly 的常用配置,包括:工作区配置、添加自定义块、配置工具箱、配置代码生成器等。
Block
)Toolbox
)配置Grid
)Zoom
)把 Blockly 放到网页上最简单的方法是将其注入到一个空div
中。
首先,引入 Blockly 脚本及核心“块”:
注意:实际路径可能与上面不同,请按 Blockly 的实际位置调整上面路径
引入包含用户语言的消息定义(本例中使用了英语):
在页面创建一个空div
元素,并设置其尺寸:
在页面任意位置定义好“工具栏”结构:
最后,在页面底部(之前)创建调用脚本,完成 Blockly 的初始化:
workspace
变量当前并不会使用,但当需要保存“块”或生成代码时,它会变得非常重要。如果有多个 Blockly 实例注入到同一个页面时,应保存其返回的工作区保存在不同的变更中。
一个好的Web应用将调整 Blockly 的大小以填充屏幕的可用空间,而不是将其固定成一个尺寸。以下演示了一种创建可调尺寸的 Blockly 的方法,这本示例中只需简单的三步,就可以创建一个非固定尺寸的工作区:
使用HTML的table
元素或div
及CSS,创建一个空区域,并确保该区域有一个唯一的ID(本例中为blocklyArea
)
一个在线示例,在页底部使用table
进行定义
与创建固定尺寸工作区一样引入 Blockly,添加脚本、blocklyDiv
元素、工具栏、及初始化脚本。
最后一步是将blocklyDiv
元素定位到blocklyArea
元素上。这样,就需要移除blocklyDiv
元素的height
、width
样式,并添加绝对定位:
然后用一个同样位于
blocklyDiv
之上的blocklyArea
取代注入脚本:
3. 添加自定义块(
Block
)虽然 Blockly 已经定义了大量的标准块,但大多数应用仍然需要定义和实现一些自己业务相关的“块”
“块”有三个组件构成:
块(Block
)定义对象
- 定义块的外观和行为,包括文本、颜色、字段和连接工具箱(Toolbox
引用
- 工具箱XML中对块类型的引用,这样用户才能将其添加到工作区中生成器函数
- 用于生成块的代码字符串。该函数总是使用JavaScript编写,虽然目标语言可能不是JavaScrpit,基至运行环境也可能不是Web
Blockly 通过页面中脚本文件加载“块”,在 下面定义是字符串定义形式,两种方式是等价的: 两种定义方式,都会生成如下工具箱: 工具箱中的模块可以类别进行组织。以下示例中,包含了 完成定义后,其效果如下: “分类”表示会改变 Blockly 的UI,以支持更大的应用。而“滚动条”的出现,就提供了一个无限大的工作空间。其它的,如:“垃圾桶”、“上下文菜单”等,都可以通过配置选项来设置。 定义分类时,可以通过一个 设置颜色后,工具栏效果如下: 以下有两个分类 开发者还可以使用 类别可以嵌套在其它类别中。如下所示,有两个顶级类别 XML可以包含自定义块或块分组。现在有以下4个块: 以下工具栏中包括了上面4个块: 这些定制块或组的XML与Blockly的XML存储格式相同。因此,为此类块构造XML的最简单方法是使用 “阴影块”是执行几个函数的占位符块,它们具有以下特征: 阴影块不能直接用 阴影块是占位符块,它们可执行多种功能: 阴影块无法直接使用代码构建。但可以使用常规块,然后将XML中的 添加一个 默认情况下,两个相邻块之间的分隔间隔为24像素。可以通过修改 通过调整块之间间隙,就可以在工具箱中创建逻辑块组: 就像“块”一样,你也可以将一个按钮或标签放在工具栏的任何位置: 还可以对按钮和标签使用CSS样式。在上面示例中,第一个 按钮需要回调一个函数,但标签不用。设置按钮点击时的回调函数: 工具箱中的“块”,可以在XML中通过 禁用块可用于限制用户的选择。这可以用于用户完成某些成就后解锁块的场景: 应用可以在任何时候通过调用以下函数来修改工具箱: 在初始配置下, 别外,更新工具栏可能会导致一些较小的UI重置: 大多数 Blockly 应用都需要将用户程序转换为JavaScript、Python、PHP、Lua、Dart或其它语言,这一转换过程是由 Blockly 客户端完成的。 首先,引入所需生成语言的生成器。Blockly 中包含了以下生成器: 生成器类需要在 应用调用时,用户块可以随时从应用中导出到代码: 将以上代码中的 生成的操作非常快,所以频繁调用生成函数也不会有问题。这样就可以通过添加 Blockly 事件监听来实时生成代码: Blockly 的主工作区可以有一个网格。而网格可以对块进行分隔,从而实现更整洁的布局。当工作区较大时,这会非常有用。 注入 Blockly 时,可以在其配置选项中启用网格: 网格最重要的配置项就是 以下为将 Blockly 的主工作区大小是可调的,其大小可以由用户动态控制,或由开发者设置为静态的。 设置为 设置为 初始放大基数。对于多层应用来说, 最大可放大倍数,默认为 最小可缩小倍数,默认为 每次放大或缩小时,维放速度比。即: 工作区上的每个更改都会触发事件。这些事件完全描述了每次更改前后的状态。 工作区对象( 在以下示例中,会检测用户创建的第一条注释,然后发出警报,并停止监听,从而不进一步触发警报: 此外,Blockly 还提供了另一种监听事件流的方法,可以在每个“块”中定义一个 所有事件都具有以下共同属性: 你的应用是托管在云端的,那么你可以使用 Blockly 的云存储功能,并利用这一服务的优势来保存、加载、分享、或发布你的程序。 首先,需要将 Blockly 发布到应用引擎上: 上传 Blockly 后,就可以在浏览器中输入步骤2中创建的URL。然后就能到看到Demo列表,包括云存储Demo。 在云存储Demo的demos/storage/index.html文件中有以下特点。 首先,有一个加载云存储API的脚本: 请注意,该脚本假设只有页面上只有一个 Blockly 工作区。还有下面这些消息定义,应该根据你需要修改这些定义: 将这些消息解析成其它语言的示例,可以参考 Blockly Games 中的json目录 保存当前块调用 在页面加载时重新保存,仅需在 Blockly 注入页面,通过根据URL中的Hash调用 从本地存储中恢复块,只需要在 Blockly 注入后超时调用 在用户离开时自动备份到本地存储,可调用blocks/
目录下包含了几个标准块示例。如果需要创建一个新的块,就需要创建个包含块定义的脚本文件,并将其添加到引用页的
4.2 类别
'Control'
和'Logic'
两个分类:
colour
属性来指定该分类的颜色。颜色使用0~360
之间的数字表示:
4.3 动态类别
'Variables'
和'Functions'
用于指定形为,分类里面并没有内容,但定义分类时添加了'custom'
属性,值可以是'VARIABLE'
或'PROCEDURE'
。这些分类,将使用适合的“块”自动填充:custom
属性来创建动态填充弹出类别。例如,要创建一个自定义的弹出颜色块:
/**
* Construct the blocks required by the flyout for the colours category.
* @param {!Blockly.Workspace} workspace The workspace this flyout is for.
* @return {!Array.} Array of XML block elements.
*/
myApplication.coloursFlyoutCallback = function(workspace) {
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
var colourList = myApplication.getPalette();
var xmlList = [];
if (Blockly.Blocks['colour_picker']) {
for (var i = 0; i < colourList.length; i++) {
var blockText = '
myWorkspace.registerToolboxCategoryCallback(
'COLOUR_PALETTE', myApplication.coloursFlyoutCallback);
4.4 类别树
'Core'
和'Custom'
,每个类别中又包含了两个子类别:
4.5 块分组
logic_boolean
块: math_number
块,用于修改时显示42而不是0: controls_for
块,其中包括了3个math_number
块: math_arithmetic
块,其中包括了两个math_number
“阴影块”:
Code application
来构建块,然后切换到XML
选项卡并复制结果。
Code application
构造,但可以通过替换普通块的XML中的
和为
和来构造。
4.6 阴影块
4.7 分隔器
标签到任意两个分类之间,将会创建一个“分隔器”。sep
标签的gap
属性来调整距离:
4.8 按钮与标签
label
标签使用了自定义样式,而button
则使用了默认样式。yourWorkspace.registerButtonCallback(yourCallbackKey, yourFunction).
4.9 禁用
disabled
属性将其禁用:
4.10 Toolbox的修改
workspace.updateToolbox(newTree);
newTree
可能是一个节点树或字符串。唯一的限制就是不能修改模式,也就是说在初始工具箱中定义的类别,那么新工具箱也必须有;同样的,初始工具箱中没有的类别,在新工具箱中也不能有。
5. 代码生成器
5.1 生成代码
javascript_compressed.js
python_compressed.js
php_compressed.js
lua_compressed.js
dart_compressed.js
blockly_compressed.js
之后引入。如:
var code = Blockly.JavaScript.workspaceToCode(workspace);
JavaScript
替换为Python
、PHP
、Lua
、或Dart
就可以生成相应的代码。5.2 实时生成
function myUpdateFunction(event) {
var code = Blockly.JavaScript.workspaceToCode(workspace);
document.getElementById('textarea').value = code;
}
workspace.addChangeListener(myUpdateFunction);
6. 网格(
Grid
)6.1 使用网格
var workspace = Blockly.inject('blocklyDiv',
{toolbox: document.getElementById('toolbox'),
grid:
{spacing: 20,
length: 3,
colour: '#ccc',
snap: true},
trashcan: true});
6.2 网格配置参数
Spacing
spacing
,它定义了网格中点的距离。其默认值是0
,其结果是不会有网格。以下演示了分别设置为10、20、40时的效果:Length
length
是定义网格端点形状的数字。长度为0的结果是一个看不见的网格,长度为1(默认值)会是点,一个更长的长度会导致交叉,长度等于或大于spacing
时将没有间隔。下面是将length
分别设置为1、5和20的示例:Colour
colour
属性定义了网格端点的颜色,可以使用任何与CSS兼容的格式,如:#f00
、#ff0000
或rgb(255, 0, 0)
,其默认值是#888
。colour
分别设置为#000
、#ccc
和#f00
的效果:Snap
snap
属性是一个布尔值,用于设置当放置在工作空间时块是否应该锁定到最近的网格点。其默认值为false
7. 缩放(
Zoom
)7.1 使用缩放
zoom
是 Blocly 的初始化选项之一:var workspace = Blockly.inject('blocklyDiv',
{toolbox: document.getElementById('toolbox'),
zoom:
{controls: true,
wheel: true,
startScale: 1.0,
maxScale: 3,
minScale: 0.3,
scaleSpeed: 1.2},
trashcan: true});
7.2 缩放配置参数
controls
true
时,会显示zoom-centre、zoom-in、and zoom-out三个按钮,默认为false
。wheel
true
时允许鼠标滚轮缩放,默认为false
startScale
startScale
会在第一层设置一个高级值,这样在子层就可以根据这个值进行更复杂的缩放。默认为1.0
maxScale
3
minScale
0.3
scaleSpeed
scale = scaleSpeed ^ steps
。注意,缩小时使用减,而放大时使用加。默认为1.2
8. 事件
8.1 事件监听
workspace
)中有addChangeListener
和removeChangeListener
两个方法,可用于监听事件流。function onFirstComment(event) {
if (event.type == Blockly.Events.CHANGE &&
event.element == 'comment' &&
!event.oldValue && event.newValue) {
alert('Congratulations on creating your first comment!')
workspace.removeChangeListener(onFirstComment);
}
}
workspace.addChangeListener(onFirstComment);
'onchange'
函数,该函数会在块发生变化时被调用。8.2 事件类型
type
- string。Blockly.Events.CREATE
、Blockly.Events.DELETE
、Blockly.Events.CHANGE
、Blockly.Events.MOVE
、Blockly.Events.UI
之一workspaceId
- string。工作区UUID。工作区可以通过Blockly.Workspace.getById(event.workspaceId)
找到blockId
- string。块的UUID。块可以通过workspace.getBlockById(event.blockId)
找到group
- string。UUID 分组。有些事件是不可分割的组中的一部分。例如,在堆栈中插入语句Blockly.Events.CREATE
Create
事件,其有两个附加属性:
xml
- object。定义新块及任何连接子块的XMLids
- array。包含新块及任何连接子块Id的数组Blockly.Events.DELETE
Delete
事件,其有两个附加属性:
oldXml
- object。所删除块及任何连接子块的XMLids
- array。包含所删除块及任何连接子块Id的数组Blockly.Events.CHANGE
Change
事件,其有4个附加属性:
element
- string。'field'
、'comment'
、'collapsed'
、'disabled'
、'inline'
、'mutate'
之一name
- string。所更改字段的名称oldValue
- value。原始值newValue
- value。修改后的值Blockly.Events.MOVE
Move
事件,其有以下6个附加属性:
oldParentId
- string。其旧父块的UUID,当为顶级块时为Undefined
oldInputName
- string。旧输入值,当为顶级块时为Undefined
oldCoordinate
- object。当为顶级块时,则为X和Y坐标;否则为Undefined
newParentId
- string。其新父块的UUID,当为顶级块时为Undefined
newInputName
- string。新输入值,当为顶级块时为Undefined
newCoordinate
- object。当为顶级块时,则为X和Y坐标;否则为Undefined
Blockly.Events.UI
UI
事件,其有以下3个附加属性:
element
- string。'selected'
、'category'
、'click'
、'commentOpen'
、'mutatorOpen'
、'warningOpen'
值之一oldValue
- value。原始值newValue
- value。修改后的值9. 云存储
9.1 设置应用引擎
appengine/app.yaml
,并将blockly-demo
中的应用Id修改为上一步所创建的Idappengine/static/
:
demos/
msg/
media/
*_compressed.js
blockly_uncompressed.js
,则同样需要将其复制到appengine/static/
。复制core
到appengine/static/
,并复制closure-library/
到上级目录appengine/
blocks
、generators
和tests
几个目录到指定位置appengine
,然后点击“Deploy”按钮。如果习惯使用命令行,那么执行:appcfg.py --oauth2 update appengine/
9.2 云端通讯
BlocklyStorage.HTTPREQUEST_ERROR = 'There was a problem with the request.\n';
BlocklyStorage.LINK_ALERT = 'Share your blocks with this link:\n\n%1';
BlocklyStorage.HASH_ERROR = 'Sorry, "%1" doesn\'t correspond with any saved Blockly file.';
BlocklyStorage.XML_ERROR = 'Could not load your saved file.\n' +
'Perhaps it was created with a different version of Blockly?';
BlocklyStorage.link()
即可:BlocklyStorage.retrieveXml
:if ('BlocklyStorage' in window && window.location.hash.length > 1) {
BlocklyStorage.retrieveXml(window.location.hash.substring(1));
}
9.3 本地存储
storage.js
API 还有浏览器本地存储能力。这可以用于替代云存储,或二者结合使用。BlocklyStorage.restoreBlocks
即可:window.setTimeout(BlocklyStorage.restoreBlocks, 0);
BlocklyStorage.backupOnUnload
实现,且其可以通过监听页面的unload
事件来自动调用:BlocklyStorage.backupOnUnload();