谷歌开源项目blockly的使用----采坑之路

4,967 阅读15分钟

blockly是什么

少啰嗦,先看东西

blockly能做什么:通过拖动块产生代码;生产的代码可以是JS,Python,PHP,Lua,Dart,以下亲测两种。

下图为生成的 Python 和 JS 代码

个人使用心得:

优势:强大的blockly,可以几乎可以生成任何公式,甚至非常复杂的公式或者规则;

缺点:对于程序员简单,对非程序员的使用者学习成本有点高,甚至要求有点程序基础,例如让一个HR去用这个东西开始可能抵触心理很大;

安装

npm install blockly
注意:如果安装失败,请删除node_modules后再重装

少啰嗦,先看demo

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Button } from 'antd';
import Blockly from 'blockly';
import * as Ch from 'blockly/msg/zh-hans';
// import * as Blockly_core from 'blockly/core'; zh-hans.js
import 'blockly/blocks';
import 'blockly/python';

import XmlBlockly from './blockly_xml';

export default () => {
  const blockId = useRef('blocklyDiv');// 薪酬模块

  useEffect(()=>{
    Blockly.setLocale(Ch); // 支持中文
    if(blockId.current && document.getElementById(blockId.current)){
      const workspace =  Blockly.inject('blocklyDiv',{toolbox: document.getElementById('toolbox')});
      function myUpdateFunction(event) {
        const code_js = Blockly.JavaScript.workspaceToCode(workspace);
        console.log('code_js: ', code_js);
        // const code = Blockly.Xml.workspaceToDom(workspace);
        // const code_python = Blockly.Python.workspaceToCode(workspace);
        // console.log('code_python: ', code_python);
        const xml = Blockly.Xml.workspaceToDom(workspace);
        const xml_text = Blockly.Xml.domToText(xml);
        window.localStorage.setItem('xml_text',xml_text);
      }
      workspace.addChangeListener(myUpdateFunction);

      const xml_text = window.localStorage.getItem('xml_text');
      const xml = Blockly.Xml.textToDom(xml_text);
      Blockly.Xml.domToWorkspace(xml, workspace);
    }
  },[blockId.current])

  
  return (
    <>
      <div id={blockId.current} style={{height: '715px', width: '100%', overflow: 'auto'}}></div>
      <XmlBlockly />
      <div className={styles.drag} draggable>draggable</div>
    </>
  )
}

blockly_xml.js


import React from 'react';

function Xml_Blockly(){
  return(
    <xml id="toolbox" style={{display:'none'}}>
    <category name="逻辑" colour="#5C81A6">
      <block type="controls_if"></block>
      <block type="logic_compare">
        <field name="OP">EQ</field>
      </block>
      <block type="logic_operation">
        <field name="OP">AND</field>
      </block>
      <block type="logic_negate"></block>
      <block type="logic_boolean">
        <field name="BOOL">TRUE</field>
      </block>
      <block type="logic_null"></block>
      <block type="logic_ternary"></block>
    </category>
    <category name="循环" colour="#5CA65C">
      <block type="controls_repeat_ext">
        <value name="TIMES">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
      </block>
      <block type="controls_whileUntil">
        <field name="MODE">WHILE</field>
      </block>
      <block type="controls_for">
        <field name="VAR" id="cV8(8gw)4+2F=@{-oa%U" variabletype="">i</field>
        <value name="FROM">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="TO">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
        <value name="BY">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
      </block>
      <block type="controls_forEach">
        <field name="VAR" id="49TAe6j@7~K]V3lvrC!i" variabletype="">j</field>
      </block>
      <block type="controls_flow_statements">
        <field name="FLOW">BREAK</field>
      </block>
    </category>
    <category name="数学运算" colour="#5C68A6">
      <block type="math_round">
        <field name="OP">ROUND</field>
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">3.1</field>
          </shadow>
        </value>
      </block>
      <block type="math_number">
        <field name="NUM">0</field>
      </block>
      <block type="math_single">
        <field name="OP">ROOT</field>
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">9</field>
          </shadow>
        </value>
      </block>
      <block type="math_trig">
        <field name="OP">SIN</field>
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">45</field>
          </shadow>
        </value>
      </block>
      <block type="math_constant">
        <field name="CONSTANT">PI</field>
      </block>
      <block type="math_number_property">
        <mutation divisor_input="false"></mutation>
        <field name="PROPERTY">EVEN</field>
        <value name="NUMBER_TO_CHECK">
          <shadow type="math_number">
            <field name="NUM">0</field>
          </shadow>
        </value>
      </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>
      <block type="math_on_list">
        <mutation op="SUM"></mutation>
        <field name="OP">SUM</field>
      </block>
      <block type="math_modulo">
        <value name="DIVIDEND">
          <shadow type="math_number">
            <field name="NUM">64</field>
          </shadow>
        </value>
        <value name="DIVISOR">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
      </block>
      <block type="math_constrain">
        <value name="VALUE">
          <shadow type="math_number">
            <field name="NUM">50</field>
          </shadow>
        </value>
        <value name="LOW">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="HIGH">
          <shadow type="math_number">
            <field name="NUM">100</field>
          </shadow>
        </value>
      </block>
      <block type="math_random_int">
        <value name="FROM">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="TO">
          <shadow type="math_number">
            <field name="NUM">100</field>
          </shadow>
        </value>
      </block>
      <block type="math_random_float"></block>
    </category>
    <category name="文本" colour="#5CA68D">
      <block type="text_charAt">
        <mutation at="true"></mutation>
        <field name="WHERE">FROM_START</field>
        <value name="VALUE">
          <block type="variables_get">
            <field name="VAR" id="B0!JU8*uGn](FPPot0b8" variabletype="">text</field>
          </block>
        </value>
      </block>
      <block type="text">
        <field name="TEXT"></field>
      </block>
      <block type="text_append">
        <field name="VAR" id="-*Z%h!C8]8?:CrB^l[Sb" variabletype="">item</field>
        <value name="TEXT">
          <shadow type="text">
            <field name="TEXT"></field>
          </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">
        <field name="END">FIRST</field>
        <value name="VALUE">
          <block type="variables_get">
            <field name="VAR" id="B0!JU8*uGn](FPPot0b8" variabletype="">text</field>
          </block>
        </value>
        <value name="FIND">
          <shadow type="text">
            <field name="TEXT">abc</field>
          </shadow>
        </value>
      </block>
      <block type="text_join">
        <mutation items="2"></mutation>
      </block>
      <block type="text_getSubstring">
      <mutation at1="true" at2="true"></mutation>
      <field name="WHERE1">FROM_START</field>
      <field name="WHERE2">FROM_START</field>
      <value name="STRING">
        <block type="variables_get">
          <field name="VAR" id="B0!JU8*uGn](FPPot0b8" variabletype="">text</field>
        </block>
      </value>
    </block>
      <block type="text_changeCase">
        <field name="CASE">UPPERCASE</field>
        <value name="TEXT">
          <shadow type="text">
            <field name="TEXT">abc</field>
          </shadow>
        </value>
      </block>
      <block type="text_trim">
        <field name="MODE">BOTH</field>
        <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">
        <mutation type="TEXT"></mutation>
        <field name="TYPE">TEXT</field>
        <value name="TEXT">
          <shadow type="text">
            <field name="TEXT">abc</field>
          </shadow>
        </value>
      </block>
    </category>
    <category name="数组" colour="#745CA6">
    <block type="lists_indexOf">
      <field name="END">FIRST</field>
      <value name="VALUE">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_create_with">
      <mutation items="0"></mutation>
    </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_create_with">
      <mutation items="3"></mutation>
    </block>
    <block type="lists_getIndex">
      <mutation statement="false" at="true"></mutation>
      <field name="MODE">GET</field>
      <field name="WHERE">FROM_START</field>
      <value name="VALUE">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_setIndex">
      <mutation at="true"></mutation>
      <field name="MODE">SET</field>
      <field name="WHERE">FROM_START</field>
      <value name="LIST">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_getSublist">
      <mutation at1="true" at2="true"></mutation>
      <field name="WHERE1">FROM_START</field>
      <field name="WHERE2">FROM_START</field>
      <value name="LIST">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_split">
      <mutation mode="SPLIT"></mutation>
      <field name="MODE">SPLIT</field>
      <value name="DELIM">
        <shadow type="text">
          <field name="TEXT">,</field>
        </shadow>
      </value>
    </block>
    <block type="lists_sort">
      <field name="TYPE">NUMERIC</field>
      <field name="DIRECTION">1</field>
    </block>
    </category>
    <category name="颜色" colour="#A6745C">
  <block type="colour_picker">
    <field name="COLOUR">#ff0000</field>
  </block>
  <block type="colour_random"></block>
  <block type="colour_rgb">
    <value name="RED">
      <shadow type="math_number">
        <field name="NUM">100</field>
      </shadow>
    </value>
    <value name="GREEN">
      <shadow type="math_number">
        <field name="NUM">50</field>
      </shadow>
    </value>
    <value name="BLUE">
      <shadow type="math_number">
        <field name="NUM">0</field>
      </shadow>
    </value>
  </block>
  <block type="colour_blend">
  <value name="COLOUR1">
    <shadow type="colour_picker">
      <field name="COLOUR">#ff0000</field>
    </shadow>
  </value>
  <value name="COLOUR2">
    <shadow type="colour_picker">
      <field name="COLOUR">#3333ff</field>
    </shadow>
  </value>
  <value name="RATIO">
    <shadow type="math_number">
      <field name="NUM">0.5</field>
    </shadow>
  </value>
</block>
</category>
    <category name="变量" colour="#A65C81" custom="VARIABLE"></category>
    <category name="函数" colour="#9A5CA6" custom="PROCEDURE"></category>
  </xml>)
}
export default Xml_Blockly;

使用(只针对react的使用,其他先不介绍)

import Blockly from 'blockly'; // 引用
 
render(
    return(
        <>
         <div id={blockId.current} style={{height: '715px', width: '100%', overflow: 'auto'}}></div>
         
        <xml id="toolbox" style={{display:'none'}}>
            <category name="逻辑" colour="#5C81A6">
      <block type="controls_if"></block>
      <block type="logic_compare">
        <field name="OP">EQ</field>
      </block>
      <block type="logic_operation">
        <field name="OP">AND</field>
      </block>
      <block type="logic_negate"></block>
      <block type="logic_boolean">
        <field name="BOOL">TRUE</field>
      </block>
      <block type="logic_null"></block>
      <block type="logic_ternary"></block>
    </category>
    <category name="循环" colour="#5CA65C">
      <block type="controls_repeat_ext">
        <value name="TIMES">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
      </block>
      <block type="controls_whileUntil">
        <field name="MODE">WHILE</field>
      </block>
      <block type="controls_for">
        <field name="VAR" id="cV8(8gw)4+2F=@{-oa%U" variabletype="">i</field>
        <value name="FROM">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="TO">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
        <value name="BY">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
      </block>
      <block type="controls_forEach">
        <field name="VAR" id="49TAe6j@7~K]V3lvrC!i" variabletype="">j</field>
      </block>
      <block type="controls_flow_statements">
        <field name="FLOW">BREAK</field>
      </block>
    </category>
    <category name="数学运算" colour="#5C68A6">
      <block type="math_round">
        <field name="OP">ROUND</field>
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">3.1</field>
          </shadow>
        </value>
      </block>
      <block type="math_number">
        <field name="NUM">0</field>
      </block>
      <block type="math_single">
        <field name="OP">ROOT</field>
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">9</field>
          </shadow>
        </value>
      </block>
      <block type="math_trig">
        <field name="OP">SIN</field>
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">45</field>
          </shadow>
        </value>
      </block>
      <block type="math_constant">
        <field name="CONSTANT">PI</field>
      </block>
      <block type="math_number_property">
        <mutation divisor_input="false"></mutation>
        <field name="PROPERTY">EVEN</field>
        <value name="NUMBER_TO_CHECK">
          <shadow type="math_number">
            <field name="NUM">0</field>
          </shadow>
        </value>
      </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>
      <block type="math_on_list">
        <mutation op="SUM"></mutation>
        <field name="OP">SUM</field>
      </block>
      <block type="math_modulo">
        <value name="DIVIDEND">
          <shadow type="math_number">
            <field name="NUM">64</field>
          </shadow>
        </value>
        <value name="DIVISOR">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
      </block>
      <block type="math_constrain">
        <value name="VALUE">
          <shadow type="math_number">
            <field name="NUM">50</field>
          </shadow>
        </value>
        <value name="LOW">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="HIGH">
          <shadow type="math_number">
            <field name="NUM">100</field>
          </shadow>
        </value>
      </block>
      <block type="math_random_int">
        <value name="FROM">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="TO">
          <shadow type="math_number">
            <field name="NUM">100</field>
          </shadow>
        </value>
      </block>
      <block type="math_random_float"></block>
    </category>
    <category name="文本" colour="#5CA68D">
      <block type="text_charAt">
        <mutation at="true"></mutation>
        <field name="WHERE">FROM_START</field>
        <value name="VALUE">
          <block type="variables_get">
            <field name="VAR" id="B0!JU8*uGn](FPPot0b8" variabletype="">text</field>
          </block>
        </value>
      </block>
      <block type="text">
        <field name="TEXT"></field>
      </block>
      <block type="text_append">
        <field name="VAR" id="-*Z%h!C8]8?:CrB^l[Sb" variabletype="">item</field>
        <value name="TEXT">
          <shadow type="text">
            <field name="TEXT"></field>
          </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">
        <field name="END">FIRST</field>
        <value name="VALUE">
          <block type="variables_get">
            <field name="VAR" id="B0!JU8*uGn](FPPot0b8" variabletype="">text</field>
          </block>
        </value>
        <value name="FIND">
          <shadow type="text">
            <field name="TEXT">abc</field>
          </shadow>
        </value>
      </block>
      <block type="text_join">
        <mutation items="2"></mutation>
      </block>
      <block type="text_getSubstring">
      <mutation at1="true" at2="true"></mutation>
      <field name="WHERE1">FROM_START</field>
      <field name="WHERE2">FROM_START</field>
      <value name="STRING">
        <block type="variables_get">
          <field name="VAR" id="B0!JU8*uGn](FPPot0b8" variabletype="">text</field>
        </block>
      </value>
    </block>
      <block type="text_changeCase">
        <field name="CASE">UPPERCASE</field>
        <value name="TEXT">
          <shadow type="text">
            <field name="TEXT">abc</field>
          </shadow>
        </value>
      </block>
      <block type="text_trim">
        <field name="MODE">BOTH</field>
        <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">
        <mutation type="TEXT"></mutation>
        <field name="TYPE">TEXT</field>
        <value name="TEXT">
          <shadow type="text">
            <field name="TEXT">abc</field>
          </shadow>
        </value>
      </block>
    </category>
    <category name="数组" colour="#745CA6">
    <block type="lists_indexOf">
      <field name="END">FIRST</field>
      <value name="VALUE">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_create_with">
      <mutation items="0"></mutation>
    </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_create_with">
      <mutation items="3"></mutation>
    </block>
    <block type="lists_getIndex">
      <mutation statement="false" at="true"></mutation>
      <field name="MODE">GET</field>
      <field name="WHERE">FROM_START</field>
      <value name="VALUE">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_setIndex">
      <mutation at="true"></mutation>
      <field name="MODE">SET</field>
      <field name="WHERE">FROM_START</field>
      <value name="LIST">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_getSublist">
      <mutation at1="true" at2="true"></mutation>
      <field name="WHERE1">FROM_START</field>
      <field name="WHERE2">FROM_START</field>
      <value name="LIST">
        <block type="variables_get">
          <field name="VAR" id="Jk,w-5|uh#:o4}s(K7F*" variabletype="">list</field>
        </block>
      </value>
    </block>
    <block type="lists_split">
      <mutation mode="SPLIT"></mutation>
      <field name="MODE">SPLIT</field>
      <value name="DELIM">
        <shadow type="text">
          <field name="TEXT">,</field>
        </shadow>
      </value>
    </block>
    <block type="lists_sort">
      <field name="TYPE">NUMERIC</field>
      <field name="DIRECTION">1</field>
    </block>
    </category>
    <category name="颜色" colour="#A6745C">
  <block type="colour_picker">
    <field name="COLOUR">#ff0000</field>
  </block>
  <block type="colour_random"></block>
  <block type="colour_rgb">
    <value name="RED">
      <shadow type="math_number">
        <field name="NUM">100</field>
      </shadow>
    </value>
    <value name="GREEN">
      <shadow type="math_number">
        <field name="NUM">50</field>
      </shadow>
    </value>
    <value name="BLUE">
      <shadow type="math_number">
        <field name="NUM">0</field>
      </shadow>
    </value>
  </block>
  <block type="colour_blend">
  <value name="COLOUR1">
    <shadow type="colour_picker">
      <field name="COLOUR">#ff0000</field>
    </shadow>
  </value>
  <value name="COLOUR2">
    <shadow type="colour_picker">
      <field name="COLOUR">#3333ff</field>
    </shadow>
  </value>
  <value name="RATIO">
    <shadow type="math_number">
      <field name="NUM">0.5</field>
    </shadow>
  </value>
</block>
</category>
    <category name="变量" colour="#A65C81" custom="VARIABLE"></category>
    <category name="函数" colour="#9A5CA6" custom="PROCEDURE"></category>
        </xml>
        </>
    )
)

输出js格式的代码

const code_js = Blockly.JavaScript.workspaceToCode(workspace);

console.log('code_js: ', code_js);

输出Python格式的代码

import 'blockly/python';

const code_python = Blockly.Python.workspaceToCode(workspace);

console.log('code_python: ', code_python);

回显保存的block

import 'blockly/blocks';

<!--我们将拖动的块生成代码保存到后端,那么下次我们再打开的话应该回显正确的拼图;但是值提交后端代码是不能回显拼图的,所以就有了相应的xml来回显拼图;-->

<!--提交xml(当前保存在localStorage中)-->
const xml = Blockly.Xml.workspaceToDom(workspace);
        const xml_text = Blockly.Xml.domToText(xml);
        window.localStorage.setItem('xml_text',xml_text);
        
<!--回显xml-->
const xml_text = window.localStorage.getItem('xml_text');

const xml = Blockly.Xml.textToDom(xml_text);
      Blockly.Xml.domToWorkspace(xml, workspace);

中文支持

import * as Ch from 'blockly/msg/zh-hans'; 

Blockly.setLocale(Ch);

参考文章

https://developers.google.com/blockly/guides/get-started/web

https://developers.google.com/blockly/guides/configure/web/fixed-size?hl=zh_cn

https://github.com/google/blockly-samples

https://github.com/google/blockly/tree/master/msg/js](https://github.com/google/blockly/tree/master/msg/js)