Scratch3.0 二次开发——增加积木的完全锁定、隐藏积木、禁止删除功能,增加角色一键隐藏功能
概述
Scratch3.0 二次开发系列,系列说明、Demo源码等请查看:《Scratch3.0 二次开发——开篇》
这次核心介绍如何修改 scratch-blocks、scratch-vm、scratch-gui 三个库,增加积木的完全锁定、隐藏积木、禁止删除功能,增加角色一键隐藏功能。
最终效果如图:
- 完全锁定
- 某积木(或积木组)开启“完全锁定”后,不能移动、不能修改里面的内容、不能改变原本的积木拼接、不能新增拼接
- 某些积木希望给学生看到,但又不想让学生修改(改后会影响到程序的运行),此时就使用“完全锁定”功能
- 隐藏积木
- 继承“完全锁定”的所有特性,额外增加一个隐藏功能(如果该积木处于隐藏状态,且当前用于没有查看权限,则该积木不会显示在积木编辑区)
- 禁止删除
- 某积木(或积木组)开启“禁止删除”后,该积木无法被删除
- 角色一键隐藏
- 一键使某个角色内的所有积木启动“隐藏积木”功能
需求分析
在教学场景中,老师用于授课和给学生练习的作品里面的积木逻辑可能会很复杂,包含很多角色、很多积木的拼接才能呈现出最终的效果。如此多的内容对于学生来说是一个很大的干扰(也许当前课程只学一个比较简单的知识点),因此产生了这篇文章中业务的原始需求——希望隐藏一些暂时不想给学生看到的积木或角色。
基于上述原始需求,延伸出上一节“概述”中的四大功能:完全锁定、隐藏积木、禁止删除、角色一键隐藏
前置准备
接下来的修改会涉及到scratch-blocks、scratch-vm、scratch-gui 三个库
scratch-blocks 项目的前置准备请参考这篇:Scratch3.0 二次开发——支持隐藏积木选择区域
另外两个库的环境准备请参考这篇:Scratch3.0 二次开发——开篇
思路 && 实现细节
这次内容比较多,具体思路和实现细节就放在一起写了。
还是老规矩,文中没有贴完所有源码,有需要的朋友麻烦上 scratch-blocks、scratch-vm、scratch-gui 自取,有很多关键性的注释也附在源码上面,记得看看中文类型的注释(英文的注释是官方的,中文的注释都是我自己加上去的)。下文只贴一些变量、函数名,作为源代码中的“目录”,起到总览的作用,后续看源码的时候不至于迷路 (づ ̄3 ̄)づ╭❤~
权限控制
上一节“概述”中提到,积木开启“隐藏积木”功能后或者角色开启“角色一键隐藏”功能后,对于没有权限的用户是开不到对应的积木和角色的,因此我们需要先弄一个简单的权限管理(目前只用到学生、老师两种角色,后续有需要的话可以基于这套权限控制进行扩展角色和权限内容)
scratch-blocks 项目
- 新增 authority.js,定义编辑区域权限值
- 新增 role.js,定义编辑区域角色以及角色对应的权限值
- 在 blockly.js 中添加
role
属性和setRole
函数role
属性,用于记录当前用户是什么角色setRole
函数,用于修改当前用户的角色
scratch-gui 项目
- src/models/global.js,中有
role
属性,用于记录当前用户的角色- 这个属性也会传递到 scratch-blocks 项目中,同步进行修改
- PS:这里以及后续都会使用 dva 来做数据管理,详细的配置可以参考:Scratch3.0 二次开发——引入 dva
- src/containers/gui.jsx,新增
setRole
函数,并且在componentDidMount
中调用- 目前在 url 上通过添加
?role=teacher
参数,可以直接设置当前用户的角色
- 目前在 url 上通过添加
- src/containers/blocks.jsx
shouldComponentUpdate
函数中,增加this.props.role !== nextProps.role
,监听src/models/global.js
中role
的变化componentDidUpdate
函数中,如果src/models/global.js
中role
的发生了变化,则传递到 scratch-blocks 项目中,同步修改当前用户角色——this.ScratchBlocks.setRole(this.props.role);
- 新增 src/lib/authority.js,定义权限值
- 新增 src/lib/role.js,定义角色拥有的权限值、校验是否拥有特定权限
“禁止删除”的逻辑实现
scratch-blocks 项目
- 新增 core/prevent_deletion.js,用来在积木上显示“禁止删除”的标识
- 附带要新增 media/prevent-deletion.svg 资源文件
- 显示“禁止删除”的标识如下图:
- 在 core/block.js 中:
- 增加 menuSetDeletable 函数,后续调用,实现禁止删除的功能
- 增加 this.preventDeletion 属性,用于存放 PreventDeletion 的实例化对象(禁止删除 icon)
“完全锁定”的逻辑实现
scratch-blocks 项目
- 新增 core/locking.js,用来在积木上显示“完全锁定”的标识
- 显示“禁止删除”的标识如下图:(这里是直接画 svg,不用引入额外的资源文件)
- 显示“禁止删除”的标识如下图:(这里是直接画 svg,不用引入额外的资源文件)
- 在 core/block.js 中:(详解请看代码注释)
- 增加
this.locking
属性 - 增加
isLocking
、setLocking
、menuSetLocking
、setBlocksEditable
函数
- 增加
- 在 core/connection.js 中:(详解请看代码注释)
- 在
isConnectionAllowed
函数中,添加处于锁定状态或者隐藏状态,不能链接其它积木的条件判断
- 在
“隐藏积木”的逻辑实现
scratch-blocks 项目
- 新增 core/invisible.js,用来在积木上显示“隐藏积木”的标识
- 附带要新增 media/invisible.svg 资源文件
- 显示“隐藏积木”的标识如下图:
- 在 core/block.js 中:(详解请看代码注释)
- 增加
this.invisible
属性 - 增加
isInvisible
、setInvisible
、menuSetInvisible
函数
- 增加
- 在 core/connection.js 中:(详解请看代码注释)(同上述“完全锁定”中的代码,两个写在一起了)
- 在
isConnectionAllowed
函数中,添加处于锁定状态或者隐藏状态,不能链接其它积木的条件判断
- 在
添加积木右键菜单
scratch-blocks 项目
- 在 msg/scratch_msgs.js 中:
- 增加新增右键菜单的多语言版本文案(目前只加了英文、简体中文、繁体中文三种,其它语言有需要的可以自行添加)
- 在 core/contextmenu.js 中:(详解请看代码注释)
- 新增
blockDeletableOption
、blockLockingOption
、blockInvisibleOption
三个函数,用来注册新增的右键菜单按钮
- 新增
- 在 core/block_svg.js 中:(详解请看代码注释)
- 在
showContextMenu_
函数中根据角色权限以及功能之间的逻辑关系,修改积木右键菜单按钮
- 在
状态初始化
所谓“状态初始化”,是指当设置了积木为“完全锁定”、“隐藏积木”、“禁止删除”时,要把这些状态保存到作品文件中,使其重新打开作品时还能保留当前设置的这些状态。这部分修改会涉及到 scratch-blocks、scratch-vm 两个库。
scratch-blocks 项目
- 在 core/xml.js 中:(详解请看代码注释)
- 在
blockToDom
函数中,添加“完全锁定”、“隐藏积木”两种状态的标识 - 在
domToBlockHeadless_
函数中,根据 dom 的 attr 属性,调用相关状态逻辑,设置状态 - 以上两步,分别是用于将“完全锁定”、“隐藏积木”、“禁止删除”状态保存到 DOM 中,以及从 DOM 中解析出当前积木的这三种状态
- 在
scratch-vm 项目
- 在 src/engine/blocks.js 中:(详解请看代码注释)
- 在
changeBlock
函数中,添加对“完全锁定”、“隐藏积木”、“禁止删除”三种状态的处理- 这个函数应该是在 scratch-vm 项目中对于 scratch-blocks 项目中的
Blockly.Events.BlockChange
事件的监听- 上图
deletable
、locking
、invisible
实际上就是对应 scratch-blocks - core/contextmenu.js 中blockDeletableOption
、blockLockingOption
、blockInvisibleOption
中定义的Blockly.Events.BlockChange
事件中的deletable
- PS:
blockLockingOption
、blockInvisibleOption
函数中的Blockly.Events.BlockChange
事件分别在其里面调用的block.menuSetLocking
和block.menuSetInvisible
中
- 上图
- 另外,scratch-blocks 项目和 scratch-vm 项目两个项目各自有维护自身需要的
block
,这个changeBlock
起到了将 scratch-blocks 项目的这三种状态同步给 scratch-vm 中的 block 的作用
- 在
blockToXML
函数中,将状态同步到 xml 的 attr属性上- PS:
deletable
、locking
、invisible
的值有可能是undefined
,后续判断时要注意一下
- 在
- 在 src/serialization/sb3.js 中:(详解请看代码注释)
- 在
serializeBlock
函数中,将作品文件中的值解析到 scratch-vm 的 block 对象中
- 在
隐藏角色
scratch-vm 项目
- 在 src/sprites/rendered-target.js 中:(详解请看代码注释)
- 添加
this.isInvisible
属性,控制当前目标是否需要隐藏(教师角色操作隐藏角色的功能,该功能会将角色在角色列表中隐藏掉,并且该角色中的积木都添加隐藏状态);同时新增setIsInvisible
函数用于修改该属性 - 修改
toJSON
函数,把this.isInvisible
属性也保存到导出的 JSON 中
- 添加
- 在 src/virtual-machine.js 中:(详解请看代码注释)
- 新增
invisibleSprite
函数 - 修改
installTargets
函数
- 新增
- 在 src/serialization/sb3.js 中:(详解请看代码注释)
- 修改
serializeTarget
、parseScratchObject
函数,分别添加isInvisible
属性,用来导出作品文件以及作品文件时,可以读到该数据
- 修改
scratch-gui 项目
- 在角色渲染列表,添加按钮
- PS: 因为加了权限控制,跑 scratch-gui 项目后,要访问
http://localhost:8601/?role=teacher
才能看到以上按钮(通过在 url 上传?role=teacher
参数,识别角色)(详细可见上文 “权限控制” 小节) - 为了加这么一个小小的按钮,从 src/containers/target-pane.jsx 文件开始,一路改了好几个文件(官方封装了很多层,一路往下找就 ok)
- 在 src/containers/target-pane.jsx 中,新增了
handleInvisibleSprite
函数,用于修改角色的能否可见的状态,然后就一路往下传了
- 在 src/containers/target-pane.jsx 中,新增了
至此,就完成了这一次的需求实现 ✿✿ヽ(°▽°)ノ✿
转载自:https://juejin.cn/post/7132627304949219335