美文网首页
Vue实现图形化积木式编程(九)

Vue实现图形化积木式编程(九)

作者: 温温温B | 来源:发表于2021-10-10 17:07 被阅读0次

    前言

    前段时间想要做一个web端的图形化积木式编程(类似少儿编程)的案例,网上冲浪了一圈又一圈,终于技术选型好,然后代码一顿敲,终于出来了一个雏形。

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

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

    最终实现效果

    最终实现效果

    本文实现效果

    blockly代码块编辑区域基本场景搭建

    完整代码

    • Blockly代码块编辑区域基本场景搭建
    <template>
      <div id="blockly">
        <!-- 工作区 -->
        <div id="blocklyDiv" ref="blocklyDiv" style="height: 500px; width: 700px;"></div>
        <button style="position: fixed;left: 50px;top: 10px;" @click="block2code">生成代码</button>
        <!-- 代码显示区 -->
        <div style="background-color: lightgrey;width: 700px;text-align: left">
          {{code?code:'请点击生成代码按钮'}}
        </div>
        <button style="position: fixed;left: 150px;top: 10px;" @click="runCode">执行代码</button>
      </div>
    </template>
    
    <script>
    import Blockly from 'blockly'
    import BlocklyJS from 'blockly/javascript';
    
    export default {
      name: "blocklyClass1",
      data() {
        return {
          code:'',
          options: {
            toolbox: {
              "kind": "flyoutToolbox",
    
              "contents": [
                {
                  "kind": "block",
                  "type": "controls_if"
                },
                {
                  "kind": "block",
                  "type": "controls_repeat_ext"
                },
                {
                  "kind": "block",
                  "type": "logic_compare"
                }
                ,
                {
                  "kind": "block",
                  "type": "math_number"
                }
                ,
                {
                  "kind": "block",
                  "type": "math_arithmetic"
                }
                ,
                {
                  "kind": "block",
                  "type": "text"
                }
                ,
                {
                  "kind": "block",
                  "type": "text_print"
                }
              ]
            }
          }
        }
      },
      mounted() {
        Blockly.inject(this.$refs.blocklyDiv, this.options);
      },
      methods:{
        /**
         * block代码块转为代码
         */
        block2code(){
          this.code = BlocklyJS.workspaceToCode(this.$refs.blocklyDiv.workspace)
        },
    
        /**
         * 执行生成代码
         */
        runCode(){
            if(!this.code){alert('请先点击生成代码');return}
            eval(this.code)
        },
      },
    }
    </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>
    
    
    

    代码分解

    0.项目初始化

    • 利用脚手架搭建vue项目
    npx @vue/cli create block-class
    
    • 脚手架中选择Vue2、npm 脚手架中选择Vue2、npm
    • 启动工程
    npm run serve
    
    • 安装Block依赖
    npm install --save blockly
    
    • 引入模块
    import Blockly from 'blockly';
    
    • 配置白名单!

    不配置白名单,解析器在解析到xml、block等标签的时候会报错,而其实我们并不希望解析器解析这些block定义的工具栏标签

    //main.js - blocky配置
    Vue.config.ignoredElements = ['field','block','category','xml','mutation','value','sep']
    

    1.Blockly基本场景搭建

    • blocklyDiv为整个block工作区,个人习惯将工具栏写在工作区里面(不一定嵌套在工作区,Block是通过将获取到的这个工具栏xml元素注入到工作区来实现的)
    <template>
      <div id="blockly">
        <!-- 工作区 -->
        <div id="blocklyDiv" ref="blocklyDiv" style="height: 500px; width: 700px;">
          <!-- 工具栏 -->
          <xml id="toolbox" ref="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>
        </div>
      </div>
    </template>
    
    • 引入blockly库,通过Blockly.inject(‘在DOM中注入工作区的区域’,'注入选项字典')

    注入的选项有很多,具体查看:Blockly注入选项

    <script>
    import Blockly from 'blockly'
    export default {
      name: "blocklyClass1",
      mounted() {
        //目前只配置了工具栏
        let options = {
          toolbox: this.$refs.toolbox
        }
        //将工作区注入到blocklyDiv的元素上,并将配置的选项注入
        Blockly.inject(this.$refs.blocklyDiv,options);
      }
    }
    </script>
    

    2.工具栏工具列表转化为JSON格式

    • 为了让工具栏列表更具灵活性,把它转为JSON格式是首选
          <!-- 工具栏 -->
          <xml id="toolbox" ref="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>
    
    • 转为JSON格式
    {
              "kind": "flyoutToolbox",
              "contents": [
                {
                  "kind": "block",
                  "type": "controls_if"
                },
                {
                  "kind": "block",
                  "type": "controls_repeat_ext"
                },
                {
                  "kind": "block",
                  "type": "logic_compare"
                }
                ,
                {
                  "kind": "block",
                  "type": "math_number"
                }
                ,
                {
                  "kind": "block",
                  "type": "math_arithmetic"
                }
                ,
                {
                  "kind": "block",
                  "type": "text"
                }
                ,
                {
                  "kind": "block",
                  "type": "text_print"
                }
              ]
            }
    
    • 将template中的xml元素删除掉,在data中加入options的选择,里面加入一个key为toolbox的工具栏列表
    export default {
      name: "blocklyClass1",
      data() {
        return {
          options: {
            toolbox: {
              "kind": "flyoutToolbox",
              "contents": [
                {
                  "kind": "block",
                  "type": "controls_if"
                },
                {
                  "kind": "block",
                  "type": "controls_repeat_ext"
                },
                {
                  "kind": "block",
                  "type": "logic_compare"
                }
                ,
                {
                  "kind": "block",
                  "type": "math_number"
                }
                ,
                {
                  "kind": "block",
                  "type": "math_arithmetic"
                }
                ,
                {
                  "kind": "block",
                  "type": "text"
                }
                ,
                {
                  "kind": "block",
                  "type": "text_print"
                }
              ]
            }
          }
        }
      },
      mounted() {
        //选项中的toolbox改为了配置的字典列表
        Blockly.inject(this.$refs.blocklyDiv, this.options);
      }
    }
    </script>
    
    • 当然了,除了这中直列的工具栏以为,还可以配置带分类的工具栏:

    • 直列式工具栏


      直列式工具栏
    • 带分类工具栏


      在这里插入图片描述
    • 带分类工具栏配置如下(kind设置为category,然后再嵌套一层contents的列表):

    {
      "kind": "categoryToolbox",
      "contents": [
        {
          "kind": "category",
          "name": "Control",
          "contents": [
            {
              "kind": "block",
              "type": "controls_if"
            },
            {
              "kind": "block",
              "type": "controls_whileUntil"
            },
            {
              "kind": "block",
              "type": "controls_for"
            }
          ]
        },
        {
          "kind": "category",
          "name": "Logic",
          "contents": [
            {
              "kind": "block",
              "type": "logic_compare"
            },
            {
              "kind": "block",
              "type": "logic_operation"
            },
            {
              "kind": "block",
              "type": "logic_boolean"
            }
          ]
        }
      ]
    }
    

    3.生成代码

    • 将代码块拖动到工作区之后,就应该生成对应代码了
    • 我们需要生成的是javascript的代码,所以引入'blockly/javascript'的库
    • Block还支持生成的代码有Python,PHP,Lua,或Dart,按需引入相关库即可
    import BlocklyJS from 'blockly/javascript';
    
    • 定义一个生成代码按钮和一个显示生成代码的区域
        <button style="position: fixed;left: 50px;top: 10px;" @click="block2code">生成代码</button>
        <!-- 代码显示区 -->
        <div style="background-color: lightgrey;width: 700px;text-align: left">
          {{code?code:'请点击生成代码按钮'}}
        </div>
    
    • 生成代码
    //将工作区作为参数传入,返回的是该工作区中的blocks组合块形成的代码
    this.code = BlocklyJS.workspaceToCode(this.$refs.blocklyDiv.workspace)
    

    4.执行代码

    • 用简单粗暴eval
    • 当然啦,eval执行将字符串转化为可执行的代码存在安全、执行函数的作用域、无法监控执行过程、无法断点和单步执行等问题,在以后的篇章中会优化
    eval(this.code)
    

    后续计划

    Blockly

    • 自定义block块
    • blockly第三方组件使用
    • 接入js-interpreter,步骤运行block块
    • ......(想到啥写啥)

    开源项目GitHub链接

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

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

    相关文章

      网友评论

          本文标题:Vue实现图形化积木式编程(九)

          本文链接:https://www.haomeiwen.com/subject/dviholtx.html