步骤运行代码块高亮

  • 路由
    • 历史回顾
      • Babylon.js部分
      • Blockly部分
  • 前言
  • 最终实现效果
  • 本文内容
  • 实现
    • 1. 安装依赖
    • 2. 简化语法
    • 3. 引入js解析器
    • 4. 运行代码
    • 5. 加入高亮
  • 完整代码
  • 本文章的用例代码已经同步到github上,运行程序后访问链接即可查看效果: [http://localhost:3000/#/Class/blockclass4](http://localhost:3000/#/Class/blockclass4)
  • 开源项目GitHub链接
  • 资源下载链接
  • 你的点赞是我继续编写的动力

路由

历史回顾

Babylon.js部分

  • Vue实现图形化积木式编程(一) ---- Babylon.js基础场景搭建
  • Vue实现图形化积木式编程(二) ---- Babylon.js加载模型到场景中
  • Vue实现图形化积木式编程(三) ---- Babylon.js点击拖拽移动模型
  • Vue实现图形化积木式编程(四) ---- Babylon.js实现碰撞效果
  • Vue实现图形化积木式编程(五) ---- Babylon.js自定义启动界面
  • Vue实现图形化积木式编程(六) ---- Babylon.js相机控制与相机动画
  • Vue实现图形化积木式编程(七) ---- babylonjs-gui 按钮实现
  • Vue实现图形化积木式编程(八) ---- 将3d界面放入可拖动窗口中

Blockly部分

  • Vue实现图形化积木式编程(九) ---- Blockly代码块编辑区域基本场景搭建
  • Vue实现图形化积木式编程(十) ---- Blockly自定义块
  • Vue实现图形化积木式编程(十一) ---- Blockly插件使用
  • Vue实现图形化积木式编程(十二) ---- 执行Blockly生成代码

前言

TIPS:该案例设计主要参考iRobot Coding,只用做学习用途,侵删。

https://code.irobot.com/#/

最终实现效果

本文内容

  • 步骤运行代码块高亮

实现

1. 安装依赖

  • 安装js解析器
npm install js-interpreter

2. 简化语法

在上一篇文章中提到了由于代码块是异步任务,而异步任务需要顺序执行,所以语法上需要构造成一个用async函数包裹的立即执行函数,虽然它的执行逻辑符合需求,但是对于没学过编程的同学第一眼看上去会觉得很乱,所以这里需要引入js-interpreter来简化生成代码语法。

(async ()=>{robot.init();await robot.move(50);await robot.arc(0, 90, 50);
robot.stop();
})()
  • 从原来的async代码块形式改为:
robot.init();robot.move(50);robot.arc(0, 90, 50);
robot.stop();
  • 相应的,生成代码的地方改成如下形式:
/*** 自定义组件生成代码* @param block* @returns {string}*/
Blockly.JavaScript['while_program_start'] = function (block) {let while_content = Blockly.JavaScript.statementToCode(block, 'while_content');while_content = while_content.slice(0, -1) // 去除最后一个\nconst code = `
robot.init();
${while_content}
robot.stop();
`return code;
};Blockly.JavaScript['move'] = function (block) {var text_move_distance = block.getFieldValue('move_distance');var code = `robot.move(${text_move_distance});\n`;return code;
};Blockly.JavaScript['turn'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var code = `robot.turn(${dropdown_dirction}, ${angle_degree});\n`;return code;
};Blockly.JavaScript['arc'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var radius = block.getFieldValue('radius');var code = `robot.arc(${dropdown_dirction}, ${angle_degree}, ${radius});\n`;return code;
};

3. 引入js解析器

  • 当然了,上面简化后的语句使用eval执行并不能实现串行顺序执行的效果,我们需要实例化一个JS Interpreter
  • JS Interpreter是一个与浏览器完全隔离的沙箱环境,任何函数或者变量都需要添加到解释器中。
  • 需要注意的是,interpreter.createNativeFunction接受的函数的最后一个参数callback(额外参数)必须调用才视为异步函数调用结束,具体见链接: JS-Interpreter文档,也可加下图
import Interpreter from 'js-interpreter'
// 将blockly工作区挂载到dom树上
let workspace = Blockly.inject(this.$refs.blocklyDiv, this.options)
// 这个Robot实例对象在上一篇文章有定义,其中包含最基本的init()、stop()、move(dir)、arc(direction, degree, distance)等方法
let robotController = new Robot()
// let code = BlocklyJS.workspaceToCode(this.$refs.blocklyDiv.workspace)
// 假设已经通过BlocklyJS.workspaceToCode获取到了blocky代码块生成的代码字符串
let code = `
robot.init();robot.move(50);robot.arc(0, 90, 50);
robot.stop();
` function runCode(code) {// 实例化js解析器// 在创建js解析器期间,会调用initApi方法创建解析器的全局变量let myInterpreter = new Interpreter(code, initApi);
}function initApi(interpreter, globalObject) {// 创建 'robot' 的全局对象var robot = interpreter.nativeToPseudo({});interpreter.setProperty(globalObject, 'robot', robot);// 定义 'robot.init' 的函数let iniWrapper = function init() {return robotController.init();};interpreter.setProperty(robot, 'init',interpreter.createNativeFunction(iniWrapper));// 定义 'robot.stop' 的函数var stopWrapper = function stop() {return robotController.stop();};interpreter.setProperty(robot, 'stop',interpreter.createNativeFunction(stopWrapper));// 定义 'robot.move' 的函数// interpreter.createAsyncFunction接受的函数最后一个参数为callback必须调用了才视为异步函数执行完成var moveWrapper = function move(distance, callback) {console.log('调用moveWrapper', that.robot,that.robot.move)robotController.move(distance).then(()=>{console.log('move完成了')callback(1)})};interpreter.setProperty(robot, 'move',interpreter.createAsyncFunction(moveWrapper));// 定义 'robot.arc' 的函数var arcWrapper = function arc(dir, degree, radius, callback) {robotController.arc(dir, degree, radius).then(()=>{console.log('arc完成了')callback()})};interpreter.setProperty(robot, 'arc',interpreter.createAsyncFunction(arcWrapper));}

4. 运行代码

  • 上一个操作中js-Interpreter已经将code解析成一系列可单步执行的代码块
  • 可通过myInterpreter.step()来单步执行代码,或者通过myInterpreter.run()一次性执行所有
function runStepByStep(myInterpreter){if (myInterpreter) {// run函数是一次性执行完所有的,单步执行可使用myInterpreter.step()var hasMore = myInterpreter.run();if (hasMore) {// 当前程序处于某个异步调用函数中,被阻塞了,设置延迟再调用。setTimeout(runStepByStep, 10, myInterpreter);} else {console.log('代码全部执行完了');}}}

5. 加入高亮

使用workspace.highlightBlock(id) 能高亮某个代码块
使用Blockly.JavaScript.STATEMENT_SUFFIX 可再每个语句前插入highlightBlock函数- 具体见: STATEMENT_PREFIX解释

  • 在创建worksapce之前设置在每个语句之前出入高亮块
function initHighlightBlock() {// 可以在生成JavaScript代码之前通过设置STATEMENT_PREFIX在逐条语句级别上完成此操作Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';// 将highlightBlock添加为保留字Blockly.JavaScript.addReservedWords('highlightBlock');}
  • 在原来initApi函数基础上再加一个
      // 创建 'highlightBlock' 的函数var hightlightWrapper = function(id) {// console.log("highlightBlock")id = String(id || '');return workspace.highlightBlock(id);};interpreter.setProperty(globalObject, 'highlightBlock',interpreter.createNativeFunction(hightlightWrapper));
  • 在原来runStepByStep函数基础上,调用结束后设置workspace.highlightBlock(null)
function runStepByStep(myInterpreter){if (myInterpreter) {// run函数是一次性执行完所有的,单步执行可使用myInterpreter.step()var hasMore = myInterpreter.run();if (hasMore) {// 当前程序处于某个异步调用函数中,被阻塞了,设置延迟再调用。setTimeout(runStepByStep, 10, myInterpreter);} else {console.log('代码全部执行完了');workspace.highlightBlock(null)}}}

完整代码

  • 测试用例
<template><div id="blockly"><!-- 工作区 --><div id="blocklyDiv" ref="blocklyDiv" style="height: 500px; width: 800px;"></div><button style="position: fixed;left: 50px;top: 10px;" @click="block2code">生成代码</button><!-- 代码显示区 --><div style="background-color: lightgrey;width: 800px;text-align: left"><pre v-html="code?code:'请点击生成代码按钮'"></pre></div><button style="position: fixed;left: 150px;top: 10px;" @click="runCode">eval执行代码</button><button style="position: fixed;left: 300px;top: 10px;" @click="runCode2">new Function执行代码</button><button style="position: fixed;left: 500px;top: 10px;" @click="runCode3">js-interpreter执行代码</button></div>
</template><script>
import Blockly from 'blockly'
import BlocklyJS from 'blockly/javascript';
import './customBlock'
import Robot from './robot'
import Interpreter from "js-interpreter";
export default {name: "blocklyClass4",data() {return {code: '',options: {horizontalLayout: true,//工具箱水平toolboxPosition: "end",//工具箱在底部toolbox: {"kind": "flyoutToolbox","contents": [{"kind": "block","type": "while_program_start",},{"kind": "block","type": "move",},{"kind": "block","type": "turn",},{"kind": "block","type": "arc"},{"kind": "block","type": "draw"},{"kind": "block","type": "pencilcolor"},{"kind": "block","type": "controls_repeat_ext"},{"kind": "block","type": "controls_whileUntil"},{"kind": "block","type": "controls_for"},{"kind": "block","type": "controls_if"},{"kind": "block","type": "logic_compare"},{"kind": "block","type": "logic_operation"},{"kind": "block","type": "logic_negate"},{"kind": "block","type": "logic_boolean"},{"kind": "sep","gap": "32"},{"kind": "block","blockxml": "<block type='math_number'><field name='NUM'>10</field></block>"},{"kind": "block","type": "math_arithmetic"},{"kind": "block","type": "math_single"},{"kind": "block","type": "text"},{"kind": "block","type": "text_length"},{"kind": "block","type": "text_print"},{"kind": "block","type": "variables_get"},{"kind": "block","type": "variables_set"},]}},workspace: null}},mounted() {this.initHighlightBlock()this.workspace = Blockly.inject(this.$refs.blocklyDiv, this.options);this.robot = new Robot()},methods: {/*** block代码块转为代码*/block2code() {this.code = BlocklyJS.workspaceToCode(this.$refs.blocklyDiv.workspace)},/*** 执行生成代码*/runCode() {if (!this.code) {alert('请先点击生成代码');return}window.robot = this.roboteval(this.code)},runCode2() {if (!this.code) {alert('请先点击生成代码');return}let fn = new Function('robot', this.code)fn(this.robot)},runCode3() {if (!this.code) {alert('请先点击生成代码');return}// 实例化js解析器// 在创建js解析器期间,会调用initApi方法创建解析器的全局变量let myInterpreter = new Interpreter(this.code, this.initApi);console.log('myInterpreter', myInterpreter)// this.initJsInterpreter(this.code)this.runStepByStep(myInterpreter)},runStepByStep(myInterpreter){if (myInterpreter) {var hasMore = myInterpreter.run();if (hasMore) {// 执行当前被某个异步调用阻止。//请稍后再试。setTimeout(this.runStepByStep, 10, myInterpreter);} else {this.highlightBlock(null);console.log('代码全部执行完了');}}},initHighlightBlock() {// 可以在生成JavaScript代码之前通过设置STATEMENT_PREFIX在逐条语句级别上完成此操作Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';// 将highlightBlock添加为保留字Blockly.JavaScript.addReservedWords('highlightBlock');},highlightBlock(id) {this.workspace.highlightBlock(id);},initApi(interpreter, globalObject) {let that = this// 创建 'highlightBlock' 的函数var hightlightWrapper = function(id) {// console.log("highlightBlock")id = String(id || '');return that.highlightBlock(id);};interpreter.setProperty(globalObject, 'highlightBlock',interpreter.createNativeFunction(hightlightWrapper));// 创建 'robot' 的全局对象var robot = interpreter.nativeToPseudo({});interpreter.setProperty(globalObject, 'robot', robot);// 定义 'robot.init' 的函数let iniWrapper = function init() {return that.robot.init();};interpreter.setProperty(robot, 'init',interpreter.createNativeFunction(iniWrapper));// 定义 'robot.stop' 的函数var stopWrapper = function stop() {return that.robot.stop();};interpreter.setProperty(robot, 'stop',interpreter.createNativeFunction(stopWrapper));// 定义 'robot.move' 的函数// interpreter.createAsyncFunction接受的函数最后一个参数为callback必须调用了才视为异步函数执行完成var moveWrapper = function move(distance, callback) {that.robot.move(distance).then(()=>{console.log('move完成了')callback(1)})};interpreter.setProperty(robot, 'move',interpreter.createAsyncFunction(moveWrapper));// 定义 'robot.arc' 的函数var arcWrapper = function arc(dir, degree, radius, callback) {that.robot.arc(dir, degree, radius).then(()=>{console.log('arc完成了')callback()})};interpreter.setProperty(robot, 'arc',interpreter.createAsyncFunction(arcWrapper));}}
}
</script><style scoped>
#blockly {position: absolute;left: 50px;top: 50px;bottom: 0;width: calc(100vw - 50px);height: calc(100vh - 50px);display: flex;flex-direction: column;
}
</style>
  • Blockly自定义组件
import * as Blockly from 'blockly/core'import * as hans from 'blockly/msg/zh-hans'Blockly.setLocale(hans);//汉化/*** 自定义组件注册*/
Blockly.defineBlocksWithJsonArray([//事件{"type": "while_program_start","message0": "当程序运行 %1 %2","args0": [{"type": "input_dummy"},{"type": "input_statement","name": "while_content"}],"previousStatement": null,"nextStatement": null,"colour": "#609FD6","strokeColour": "#4088C8","tooltip": "123","helpUrl": "1"},//指令{"type": "move","message0": "移动 %1 CM","args0": [{"type": "field_input","name": "move_distance","text": "50"}],"previousStatement": null,"nextStatement": null,"colour": "#F7D233","strokeColour": "#CCAD2B","tooltip": "","helpUrl": ""},{"type": "turn","message0": "向 %1 %2","args0": [{"type": "field_dropdown","name": "dirction","options": [["左转","0"],["右转","1"]]},{"type": "field_angle","name": "degree","angle": 90}],"previousStatement": null,"nextStatement": null,"colour": "#F7D233","strokeColour": "#CCAD2B","tooltip": "","helpUrl": ""},{"type": "arc","message0": "弧形 %1 %2 ,半径 %3 CM","args0": [{"type": "field_dropdown","name": "dirction","options": [["向左","0"],["向右","1"]]},{"type": "field_angle","name": "degree","angle": 90},{"type": "field_number","name": "radius","value": 50,"min": 1,"max": 100}],"previousStatement": null,"nextStatement": null,"colour": "#F7D233","strokeColour": "#CCAD2B","tooltip": "","helpUrl": ""},{"type": "draw","message0": "设置 %1","args0": [{"type": "field_dropdown","name": "pencilState","options": [[{"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjEtMDgtMjNUMTE6Mzk6NTQrMDg6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTA4LTIzVDExOjUxOjAxKzA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIxLTA4LTIzVDExOjUxOjAxKzA4OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmVkZGRkMGNlLTU4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","width": 50,"height": 50,"alt": "pencil down"},"1"],[{"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjEtMDgtMjNUMTE6NDA6MTErMDg6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTA4LTIzVDExOjUyOjA2KzA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIxLTA4LTIzVDExOjUyOjA2KzA4OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU3Y2UzN2ViLTQ0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","width": 50,"height": 50,"alt": "pencil up"},"0"]]}],"previousStatement": null,"nextStatement": null,"colour": "#81C679","tooltip": "","helpUrl": ""},{"type": "pencilcolor","message0": "设置笔颜色: 红 %1 绿 %2 蓝 %3","args0": [{"type": "field_number","name": "red","value": 100,"min": 0,"max": 255},{"type": "field_number","name": "green","value": 100,"min": 0,"max": 255},{"type": "field_number","name": "blue","value": 100,"min": 0,"max": 255}],"previousStatement": null,"nextStatement": null,"colour": "#81C679","tooltip": "","helpUrl": ""}]
);/*** 自定义组件生成代码* @param block* @returns {string}*/
Blockly.JavaScript['while_program_start'] = function (block) {let while_content = Blockly.JavaScript.statementToCode(block, 'while_content');while_content = while_content.slice(0, -1) // 去除最后一个\nconst code =
`robot.init();
${while_content}
robot.stop();
`return code;
};Blockly.JavaScript['move'] = function (block) {var text_move_distance = block.getFieldValue('move_distance');var code = `robot.move(${text_move_distance});\n`;return code;
};Blockly.JavaScript['turn'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var code = `robot.turn(${dropdown_dirction}, ${angle_degree});\n`;return code;
};Blockly.JavaScript['arc'] = function (block) {var dropdown_dirction = block.getFieldValue('dirction');var angle_degree = block.getFieldValue('degree');var radius = block.getFieldValue('radius');var code = `robot.arc(${dropdown_dirction}, ${angle_degree}, ${radius});\n`;return code;
};Blockly.JavaScript['draw'] = function (block) {var dropdown_pencilstate = block.getFieldValue('pencilState');var code = `robot.drawable(${dropdown_pencilstate});\n`;return code;
};Blockly.JavaScript['pencilcolor'] = function (block) {var number_red = block.getFieldValue('red') / 255.0;var number_green = block.getFieldValue('green') / 255.0;var number_blue = block.getFieldValue('blue') / 255.0;var code = `robot.pencilcolor(${number_red}, ${number_green}, ${number_blue});\n`;return code;
};
  • Robot控制类
// eslint-disable-next-line no-unused-vars
class Robot {constructor() {this.isRun = false}init() {console.log("robot模块化程序初始化")this.isRun = true}stop() {console.log("robot模块运行结束")this.isRun = false}checkStatus() {if (!this.isRun) {throw '程序需要初始化模块'}}async move(distance) {this.checkStatus()// 模拟小车运动return new Promise(resolve => {let moveDis = 0let interval = setInterval(() => {if (moveDis < distance) {console.log(`move ${moveDis++}`)} else {clearInterval(interval)interval = undefinedresolve()}}, 100)})}async arc(direction, degree, distance) {this.checkStatus()// 模拟小车运动return new Promise(resolve => {let moveDis = 0let interval = setInterval(() => {if (moveDis < distance) {console.log(`direction: ${direction}, move:${moveDis++}, degree: ${degree}`)} else {clearInterval(interval)interval = undefinedresolve()}}, 100)})}
}
export default Robot

本文章的用例代码已经同步到github上,运行程序后访问链接即可查看效果: http://localhost:3000/#/Class/blockclass4

开源项目GitHub链接

https://github.com/Wenbile/Child-Programming-Web

资源下载链接

  • Vue前端源码
  • ThinkJS后端源码

你的点赞是我继续编写的动力

Vue实现图形化积木式编程(十三)相关推荐

  1. Vue实现图形化积木式编程(十二)

    执行Blockly生成代码 路由 下一篇 历史回顾 Babylon.js部分 Blockly部分 前言 最终实现效果 本文内容 实现思路 问题分析 问题 原因 不优雅解决 优雅解决 完整代码 后续计划 ...

  2. Vue实现图形化积木式编程(十)

    Blockly自定义块 路由 下一篇 历史回顾 Babylon.js部分 Blockly部分 前言 最终实现效果 本文实现效果 完整代码 代码分解 0.代码块前置知识 0.1 汉化 0.2 预置块 0 ...

  3. Vue实现图形化积木式编程(十一)

    Blockly插件使用 路由 下一篇 历史回顾 Babylon.js部分 Blockly部分 前言 最终实现效果 本文内容 安装 使用 后续计划 开源项目GitHub链接 资源下载链接 你的点赞是我继 ...

  4. Vue实现图形化积木式编程(一)

    Babylon.js基础场景搭建 路由 前言 最终实现效果 本文实现效果 技术选型 1.前端 2.后端 完整代码 代码分解 0.npm安装相关依赖 1.引入模块 2.场景初始化 3.ArcRotate ...

  5. Vue实现图形化积木式编程(二)

    Babylon.js加载模型到场景中 路由 下一篇 历史回顾 前言 最终实现效果 本文实现效果 完整代码 操作分解(Babylon.js模型格式转换与导入) 0.在开源模型网上下载一个模型/自己制作一 ...

  6. 原创教程:下载和安装“图形化积木Python编程”海龟编辑器

    一.简介 Python编辑器是一款界面简单充满童趣的Python编程软件,该软件普遍适用于低龄化用户,帮助培养用户对代码编程的兴趣:众所周知编写代码是比较枯燥的事情,所以学起来更是如此,为解决这一难题 ...

  7. python积木式编程_TurnipBit—MicroPython开发板:从积木式编程语言开始学作小小创客...

    编程.建模.制做动画和游戏--这些当初咱们默认只有成年人玩得转的事情,如今早已经被无数小孩子给颠覆甚至玩出新境界了.热爱科技和动手的"创客"(Maker)如今在全世界都煊赫一时.今 ...

  8. 竞赛无人机搭积木式编程——以2022年TI电赛送货无人机一等奖复现为例学习(7月B题)

    在学习本教程前,请确保已经学习了前4讲中无人机相关坐标系知识.基础飞行控制函数.激光雷达SLAM定位条件下的室内定点控制.自动飞行支持函数.导航控制函数等入门阶段的先导教程. 同时用户在做二次开发自定 ...

  9. python积木式编程_【发现教育版亮点之美】3D One还能这么玩:“趣味编程”建模让你脑洞大开...

    原标题:[发现教育版亮点之美]3D One还能这么玩:"趣味编程"建模让你脑洞大开 "[有奖征文]发现3D One教育版亮点之美"教育版功能文章征集活动已经告一 ...

最新文章

  1. python软件安装及设置_入门Python——1.软件安装与基础语法
  2. node.js搭建简单服务器,用于前端测试websocket链接方法和性能测试
  3. request获取各种路径总结、页面跳转总结。
  4. IT人不要一直做技术(转--我也不知道转了几圈了)
  5. 车牌识别的matlab程序(程序_讲解_模板),车牌识别的matlab程序(程序讲解模板)
  6. 微服务的好处与弊端_一文了解微服务的流程和组织
  7. 使用Apache的ab工具进行压力测试
  8. 基于php重庆旅游设计网站作品
  9. 集宁哪有计算机培训班,集宁区有这么一个空间,叫共享自习室
  10. grid设置时间格式
  11. 操作系统基础知识详解
  12. 每年考证时间表(绝对有用)
  13. 努比亚 N2(Nubia NX575J) 解锁BootLoader 并进入临时recovery ROOT
  14. Pr剪辑上分攻略 -- 动漫电影混剪
  15. c++实现植物大战僵尸修改器
  16. face_recognition库的安装以及学习
  17. java 实心圆,如何用css3实现实心圆
  18. uniapp跳转外部链接
  19. [翻译]Reactor Netty参考指南 - 8.UDP客户端
  20. 服务器固态硬盘无法识别硬盘,固态硬盘无法识别的原因及解决方法

热门文章

  1. 小程序源码:修复图片音频全新升级带特效喝酒神器小游戏微信小程序
  2. 信息系统分析与设计 机票预定管理系统
  3. Android 直播中弹幕、带货和键盘的交互
  4. 【数据库课设】机票预订系统 java+mysql实现 附源码
  5. 【华人学者风采】魏少军 清华大学
  6. Linux 环境下配置远程jupyter notebook server
  7. 一键智能抠图-原理与实现
  8. 登录页面,登录后跳转不成功
  9. 普通人有没有必要学python,什么样的人适合学python
  10. HTML5基础之代码入门