写在前面:
为公共事业做贡献,做了个开源版本:scratch.lite
开源版本带MySQL后台服务器,功能:注册、登录、保存作品、分享、修改作品名称、保存作品缩略图。
有兴趣的朋友可以去下载参考:lite: 一个轻量级的Scratch编程分享平台:注册登录、作品创作、作品管理、素材管理、用户管理,作品点赞、收藏、分享。
Scratch二次开发的纯技术交流QQ群:115224892/914159821
-
一、关于作品形态
1、一个单独的文件保存,如:.sb/.sb2/.sb3;
2、分开保存:源代码以JSON格式保存到数据库,素材以文件形式保存。
两种方式的对比分析:
第一种操作简单,但作品的素材被打包进文件,源文件会比较大;(个人平台可以用用)
第二种技术上要多走几步。素材可以被简单的重复使用。(作品量大时,推荐使用)
本文重点介绍第二种方式。
-
二、一个Scratch3作品JSON格式分析
源代码中,自带了一个默认作品:/src/lib/default-project/project-data.js
想简单修改默认作品的小猫图片或是替换默认作品的,可以在此操作,直接使用。
{
targets: [
{
isStage: true,
name: 'Stage',
variables: {
'`jEk@4|i[#Fk?(8x)AV.-my variable': [
translator(messages.variable),
0
]
},
lists: {},
broadcasts: {},
blocks: {},
currentCostume: 0,
costumes: [
{
assetId: 'cd21514d0531fdffb22204e0ec5ed84a',
name: translator(messages.backdrop, {index: 1}),
md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
dataFormat: 'svg',
rotationCenterX: 240,
rotationCenterY: 180
}
],
sounds: [
{
assetId: '83a9787d4cb6f3b7632b4ddfebf74367',
name: translator(messages.pop),
dataFormat: 'wav',
format: '',
rate: 11025,
sampleCount: 258,
md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav'
}
],
volume: 100
},
{
isStage: false,
name: translator(messages.sprite, {index: 1}),
variables: {},
lists: {},
broadcasts: {},
blocks: {},
currentCostume: 0,
costumes: [
{
assetId: 'b7853f557e4426412e64bb3da6531a99',
name: translator(messages.costume, {index: 1}),
bitmapResolution: 1,
md5ext: 'b7853f557e4426412e64bb3da6531a99.svg',
dataFormat: 'svg',
rotationCenterX: 48,
rotationCenterY: 50
},
{
assetId: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f',
name: translator(messages.costume, {index: 2}),
bitmapResolution: 1,
md5ext: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg',
dataFormat: 'svg',
rotationCenterX: 46,
rotationCenterY: 53
}
],
sounds: [
{
assetId: '83c36d806dc92327b9e7049a565c6bff',
name: translator(messages.meow),
dataFormat: 'wav',
format: '',
rate: 22050,
sampleCount: 18688,
md5ext: '83c36d806dc92327b9e7049a565c6bff.wav'
}
],
volume: 100,
visible: true,
x: 0,
y: 0,
size: 100,
direction: 90,
draggable: false,
rotationStyle: 'all around'
}
],
meta: {
semver: '3.0.0',
vm: '0.1.0',
agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' // eslint-disable-line max-len
}
}
从默认作品的JSON数据就可以看到其组成,不再赘述。
默认作品资源的获取:/src/lib/default-project/index.js
[{
id: 0,
assetType: 'Project',
dataFormat: 'JSON',
data: JSON.stringify(projectJson)
}, {
id: '83a9787d4cb6f3b7632b4ddfebf74367',
assetType: 'Sound',
dataFormat: 'WAV',
data: new Uint8Array(popWav)
}, {
id: '83c36d806dc92327b9e7049a565c6bff',
assetType: 'Sound',
dataFormat: 'WAV',
data: new Uint8Array(meowWav)
}, {
id: 'cd21514d0531fdffb22204e0ec5ed84a',
assetType: 'ImageVector',
dataFormat: 'SVG',
data: encoder.encode(backdrop)
}, {
id: 'b7853f557e4426412e64bb3da6531a99',
assetType: 'ImageVector',
dataFormat: 'SVG',
data: encoder.encode(costume1)
}, {
id: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f',
assetType: 'ImageVector',
dataFormat: 'SVG',
data: encoder.encode(costume2)
}]
从其中可以看出,所有素材的命名规则(md5命名规则,这样可以确保一个素材只保存一份)及获取方式。
-
三、作品保存到服务器的步骤
1、获取作品JSON源代码
2、素材分析与保存
3、JSON源代码保存
4、作品缩略图保存
触发一个作品的保存动作,有两种方式:1、定时自动保存,、手动点击按钮保存。
这两种方式所做的事,在REACT框架下,其实就是简单的修改并分发作品需要被保存的消息。
真正保存的部分,并不在此,所以这两种方式的源代码,就不再贴了。
保存功能部分所在文件:/src/lib/project-saver-hoc.jsx
componentDidUpdate (prevProps) {
//点击保存作品时,保存作品源代码、素材
//条件已在点击保存作品时判断过:已登录、不是其他用户的作品、内容已变更
if (this.props.isUpdating && !prevProps.isUpdating) {
//console.warn("开始保存作品到服务器!!!");
this.props.onShowInlineAlerts('正在保存作品……');
return this.storeProject(this.props.reduxProjectId)
.then(() => {
this.props.onUpdatedProject(this.props.loadingState);//更新作品状态
this.props.onShowInlineAlerts('作品已保存');//更新作品成功的提示
})
.catch(err => {
this.props.onShowStandardAlert('作品未能保存');//更新作品失败的提示
this.props.onProjectError(err);//
});
}
}
其中使用的Alert提示功能,本人已摒弃了国际化,所以已简化成可以直接写提示内容。
上面的源代码只是整个保存的入口及总流程,真正保存作品的代码如下:
storeProject (projectId, requestParams) {
requestParams = requestParams || {};
if (projectId==0){//新作品,同时上传作品名称
requestParams['title']=this.props.reduxProjectTitle;
}
//在保存作品JSON源代码前,先将素材保存到服务器。
//这样可以确保在保存作品的过程中素材不必更新
const projectData = this.props.vm.toJSON();
return Promise.all(this.props.vm.assets
.filter(asset => !asset.clean)
.map(
asset => storage.store(
asset.assetType,
asset.dataFormat,
asset.data,
asset.assetId
).then(response => {
if (response.status !== 'ok') {
return Promise.reject(response.code);
}
asset.clean = true;
})
)
)
.then(() =>saveProjectToServer(projectId, projectData, requestParams))
.then(response => {
if (response.id){//返回的作品ID
//保存缩略图
this.storeProjectThumbnail(response.id);
//如果是第一次保存作品
if (projectId==0){
//第一步:设置作品新Id、作者Id
this.props.onSetProjectNewId(this.props.authorId, response.id);
//第二步:设置浏览器的uri
window.location.hash=response.id;
}
}
this.props.onSetProjectUnchanged();//设置作品为未修改状态
return response;
})
.catch(err => {
log.error(err);
throw err; // pass the error up the chain
});
}
服务器端源代码,这里只放保存JSON到数据库部分,保存素材、保存缩略图非常简单,就不贴了。
//保存作品:源代码。req.body为项目JSON源代码
this.app.put('/scratch/projects/:projectid',function(req, res) {
console.log('服务器:保存作品JSON源代码');
var UPDATE =`UPDATE scratch SET src=? WHERE id=${req.params.projectid} LIMIT 1`;
var VAL = [`${JSON.stringify(req.body)}`];//防注入
DB.qww(UPDATE, VAL, function(err,SCRATCH){
if (err) {
res.status(404).send({});
return;
}
res.status(200).json({"status":"ok"})
})
});
预告:下篇博客内容是:Scratch3.0作品三大类状态及对应源代码分析
后续准备写的几个方向:
Scatch3.0一个作品的获取、加载、变更、保存、
Scratch3.0作品状态简化及升级、
Scrath GUI版本菜单定制、
Scratch Alert功能的简化及升级、
Scratch教程功能的简化及升级、
Scratch积本定制、
Scratch扩展定制、
Scratch Player定制(脱离ScratchGUI,仅使用VM,把作品转换成H5格式,方便操作与分享展示)
... ...
(想分享的太多了,工作忙,时间紧,慢慢来吧,各位看官可以先收藏或是关注本博主!!!)
写在后面:
如果本文章对您有帮助,请不吝点个赞再走!!!
您的支持,就是本人继续分享的源动力!!!
Bailee 了个Bye!!!