scratch3.0 源码分析

最新一直在做少儿编程方向的创业,用到了scratch 3.0,在这里简单分享一下其原理。

什么是 scratch 3.0?

Scratch是美国麻省理工学院的“终身幼儿园团队”开发的一款图形化编程工具,通过点击并拖拽的方式就能完成编程,可以帮助儿童或成人初学者更好地学习编程的基础概念等。

Scratch1.0在2007年第一次公开发布,随后在2012年又推出了Scratch2.0版本,而Scratch3.0则是2019年的1月初正式推出的。

Scratch 3.0 采用了HTML5和JavaScript技术来编写,支持所有的现代浏览器和WebGL,能够跨平台使用。

Scratch 在github上有一系列的开源项目,其官方的开源scratch 3.0的编程网站的源码地址为:https://github.com/LLK/scratch-gui.git

系统构成

官方网站的主要界面和我们的很类似,因为我们就是基于它二次开发的:
scratch.png

可以看到,从左到右,依次为代码块区,编辑区和展示区,对于其使用,本文就不展开了,重点放在其原理上。

技术架构

主项目的目录结构如下:

├── build                    # 默认编译后的文件夹
│   ├── static               # 静态资源
│   ├── index.html          
│   ├── gui.js               
│   ├── lib.js               # 编译后主要的js文件                    
├── src
│   ├── components           # UI组件,负责页面呈现
│   ├── containers           # 容器组件,承载容器组件业务逻辑
│   ├── css                  # 全局通用css
│   ├── examples             # 集成测试用例
│       ├── extensions       # 拓展案例
│   ├── lib                  # 插件及高阶组件
│       ├── audio            # 声音插件
│       ├── backpack         # 背包插件
│       ├── default-project  # 默认项目
│       ├── libraries        # 素材库相关
│       ├── video            # 视频模块
│   ├── playground           # 编译后页面的模版
│   ├── reducers             # 全局状态控制
├── test                     # 测试用例
├── translations             # 翻译库
├── README.md
└── package.json
└── webpack.consig.js

通过查看其源码的package.json,我们可以看到,它是基于react 技术栈开发,核心依赖包有:

  • scratch-vm:虚拟机,管理状态并执行业务逻辑
  • scratch-blocks:代码积木块
  • scratch-l10n:国际化
  • scratch-paint:绘图拓展
  • scratch-render:舞台渲染,在舞台区域出现的基于WebGL的处理器。
  • scratch-storage:作品存储加载
  • scratch-svg-renderer:svg处理
  • scratch-audio:声音拓展

其核心原理就是用scratch-blocks生成语句块后,用scratch-vm 虚拟机抽象成底层语法,最后再调用scratch-render 和scratch-paint渲染到界面,而scratch-audio主要用于音频的剪辑处理。

关于 scratch-vm

Scratch-VM提供了一套Scratch-Blocks运行环境,因此VM定义很多丰富的外部库,在初始化时,需要对VM进行初始化,然后再定义Scratch-Blocks,最后对Blockly和VM进行事件绑定和监听:

  • 定义VirtualMachine,VirtualMachine是Scratch-VM的核心入口类;
  • 为VirtualMachine设置ScratchStorage、ScratchSVGRenderer、S- cratchRender、AudioEngine等辅助工具;
  • 定义工作空间,设置Blockly,使用inject方法,加载Scratch-Blocks;
  • 为工作空间设置相关事件;
  • 为VirtualMachine设置相关事件;
  • 开始运行VirtualMachine。

scratch-vm 的一些概念

  • sb2/sb3文件: Scratch VM能解析的文件类型,sb2为Scratch2.0项目文件,sb3为Scratch3.0项目文件;
  • Sprite角色:需要执行动画效果的对象,对象可以是svg、图像;
  • Clone克隆: 有时候角色动画需要复制自己本身,会产生许多虚拟的角色,称为克隆;
  • Costume造型:角色会有多个外观,就像人有很多衣服一样,一个角色有多个造型,造型会让角色更加生动;
  • Target目标:角色属性信息(基础信息、脚本信息),譬如角色的位置、方向、放大缩小、特效等等。每个Clone体对应一个Target;
  • Stage舞台:角色在舞台上完成动画,舞台是一个特殊的角色;
  • backdrop舞台背景:舞台会有多个外观,称为舞台背景
  • Thread线程:角色产生动画,需要运行环境,Thread线程就是角色的运行时环境;
  • Drawable图形:每个角色的Clone体,会对应一个Target,Target目标真正的绘图对象实际上是Drawable;
  • Sound声音:声音会让动画更加生动,Sound角色的属性,由所有Clone体共用;
  • effects特效:角色有很多造型,使用特效,可以在造型的基础上,修改造型外观,特效有color,fisheye,whirl,pixelate, mosaic,brightness, ghost;
  • IO输入输出:Scratch-VM将键盘、鼠标左右键、鼠标滚轮、设备连接等定义为IO,键盘、鼠标左右键、鼠标滚轮等提供统一的IO输入,设备提供蓝牙和BT连接,使用socket与Scratch-link进行通信;
  • 刷新率:每秒钟动画绘制的次数;
  • 时间片:一次绘制分配给scratch运行的时间,时间片到期自动放弃运行等待下一次运行,正常情况下,刷新率为每秒60次,时间片为1000/60ms;
  • StackFrame栈桢:线程运行时栈空间,通常情况下栈空间只有一个栈元素,当遇到循环、事件、函数时,会保留当前栈桢,新增一个栈桢;
  • ExtensionManager扩展积木管理器:扩展积木的管理,Scratch自定义了一种积木的定义方法,将自定义的积木转为json,供blockly解析,所以我们定义积木时,可以在扩展中定义。

vm调用过程

  • 加载时先全局加载虚拟机,初始化虚拟IO,然后查看网络请求中location对象的hash,如果不能识别,直接在本地新建工程,并为工程赋予唯一ID值;
  • 如果能识别,从网络或从本地加载工程,将舞台推入render生成渲染器,再把渲染器推入vm;
  • 然后调用Blockly.inject函数在一个dom(类似于div#id)上面,初始化workspace和flyoutWorkspace样式
  • 接着为workspace建立blockListener(在vm/engine/block中定义,为block建立的通用(非特定)动作函数,如move,delete,create,click什么的,特定的block动作函数的加载);
  • 然后为flyoutWorkspace建立flyoutBlockListener(所以如果需要给所有的block加统一的事件(仅限field和mutation)可以在这里添加 engine/blocks.js)

深入 scratch-vm

  • vm是在containers/gui.jsx中启动的,scratch中components是纯函数组件,而在containers文件夹中会把同名components与redux和vm连接,同时进行国际化,组件节流,版本控制,虚拟IO监听等操作,逻辑非常清晰;
  • 所有ui状态在reducer/gui.js中进行组装然后统一导出,但是要注意scratch根目录下的index.js是个假的入口文件,reducer真正是在lib/app-state-hoc中的AppStateHOC类组装的,这是一个中间件;
  • 在入口函数render-gui中GUI组件使用compose函数进行柯里化(将f(a)(b)(c)(d)变成f(a,b,c)(d)叫做柯里化)封装了AppStateHOC, HashParserHOC,TitledHOC三个中间件;
  • AppStateHOC通过判断是否需要加载paint和gui来加载不同的store,因此也在这个组件当中,guiMiddleware是一个封装了throttle的柯里化函数,按照中间件模式调用,用于为组件节流(如果一秒内点了很多次,只会执行两次),封装之后返回了经过国际化(多语言模块)和节流处理的高级组件;
  • 当vm启动时,在runtime入口定义了defaultBlockPackages类,这里面声明了每个block块的功能函数(比如moveTo);
  • 在vm/engine/runtime 715行,有一个_registerBlockPackages函数,会加载所有block块动作;
  • 然后,通过声明基类函数getPrimitives取得各个模块中的block预定义动作,之后通过订阅分发(路由模式)的方式生成packagePrimitives类,这样block块的特定功能就都可以在vm中使用了;
  • 当有点击事件发生时,vm-listener首先捕获事件,然后把消息推送至虚拟机,虚拟机会定位到对应的sprite或者带有hats的sprite,执行注册的函数;

小结

整个scratch 项目的源码非常庞大,有很多我还没有时间和精力进行总结,期待项目能够顺利推进,然后投入更多的研发,然后才能进一步深入。

我曾打算一个人使用vue 3 重写整个gui,但是没有经费支持,业余时间,个人的话真的有点力不从心。

你可能感兴趣的:(scratch3.0 源码分析)