一、 Blockly 入门
什么是blockly?
blockly是 google 发布的可视化编程工具,是一个基于web技术构建的库;blockly的目的是帮助客户创建app,使得用户(app的使用者)编程就像搭积木一样的有趣、容易上手,大名鼎鼎的 scratch 就是一款使用类似技术的少儿编程工具。blockly有几个特点:
- 它是纯粹的javascript库。
- 它是100%面对客户端的,没有任何服务端的依赖。
- 支持各种主流的浏览器: Chrome , Firefox, Safari , Opera, and IE。
- 高度可定制和可扩展。
blockly能做什么?
近期工作的主要任务是针对公司的自动化测试框架进行改造,目前我司自动化测试就是使用python+ selenium 的方式进行自动化测试,测试人员需要使用原生的python代码进行 测试用例 的编写,对编程技能要求较高,我希望通过引入blockly为测试人员提供一个可视化的、搭建积木式的编写测试用例的GUI接口,降低自动化测试的上手门槛。
blockly上手示例
前提条件:需要对 HTML 、 CSS 、Java script 有基本的了解
google官方提供的上手示例教程,这个示例是通过blockly创建一个简单的创造音乐的app,首先下载如下示例代码:
git clone
本示例代码路径如下:
- starter-code :为示例的初始代码;
- complete-code :为示例完成后的代码,如果自己编写期间遇到卡壳的地方,可以和官方提供的完成代码进行对照。
每个目录都包含同样的目录和文件:
- scripts/main.js – app的主逻辑代码
- music_maker.js – 一个小型的用来播放声音的库
- sounds/ – 声音文件
- styles/ – CSS样式文件
- index.html – 首页
浏览器打开starter-code/index.html,显示如下界面(如果首次打开不能完整显示,可能是网络原因,尝试刷新浏览器试试)
编辑模式:
点击edit进行编辑模式,按照提示点击下方的任意数字进行代码编辑
点击任意一个数字之后,将显示一篇空白区域,这里就是我们需要使用blockly进行编码的地方
编辑index.html,在如下两行script代码之前增加一行代码,引入blockly库,
<script src="#34;></script> <!-- 这一行是增加的代码 -->
<script src="scripts/music_maker.js"></script>
<script src="scripts/main.js"></script>
blockly将引入四个模块:
- Blockly core: 主要的核心代码,主要定义了block UI及逻辑
- Built-in block definitions: 常用的积木块定义如循环, 逻辑, 数据, 和 字符串 操作
- The JavaScript generator: 将积木块生成JavaScript代码
- English language files: 积木块的提示语言使用英语
创建blockly工作区:
工作区包括代码块编辑区和工具箱两部分
编辑index.html在id=”blocklyDiv”的div元素中增加如下xml结构:
<xml id="toolbox" style="display: none">
<block type="controls_repeat_ext">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
</xml>
编辑scripts/main.js,在代码的最后添加如下代码
Blockly.inject('blocklyDiv', {
toolbox: document.getElementById('toolbox'),
scrollbars: false
});
现在刷新浏览器,点击edit进入编辑模式,点击任意数字,可以发现显示了blockly工具去,工具箱有一个代码块
创建定制的代码块
本示例是一个创建音乐的app,我们需要创建一个能够播放声音的代码块,通过下拉菜单的方式选择不同的声音文件。
scripts目录下创建一个js文件,命名为sound_blocks.js,并在index. html 首页中引入该js文件
<script src="#34;></script>
<script src="scripts/music_maker.js"></script>
<script src="scripts/sound_blocks.js"></script> <!--这是增加的代码行-->
<script src="scripts/main.js"></script>
然后在sound_blocks.js中添加如下代码
Blockly.defineBlocksWithJsonArray([
{
"type": "play_sound",
"message0": "Play %1",
"args0": [
{
"type": "field_dropdown",
"name": "VALUE",
"options": [
["C4", "sounds/c4.m4a"],
["D4", "sounds/d4.m4a"],
["E4", "sounds/e4.m4a"],
["F4", "sounds/f4.m4a"],
["G4", "sounds/g4.m4a"],
["A5", "sounds/a5.m4a"],
["B5", "sounds/b5.m4a"],
["C5", "sounds/c5.m4a"]
]
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 355
}
]);
以上js代码定义了类型为play_sound的代码块,还需要把它加入到html页面中才能显示
<div id="blocklyDiv" style="height: 480px; width: 400px;">
<xml id="toolbox" style="display: none">
<block type="controls_repeat_ext">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="play_sound"></block> <!-- 这是新增加的代码行 -->
</xml>
</div>
再次刷新浏览器,打开工具箱发现多了一个红色的代码块,该代码块可以用来播放sounds目录的声音文件
保存/加载工作区
现在构建好了需要的代码块,已经可以在代码编辑区进行编写了,接下来需要做的是保存和加载功能。
编辑scripts/main.js,save方法中增加如下代码,改代码实现用户点击保存是将工作区的代码块保存为xml格式
function save(button) {
// 增加的代码行
button.blocklyXml = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
}
保存成功后,下次进入点击数字进入工作区还需要将工作区的代码块加载出来,继续编辑main.js,增加如下方法
// 增加的代码 --- start
function loadWorkspace(button) {
let workspace = Blockly.getMainWorkspace();
workspace.clear();
if (button.blocklyXml) {
Blockly.Xml.domToWorkspace(button.blocklyXml, workspace);
}
}
// 增加的代码 --- end
function enableBlocklyMode(e) {
document.body.setAttribute('mode', 'blockly');
currentButton = e.target;
loadWorkspace(currentButton); // 这一行是增加的代码
}
现在尝试刷新浏览器,点击edit进入编辑模式,点击指定数字进行代码块编辑后点击save保存,然后再次返回后工作区仍然保留了上次编辑的代码块。
生成JavaScript代码
编辑scripts/sound_blocks.js,增加如下代码
Blockly.JavaScript['play_sound'] = function(block) {
let value = '\'' + block.getFieldValue('VALUE') + '\'';
return 'MusicMaker.queueSound(' + value + ');\n';
};
这段代码的会将play_sound代码块生成如下js代码” MusicMaker.queueSound(‘Sounds/c4.m4a’); “(注:这里展示的代码以C4选项为例)。
运行生成的JavaScript代码
编辑scripts/main.js,handlePlay方法中增加如下代码
function handlePlay(event) {
// 增加的代码 -- start
loadWorkspace(event.target);
let code = Blockly.JavaScript.workspaceToCode(Blockly.getMainWorkspace());
code += 'MusicMaker.play();';
try {
eval (code);
} catch (error) {
console.log(error);
}
// 增加的代码 -- end
}
现在music maker的app就全部完成,重新刷新浏览器,点击edit进入编辑模式,然后点击不同的数字进行编程,可以根据代码块的提示设置播放什么声音,播放的次数,编辑完成后点击Done就进入运行模式,此时点击相应的数字就会播放音乐了,还是蛮好玩的,快试试吧。
二、blockly——工作区
下载blockly
首先需要获取blockly代码,官方文档中获取blockly代码有npm、 github 、Unpkg等多种方式。本文使用github,访问blockly github仓库链接
并下载,下载后的blockly代码目录结构如下:
注:上手阶段只需关注图中红色标注的三个文件和目录,其中 msg 目录暂时只用到msg/js目录中的语言包,本文采用msg/js/zh-hans.js,表示为代码块使用的语言为中文。
项目目录结构
新建目录名为blockly-demo的文件夹,增加index.html,将已经下载的blockly代码也复制到该目录下,如下图:
导入基础模块
使用自己喜欢的编辑器,添加如下代码,使用blockly需要引入blockly_compressed.js、blocks_compressed.js、msg/js/zh-hans.js三个基础模块,其中blockly_compressed.js为核心脚本,blocks_compressed.js为核心图形代码块集,zh-hans.js为语言模块,本例使用中文。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset ="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>blockly demo</title>
</head>
<body>
<!-- 导入blockly基础模块 -->
<script src="./blockly/blockly_compressed.js"></script>
<script src="./blockly/blocks_compressed.js"></script>
<script src="./blockly/msg/js/zh-hans.js"></script>
</body>
</html>
创建工作区
现在,通过浏览器打开index.html文件会显示一片空白,什么都不会发生,要使用blockly需要首先创建工作区,工作区是blockly的核心,下图就是一个典型的工作区的样子,由工具箱和图形代码编辑区两部分组成。
要创建工作区首先需要创建一个空白的div元素来承载,另外通过嵌入xml来表示工具箱,由于xml不能在浏览器中正常渲染,因此需要设置为不可见,编辑index.html增加如下代码
<!-- 空白div元素,用来承载工作区 -->
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
<!-- 工具箱 block表示为一种代码块 这里有7个代码块-->
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="controls_repeat_ext"></block>
<block type="logic_compare"></block>
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
</xml>
接下来就是调用blockly提供的api来生成工作区了,编辑index.html在页面的最后增加js脚本,Blockly.inject方法用来注入工作区到id为blocklyDiv的DOM元素中,也就是传入的第一个参数,第二个参数为键值对的字典形式的配置参数,这里传入的参数为id为toolbox表示的工具箱的信息。
<script>
var workspace = Blockly.inject('blocklyDiv',
{toolbox: document.getElementById('toolbox')});
</script>
index.html的完整代码参考
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>blockly demo</title>
</head>
<body>
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="controls_repeat_ext"></block>
<block type="logic_compare"></block>
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
</xml>
<script src="./blockly/blockly_compressed.js"></script>
<script src="./blockly/blocks_compressed.js"></script>
<script src="./blockly/msg/js/zh-hans.js"></script>
<script>
var workspace = Blockly.inject('blocklyDiv',
{toolbox: document.getElementById('toolbox')});
</script>
</body>
</html>
此时浏览器再次打开或者刷新已打开的index.html,页面显示如下图,试试拖放工具箱中的代码块到编辑区,此时我们就完成了一个固定大小的工作区的创建。
三、blockly——可调整大小的工作区
本文在基于这篇文章的基础上演示如何创建一个动态变化的blockly工作区。
1.创建html页面区域
首先需要指定一个html元素来放置blockly工作区,工作区需要根据该元素的坐标和尺寸动态调整布局,该元素可以使用div、table等元素,唯一的要求是需要指定id。至于CSS需要根据自己的应用设计进行性调整,本文以占满屏幕的div元素作为示例,编辑index.html代码,增加如下代码:
<div id="blocklyArea" style="min-height: 100vh;"></div>
2.注入blockly
接下来就是调用blockly的inject方法来向html页面注入工作区代码,和可视化编程工具blockly——工作区一文中的代码是一样的
<div id="blocklyDiv" style="position: absolute"></div>
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="controls_repeat_ext"></block>
<block type="logic_compare"></block>
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
</xml>
<script src="./blockly/blockly_compressed.js"></script>
<script src="./blockly/blocks_compressed.js"></script>
<script src="./blockly/msg/js/zh-hans.js"></script>
<script>
var blocklyArea = document.getElementById('blocklyArea');
var blocklyDiv = document.getElementById('blocklyDiv');
var workspace = Blockly.inject(blocklyDiv,
{toolbox: document.getElementById('toolbox')});
</script>
3.定位
最后一步的目的是监听浏览器窗口大小变化时间,一旦窗口有变化就计算blocklyArea的坐标和尺寸,然后动态调整blockly工作区的尺寸和坐标已适配窗口的变化,相关部分的代码如下:
<script>
var blocklyArea = document.getElementById('blocklyArea');
var blocklyDiv = document.getElementById('blocklyDiv');
var workspace = Blockly.inject(blocklyDiv,
{toolbox: document.getElementById('toolbox')});
// ------------------定位部分代码 start-------------------
var onresize = function(e) {
// 计算blocklyArea元素的绝对坐标和尺寸
var element = blocklyArea;
var x = 0;
var y = 0;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
} while (element);
// 将blocklyDiv定位到blocklyArea区域上
blocklyDiv.style.left = x + 'px';
blocklyDiv.style.top = y + 'px';
blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
Blockly.svgResize(workspace); //重新渲染工作区
};
// 监听浏览器窗口的resize事件,触发onresize方法
window.addEventListener('resize', onresize, false);
// 初次打开页面需要计算一次blocklyArea元素的坐标和尺寸并渲染工作区
onresize();
Blockly.svgResize(workspace);
// ------------------定位部分代码 end-------------------
</script>
浏览器打开index.html,可以看到blockly工作区填满了整个屏幕,尝试调整浏览器窗口大小,工作区尺寸也会随之而变化
完整的代码参考:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>blockly demo</title>
</head>
<body>
<div id="blocklyArea" style="min-height: 100vh;"></div>
<div id="blocklyDiv" style="position: absolute"></div>
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="controls_repeat_ext"></block>
<block type="logic_compare"></block>
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
</xml>
<script src="./blockly/blockly_compressed.js"></script>
<script src="./blockly/blocks_compressed.js"></script>
<script src="./blockly/msg/js/zh-hans.js"></script>
<script>
var blocklyArea = document.getElementById('blocklyArea');
var blocklyDiv = document.getElementById('blocklyDiv');
var workspace = Blockly.inject(blocklyDiv,
{toolbox: document.getElementById('toolbox')});
var onresize = function(e) {
// 计算blocklyArea元素的绝对坐标和尺寸
var element = blocklyArea;
var x = 0;
var y = 0;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
} while (element);
// 将blocklyDiv定位到blocklyArea区域上
blocklyDiv.style.left = x + 'px';
blocklyDiv.style.top = y + 'px';
blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
Blockly.svgResize(workspace);
};
window.addEventListener('resize', onresize, false);
onresize();
Blockly.svgResize(workspace);
</script>
</body>
</html>
四、blockly——工具箱
blockly工具箱支持xml和json两种方式定义,本文使用xml演示,json方式创建工具箱可参考google官网文档
。
创建分组的工具箱
工具箱就是可供用户使用的代码块,默认显示在工作区的左侧,如果代码块较多的话最好能分类组织,工具箱的代码通过在index.html中id为toolbox的xml代码进行定义,blcokly会解析xml并注入到页面图形化的工具箱代码DOM,如下图的工具箱包含了控制、逻辑两个类别。
对应的xml定义代码如下,其中category定义了一个分类,block定义了代码块,type指定的类别都是blockly默认提供的,定义代码在./blockly/blocks目录下
<xml id="toolbox" style="display: none">
<category name="控制">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
</category>
<category name="逻辑">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</xml>
工具箱还可以多层嵌套定义,如下xml定义的工具箱效果如下:
<xml id="toolbox" style="display: none">
<category name="核心">
<category name="控制">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
</category>
<category name="逻辑">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</category>
<category name="数学">
<block type="math_constant"></block>
<category name="算术">
<block type="math_arithmetic"></block>
<block type="math_number"></block>
</category>
<category name="三角">
<block type="math_single"></block>
<block type="math_trig"></block>
</category>
</category>
</xml>
设置分组颜色
工具箱分组类别还可以指定颜色,通过colour属性进行设定,colour值的范围是0~360,如下xml代码生成的工具箱效果如下:
<xml id="toolbox" style="display: none">
<category name="逻辑" colour="20">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
</category>
<category name="控制" colour="200">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</xml>
动态类别的代码块
blockly提供的工具箱中有两类有着特殊的行为,分别是变量和函数,它们的xml定义中拥有custom属性,分别为VARIABLE和PROCEDURE,代码及显示效果如下:
<xml id="toolbox" style="display: none">
<category name="变量" custom="VARIABLE"></category>
<category name="函数" custom="PROCEDURE"></category>
</xml>
其中变量分组中点击创建变量并按照提示输入变量名称后会生成如下三个代码块:
函数分组中包含三个代码块,前两个为方法定义,一个方法带有返回值,第三个为条件返回的代码块,通过这三个代码块我们可以编写新的方法来生成新的代码块,如下定义一个inc方法会生成一个新的代码块inc,该方法将输入的x变量增加1并返回,新生成的代码块又可以作为代码块进行使用:
带缺省值的代码块
有些代码块希望拥有缺省值,如下代码生成工具箱效果如下:
<xml id="toolbox" style="display: none">
<block type="logic_boolean"></block>
<block type="math_number">
<field name="NUM">42</field>
</block>
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
</xml>
其中:
- logic_boolean不需要事先设置,默认值为真(true);
- math_number通过field设置缺省值42;
- math_arithmetic使用field、value,加上shadow blocks实现了缺省值的设置。
五、blockly——生成代码
blockly通过拖动可视化的代码块来实现编程,普遍的应用场景是少儿编程,不过官方宣称blockly不是玩具,它可以实现复杂的编程任务;代码块只是提供了可视化的接口,最终执行的还是代码,blockly支持生成多种语言的代码,如JavaScript, Python, PHP, Lua, Dart,除此之外blockly拥有高度可定制化的特性,你还可以完全定制生成自己的所需的任何语言。本文以生成python语言为例进行演示,目前使用的内置的block。
定义工具箱
通过xml方式定义两个类别为文本和列表的工具箱,代码(index.html)及渲染的工具箱如下:
<xml xmlns="#34; id="toolbox" style="display: none">
<category name="文本" colour="%{BKY_TEXTS_HUE}">
<block type="text"></block>
<block type="text_join"></block>
<block type="text_append">
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_length">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_isEmpty">
<value name="VALUE">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{textVariable}</field>
</block>
</value>
<value name="FIND">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_charAt">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{textVariable}</field>
</block>
</value>
</block>
<block type="text_getSubstring">
<value name="STRING">
<block type="variables_get">
<field name="VAR">{textVariable}</field>
</block>
</value>
</block>
<block type="text_changeCase">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_trim">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_prompt_ext">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
</category>
<category name="列表" colour="%{BKY_LISTS_HUE}">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>
<block type="lists_create_with"></block>
<block type="lists_repeat">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_getIndex">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_setIndex">
<value name="LIST">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_getSublist">
<value name="LIST">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_split">
<value name="DELIM">
<shadow type="text">
<field name="TEXT">,</field>
</shadow>
</value>
</block>
<block type="lists_sort"></block>
</category>
</xml>
生成python
blockly/generators目录下的文件为生成的代码部分的代码,实际使用时引用
blockly/python_compressed.js提供的压缩版本即可
<script src="./blockly/blockly_compressed.js"></script> <!--blockly的核心代码-->
<script src="./blockly/blocks_compressed.js"></script> <!--代码块定义-->
<script src="./blockly/python_compressed.js"></script> <!--生成python代码-->
<script src="./blockly/msg/js/zh-hans.js"></script> <!--中文语言支持-->
生成python代码就是一条语句:
var code = Blockly.Python.workspaceToCode(workspace);
监听事件,实时刷新代码
blockly工作区生成代码的效率很高,为ms级,因此无需担心频繁更新代码导致性能问题,通常的做法是监听工具的change事件来实时生成代码,这里以在控制台输出为例:
function myUpdateFunction(event) {
var code = Blockly.Python.workspaceToCode(workspace); // 将工作区代码块生成代码
console.log(code); // 控制台显示生成的代码
}
workspace.addChangeListener(myUpdateFunction); // 监听工作区改变事件
浏览器打开index.html,向工作区中放置一些代码,控制台将会实时打印生成代码,如下是建立python列表的代码块及生成的代码:
点击紫色代码块中的蓝色按钮可以持续增加列表的项目,生成的代码也会相应的更新
index.html完整的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>blockly demo</title>
</head>
<body>
<div id="blocklyArea" style="min-height: 100vh;"></div>
<div id="blocklyDiv" style="position: absolute"></div>
<xml xmlns="#34; id="toolbox" style="display: none">
<category name="文本" colour="%{BKY_TEXTS_HUE}">
<block type="text"></block>
<block type="text_join"></block>
<block type="text_append">
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_length">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_isEmpty">
<value name="VALUE">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{textVariable}</field>
</block>
</value>
<value name="FIND">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_charAt">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{textVariable}</field>
</block>
</value>
</block>
<block type="text_getSubstring">
<value name="STRING">
<block type="variables_get">
<field name="VAR">{textVariable}</field>
</block>
</value>
</block>
<block type="text_changeCase">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_trim">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_prompt_ext">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
</category>
<category name="列表" colour="%{BKY_LISTS_HUE}">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>
<block type="lists_create_with"></block>
<block type="lists_repeat">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_getIndex">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_setIndex">
<value name="LIST">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_getSublist">
<value name="LIST">
<block type="variables_get">
<field name="VAR">{listVariable}</field>
</block>
</value>
</block>
<block type="lists_split">
<value name="DELIM">
<shadow type="text">
<field name="TEXT">,</field>
</shadow>
</value>
</block>
<block type="lists_sort"></block>
</category>
</xml>
<script src="./blockly/blockly_compressed.js"></script>
<script src="./blockly/blocks_compressed.js"></script>
<script src="./blockly/python_compressed.js"></script>
<script src="./blockly/msg/js/zh-hans.js"></script>
<script>
var blocklyArea = document.getElementById('blocklyArea');
var blocklyDiv = document.getElementById('blocklyDiv');
var workspace = Blockly.inject(blocklyDiv,
{
media: './blockly/media/',
toolbox: document.getElementById('toolbox'),
grid:
{spacing: 20,
length: 3,
colour: '#ccc',
snap: true},
zoom:
{controls: true,
wheel: true,
startScale: 1.0,
maxScale: 3,
minScale: 0.3,
scaleSpeed: 1.2,
pinch: true},
trashcan: true
});
var onresize = function(e) {
// 计算blocklyArea元素的绝对坐标和尺寸
var element = blocklyArea;
var x = 0;
var y = 0;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
} while (element);
// 将blocklyDiv定位到blocklyArea区域上
blocklyDiv.style.left = x + 'px';
blocklyDiv.style.top = y + 'px';
blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
Blockly.svgResize(workspace);
};
window.addEventListener('resize', onresize, false);
onresize();
Blockly.svgResize(workspace);
function myUpdateFunction(event) {
var code = Blockly.Python.workspaceToCode(workspace);
console.log(code);
}
workspace.addChangeListener(myUpdateFunction);
</script>
</body>
</html>