Добра всем хаброчитателям!
В прошлой статье мы рассматривали способ создания карточных игр с помощью манипуляций с DOM, без использования canvas, на подобии HeartStone.
Сегодня мы продолжим эту тему, подключив к нашему делу полезнейшую в данном случае библиотеку Matreshka.js.
Введение
Кратко напомню, к чему мы пришли в прошлый раз. Общение с сервером осуществляется по WebSocket’ам, передаем JSON объекты вида: { «method»: метод, «args»: аргументы}.
Серверная сторона реализована с помощью php, скрипт запустили как демона (бесконечный цикл) в поток null.
Клиент принимает такого же вида JSON строки, вызываем методы объекта Actions (подробнее в прошлой статье).
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); }; }
Начинаем внедрять матрешку
Самое первое, где можно внедрить матрешку — это меню со списком игроков. Вообще списки в матрешке сделаны очень удобно, на мой взгляд.
Итак, задача: мы получаем список игроков в JSON, когда подключаемся, необходимо их отрисовать и назначить события.
Над дизайном не заморачиваемся.
Списки в матрешке состоят из модели и класса (ну и объекта класса).
В нашем случае
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); }); }); } });
Давайте разберемся, что же тут произошло, что значит «биндим игрока»?
Для матрешки конструкция
this.bindNode('name',':sandbox .name',Matreshka.binders.innerHTML());
означает, что мы связываем свойство name, доступное потом как свойство объекта (Obj.name) и некоторую html-сущность, в данном случае сущность с селектором ‘:sandbox .name’, где sandbox — песочница, то есть тот самый элемент, который мы только что отрендрили. Напомню, что мы это событие рендера одного конкретного элемента списка.
В качестве третьего аргумента передает тип зависимости. То есть то как они (свойство и сущность) между собой связаны.
В матрешке есть стандартны набор биндеров, и в данном случае Matreshka.binders.innerHTML() ставит в зависимость значение свойства и содержимое html-контейнера ‘:sandbox .name’.
Какая меж ними конкретно зависимость? Самая очевидная: изменяем свойство объекта — изменяется содержимое html контейнера.
Основы модели разобрали, идем дальше к классу
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'); // Засовываем в песочницу } });
В классе стоит заострить внимание на двух вещах, хоть и весьма несложных. Свойство itemRenderer показывает, как будет отрисовываться каждый элемент списка. В приведенном примере /> и есть :sandbox, от которого отсчитываем прочие селекотры.
Указание
constructor: function(){ this.bindNode('sandbox','#players'); // Засовываем в песочницу }
говорит о том, что все элементы списка будут отрисовываться внутри контейнера ‘#players’.
Матрешка в режиме сражения
Когда игроки соединились и начали игру, что мы имеем (чисто логически):
- Список карт в моей руке
- Список карт в руке соперника
- Список моих карт на игровом поле
- Список карт противника на игровом поле
Осталось реализовать эти списки с помощью матрешки и задать им некоторые события.
Карты в моей руке
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; // Экземпляр класса списка
Аналогичный список, не будем повторяться, рассмотрим, как здесь применены бинды.
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());
Как мы рассматривали выше, эти строки связывают содержимое html узла и свойства объекта.
Связав их вышеуказанным способом мы легко можем создать карту, просто сделав push в наш список:
var Actions = { ......... cardToHand: function(card){ myCards.push({ name: card.name, attack: card.attack, health: card.health, picture: card.picture, mana: card.mana }); } ......... }
Крайне просто. Но еще проще то, как мы можем менять эти свойства:
this.health = 0;
Не только задаст показатель здоровья равным нулю, но и отрисует это в html в нужном объекте.
Но и это еще не все, нам же надо отслеживать изменения здоровья, и если оно станет меньшим единицы, инициировать смерть юнита. Для этого свяжем свойство health объекта с самой картой:
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); }; } });
Третий аргумент, как я говорил, задает логику связи. В данном примере логика следующая:
Когда поменялось (установилось) значение health объекта, запускаем функцию
function(v){ if (v < 1){ this.className += ' die'; var iot = myArenaCards.indexOf(this); setTimeout(function(){ myArenaCards.splice(iot,1); },2000); }; }
This указывает на карту целиком, на песочницу (второй аргумент: ‘:sandbox’).
Заключение
В сложных приложениях, где действительно нужно двусторонее и множественное связывание, матрешка великолепно облегчает жизнь и создает комфорт при разработке.
Ведь связывать можно как угодно, в одном случае ставим обработку только на принимаемое значание (setValue), в другом на изменение свойства по событию (on: ‘click’, getValue: function(){}).
ссылка на оригинал статьи http://habrahabr.ru/post/259907/
Добавить комментарий