First, you'll need the source code. Learn how to use Git and clone the official repository: https://github.com/jmoenig/Snap--Build-Your-Own-Blocks
You probably want to add your own 'native' block now.
Take a look at objects.js
. Scroll down to SpriteMorph.prototype.initBlocks = function()
. To keep the source code clean, you should search the appropriate category (indicated by a comment, e.g. // Motion
). Add your specification there. It looks like this:
foo: {
dev: true,
only: SpriteMorph,
type: 'command',
category: 'motion',
spec: 'do something funny with %s',
defaults: 'Snap',
}
foo
is the function that will be called later.dev
(boolean, defaultfalse
) means that the block is visible in the developer mode only (shift-click the logo in the top-left corner to enter it).only
(SpriteMorph
orStageMorph
, default both) lets the block appear either in the sprite's block list or the stage's block list only.type
(hat
,command
,reporter
,predicate
, required) sets the shape of the block.category
(motion
,looks
, ... other, required) will change the color of the block. It does not let it appear in that category yet!spec
(string, required) sets the text of the block.%s
,%n
and others (seeblocks.js SyntaxElementMorph.prototype.labelPart
for more) create inputs.%s
would create a string input and%n
a number one. The text will be localized if possible.defaults
(array, default[]
) sets the default values of the inputs.
StageMorph and SpriteMorph share the same blocks
.
To let your block appear in a specific category, look at SpriteMorph.prototype.blockTemplates
or StageMorph.prototype.blockTemplates
(important: if you want your block to appear in both, add it to every blockTemplates
, this is not that obvious because the source for both look similar). After if (cat === 'motion') {
, where motion
is the desired category, you can do a blocks.push(block('foo'));
where foo
is the name from above. blocks.push('-')
adds some space between two blocks and blocks.push('=')
even more. To add a block only in development mode, you can check this.world().isDevMode
. For a reporter you have the option to add a blocks.push(watcherToggle('yourblock'))
before the actual block definition so the user is able to add activate a watcher.
Your block needs to do something; you have these choices to define a function:
- add your function
foo
tothreads.js
, for exampleProcess.prototype.foo = function (bar) { console.log(bar); };
- if you want to access the stage from inside the function you can get it via
this.homeContext.receiver.parentThatIsA(StageMorph)
,this.homeContext.receiver
is the caller of the block
- if you want to access the stage from inside the function you can get it via
- add your function
foo
to the sprite and/or the stage inobjects.js
, likeSpriteMorph.prototype.foo = function (bar) { console.log(bar); }; StageMorph.prototype.foo = SpriteMorph.prototype.foo;
If you want to do your action continuously, use a threads.js
function. There you can append
this.pushContext('doYield');
this.pushContext();
and your function will be called on every tick. You can save something inside the context (this.context.test = "Hello World!"
) and it will therefore be available in the following executions. A reporter can return something with return
, this can even happen after multiple cycles.
The advantage of adding your function to a SpriteMorph
or StageMorph
is that you can access the stage/sprite directly with this
and the functions for either of them can variate.
To get something instantly on the stage the easiest way is to overwrite the pen trails. stage.trailsCanvas
is a canvas of the stage's width and height. It lies behind the sprites but on top of the stage's costume. Usually, the dimensions are 360 * 480px but you should not rely on that as it can be changed in the settings by the user.
Outside the deep insides of Snap*!* you need to get the stage object, for example like this: var stage = world.children[0].stage
. In theory there could be more than one IDE (world.children
) but most often this short assignment is enough. trailsCanvas
behaves like a normal canvas and has a context you can draw on. Drawing should happen in snap.html
's loop
but it does not need to; you can redraw with stage.changed()
otherwise.
Sometimes you need to have HTML as your 'stage' and a canvas is not enough. Luckily, modifying StageMorph.prototype.drawOn
is very straightforward.
Get your element, preferrably a div
. Resizing and positioning can be adopted from the original drawOn
(div
is your element):
var div = document.getElementById('foo'),
ide = this.parentThatIsA(IDE_Morph);
if (!ide.isAppMode) {
div.style.left = this.left() + 'px';
div.style.top = this.top() + 'px';
div.style.width = this.dimensions.x * this.scale + 'px';
div.style.height = this.dimensions.y * this.scale + 'px';
} else {
div.style.left = ide.left() + 'px';
div.style.top = this.top() + 'px';
div.style.width = ide.extent().x + 'px';
div.style.height = (ide.extent().y - this.top()) + 'px';
}
optionalCustomRefreshFunction();
Todo: There is an issue when a sprite is moved/dragged. The overlaying div then moves to the wrong position.