Creating games without canvas: Matreshka.js
Good to all habretchitel!
In a previous article, we looked at a way to create card games using DOM manipulation, without using canvas, in the likeness of HeartStone.
Today we will continue this topic by connecting the Matreshka.js library, which is most useful in this case, to our case.

Briefly recall what we came to last time. Communication with the server is carried out via WebSockets, we pass JSON objects of the form: {"method": method, "args": arguments}.
The server side is implemented using php, the script was launched as a daemon (an infinite loop) into a null stream.
The client accepts the same type of JSON string, we call the methods of the Actions object (more in the previous article).
The very first where you can implement a nesting doll is a menu with a list of players. In general, the lists in the matryoshka are made very convenient, in my opinion.
So, the task: we get a list of JSON players, when we connect, we need to draw them and assign events.
We don’t bother with design.
Lists in the nesting doll consist of a model and a class (well, an object of a class).
In our case
Let's see what happened here, what does “player’s bindim" mean?
For nesting dolls design
means that we bind the name property, which is then available as a property of the object (Obj.name) and some html entity, in this case, the entity with the ': sandbox .name' selector, where sandbox is the sandbox, that is, the same element that we just rendered. Let me remind you that this is a render event of one specific list item.
Passes the type of dependency as the third argument. That is, how they (property and essence) are interconnected.
Matryoshka has a standard set of binders, and in this case Matreshka.binders.innerHTML () makes the value of the property and the contents of the html container ': sandbox .name' dependent.
What exactly is the relationship between them? The most obvious: we change the property of the object - the contents of the html container changes.
The basics of the model were taken apart,
In the classroom it is worth focusing on two things, albeit very simple ones. The itemRenderer property shows how each item in the list will be drawn. In the above example, the>> is: sandbox, from which we count the other selectors.
Note
says that all elements of the list will be drawn inside the container '#players'.
When the players connected and started the game, what do we have (logically):
It remains to implement these lists with the help of a nesting doll and ask them some events.
We will not repeat the similar list, we will consider how binds are applied here.
As we discussed above, these lines bind the contents of the html node and the properties of the object.
By connecting them in the above way, we can easily create a map by simply pushing to our list:
It is very simple. But even simpler is how we can change these properties:
Not only will it set the health indicator to zero, but it will also render it in html in the desired object.
But this is not all, we also need to track changes in health, and if it becomes less than one, initiate the death of the unit. To do this, we associate the health property of the object with the map itself:
The third argument, as I said, sets the logic of communication. In this example, the logic is as follows:
When the health value of the object has changed (set), run the function
This points to the entire map, to the sandbox (second argument: ': sandbox').
In complex applications where you really need two-way and multiple binding, the nesting doll makes life easier and creates comfort during development.
After all, you can bind as you like, in one case we put the processing only on the accepted value (setValue), in the other on the change of the property by event (on: 'click', getValue: function () {}).
In a previous article, we looked at a way to create card games using DOM manipulation, without using canvas, in the likeness of HeartStone.
Today we will continue this topic by connecting the Matreshka.js library, which is most useful in this case, to our case.

Introduction
Briefly recall what we came to last time. Communication with the server is carried out via WebSockets, we pass JSON objects of the form: {"method": method, "args": arguments}.
The server side is implemented using php, the script was launched as a daemon (an infinite loop) into a null stream.
The client accepts the same type of JSON string, we call the methods of the Actions object (more in the previous article).
socket.onmessage
socket.onmessage = function (e){
if (typeof e.data === "string"){
var request = JSON.parse(e.data);
console.log('Response: ' + request.function);
Actions[request.function](request.args);
};
}
We begin to introduce a nesting doll
The very first where you can implement a nesting doll is a menu with a list of players. In general, the lists in the matryoshka are made very convenient, in my opinion.
So, the task: we get a list of JSON players, when we connect, we need to draw them and assign events.
We don’t bother with design.
Lists in the nesting doll consist of a model and a class (well, an object of a class).
In our case
List Model
var listModel = Matreshka.Class({ // Модель списка'extends': Matreshka.Object, // Наследуется от Matreshka.Object всегдаconstructor: function(data){
this.jset(data);
this.on('render',function(){ // Что происходит после отрисовкиthis.bindNode('name',':sandbox .name',Matreshka.binders.innerHTML()); // Биндим имя игрокаthis.bindNode('letsFight',':sandbox .fightButton'); // Биндим кнопку вызова на бойthis.on('click::letsFight',function(){
Actions.figthRequest(this.name);
});
});
}
});
Let's see what happened here, what does “player’s bindim" mean?
For nesting dolls design
this.bindNode('name',':sandbox .name',Matreshka.binders.innerHTML());
means that we bind the name property, which is then available as a property of the object (Obj.name) and some html entity, in this case, the entity with the ': sandbox .name' selector, where sandbox is the sandbox, that is, the same element that we just rendered. Let me remind you that this is a render event of one specific list item.
Passes the type of dependency as the third argument. That is, how they (property and essence) are interconnected.
Matryoshka has a standard set of binders, and in this case Matreshka.binders.innerHTML () makes the value of the property and the contents of the html container ': sandbox .name' dependent.
What exactly is the relationship between them? The most obvious: we change the property of the object - the contents of the html container changes.
The basics of the model were taken apart,
var listArray = Matreshka.Class({ // Класс списка'extends': Matreshka.Array,
Model: listModel, // Наша модель
itemRenderer: '<li class="player"><span class="name"></span><span class="fightButton"></span></li>', // Как рендрится каждый элементconstructor: function(){
this.bindNode('sandbox','#players'); // Засовываем в песочницу
}
});
In the classroom it is worth focusing on two things, albeit very simple ones. The itemRenderer property shows how each item in the list will be drawn. In the above example, the>> is: sandbox, from which we count the other selectors.
Note
constructor: function(){
this.bindNode('sandbox','#players'); // Засовываем в песочницу
}
says that all elements of the list will be drawn inside the container '#players'.
Matryoshka in battle mode
When the players connected and started the game, what do we have (logically):
- List of cards in my hand
- List of cards in the opponent’s hand
- List of my cards on the playing field
- List of opponent cards on the playing field
It remains to implement these lists with the help of a nesting doll and ask them some events.
Cards in my hand
Cards in my hand
var myCardsModel = Matreshka.Class({ // Модель списка'extends': Matreshka.Object,
constructor: function(data){
this.jset(data);
this.on('render',function(){
this.bindNode('name',':sandbox .title',Matreshka.binders.innerHTML());
this.bindNode('attack',':sandbox .attack .value',Matreshka.binders.innerHTML());
this.bindNode('health',':sandbox .health .value',Matreshka.binders.innerHTML());
this.bindNode('mana',':sandbox .mana .value',Matreshka.binders.innerHTML());
this.bindNode('picture',':sandbox .picture',{
setValue: function(v){
this.innerHTML = '<img src="img/' + v + '">'
}
});
this.on('click::sandbox',function(){
myArenaCards.push(this);
myCards.splice(myCards.indexOf(this),1);
Actions.send('putCard',this.toJSON());
});
});
}
});
var myCardsArray = Matreshka.Class({ // Класс списка'extends': Matreshka.Array,
Model: myCardsModel,
itemRenderer: '<div class="card">'
+'<div class="title"></div>'
+'<div class="health"><div class="svg">' + $b('#icons #heart')[0].innerHTML + '</div><div class="value"></div></div>'
+'<div class="attack"><div class="svg">' + $b('#icons #attack')[0].innerHTML + '</div><div class="value"></div></div>'
+'<div class="mana"><div class="svg">' + $b('#icons #diamond')[0].innerHTML + '</div><div class="value"></div></div>'
+'<div class="picture"></div>'
+'</div>',
constructor: function(){
this.bindNode('sandbox','#myhand'); // Засовываем в песочницу
}
});
var myCards = new myCardsArray; // Экземпляр класса спискаWe will not repeat the similar list, we will consider how binds are applied here.
this.bindNode('name',':sandbox .title',Matreshka.binders.innerHTML());
this.bindNode('attack',':sandbox .attack .value',Matreshka.binders.innerHTML());
this.bindNode('health',':sandbox .health .value',Matreshka.binders.innerHTML());
this.bindNode('mana',':sandbox .mana .value',Matreshka.binders.innerHTML());
As we discussed above, these lines bind the contents of the html node and the properties of the object.
By connecting them in the above way, we can easily create a map by simply pushing to our list:
var Actions = {
.........
cardToHand: function(card){
myCards.push({
name: card.name,
attack: card.attack,
health: card.health,
picture: card.picture,
mana: card.mana
});
}
.........
}
It is very simple. But even simpler is how we can change these properties:
this.health = 0;
Not only will it set the health indicator to zero, but it will also render it in html in the desired object.
But this is not all, we also need to track changes in health, and if it becomes less than one, initiate the death of the unit. To do this, we associate the health property of the object with the map itself:
this.bindNode('health',':sandbox',{
setValue: function(v){
if (v < 1){
this.className += ' die';
var iot = myArenaCards.indexOf(this);
setTimeout(function(){
myArenaCards.splice(iot,1);
},2000);
};
}
});
The third argument, as I said, sets the logic of communication. In this example, the logic is as follows:
When the health value of the object has changed (set), run the function
function(v){
if (v < 1){
this.className += ' die';
var iot = myArenaCards.indexOf(this);
setTimeout(function(){
myArenaCards.splice(iot,1);
},2000);
};
}
This points to the entire map, to the sandbox (second argument: ': sandbox').
Conclusion
In complex applications where you really need two-way and multiple binding, the nesting doll makes life easier and creates comfort during development.
After all, you can bind as you like, in one case we put the processing only on the accepted value (setValue), in the other on the change of the property by event (on: 'click', getValue: function () {}).