This is a documentation for Board Game Arena: play board games online !

Anti-Stock: Difference between revisions

From Board Game Arena
Jump to navigation Jump to search
 
(14 intermediate revisions by 3 users not shown)
Line 1: Line 1:


== Overivew ==
== Overview ==
If [[Stock]] component works for you then great, you don't need this article. If you start tweaking it and you are doing something slightly more complex and you cannot find answer, it may be best to not use it.
If the [[Stock]] component works for you then great, you don't need this article. If you start tweaking it and you are doing something slightly more complex and you cannot find an answer, it may be best to not use it.
This wiki on how to replace this.
This article describes how to replace Stock.
What is Stock? Its component that does a lot of stuff in one:
What is Stock? It's a component that does a lot of stuff at once:
* It generates inline attributes of object the represent graphics and sizes of "cards" (can be anything really)
* It generates in-line attributes of an object the represents graphics and dimensions for "cards" (can be anything really).
* It also manages layout of one single container of such cards, and cards within it - including animation
* It also manages the layout of a single container of such cards, and cards within it - including animation.
* It also manages selection in that container
* It also manages selections in that container.


Recipe to get rid of stock (the gist of it):  
Recipe to get rid of Stock (the gist):  
* Create all cards as divs in template file with help of view.php is needed  
* Create all cards as divs in the template file (with the help of view.php as needed)
* Create generic card css with image, sizing etc, create/generate css per card type with background positioning - there are scripts in sharecode to generate grid positioning (what stock does)  
* Create generic css classes for the cards defining images, sizing, etc. Create/generate a css class for each card type with background positioning - there are scripts in [https://github.com/elaskavaia/bga-sharedcode/blob/master/misc/gen_sprite.php sharedcode] project to generate grid positioning or you can use some online css generator (which is what Stock does under the hood)  
* Create a parent for card placement and use dojo.place to move cards there, for css use your favour layout for this, including margins, etc
* Create a parent div for card placement and use dojo.place to move cards there. In css use your preferred layout for this, including margins, etc.
* Trickky one: When cards are moved for animation you have to use special method, none of animation methods from bga parent class will work as card will not have absolute positioning, you can use methods from sharedcode project or create your own (the trick is during animation object has to have absolute positioning which is removed after animation)
* Tricky one: When cards are moved during animations you have to use a special method; none of animation methods from BGA parent classes will work, as the card will not have absolute positioning. You can use methods from the sharedcode project or create your own (the trick is that during animation the object has to have absolute positioning, which is removed after the animation).


== Presentation ==
In modern JS the objects that we want to render will always be represented by '''div''' elements in the dom. This div will have a unique id, classes, and also custom data attributes which can be used for styling and selection.


== Presentation ==
Here is an example of some sort of card 1 of type card_21. Note that you probably don't need both card_21 as a class and as a data-num, you can use either for selection or styling.  
In modern word the objects that we want to render will be always represented by div element in the dom. This div will have unique Id, classes and also custom data attributes which can use for styling and selection.


This is some sort of card 1 of type card_21
<pre>
<pre>
   <div id="card_21_1" class="card card_21" data-num="21"></div>
   <div id="card_21_1" class="card card_21" data-num="21"></div>
</pre>
</pre>


This is 10 of hearts and king of clubs inside a hand inside a game
This is the 10 of hearts and king of clubs inside a hand in a game
 
<pre>
<pre>
<div id="game" class="classic_deck">
<div id="game" class="classic_deck">
Line 33: Line 34:
</pre>
</pre>


If you prefer not to use custom attributes you can do this  
If you prefer not to use custom attributes you can do this:
 
<pre>
<pre>
     <div id="card_H_10" class="card_H_10 suit_H rank_10"> </div>
     <div id="card_H_10" class="card_H_10 suit_H rank_10"> </div>
</pre>
</pre>


Card id, does not have to be like this either, I use this one because it maps to my db, but if you using Deck component for cards in "deck" table, you can use something like
Card id, does not have to be like this either, I use this one because it maps to my db, but if you are using the '''Deck''' component for cards in a "deck" table, you can use something like:
 
<pre>
<pre>
     <div id="deck_33" class="card_H_10 suit_H rank_10"> </div>
     <div id="deck_33" class="card_H_10 suit_H rank_10"> </div>
Line 45: Line 48:




Now we can use css to show graphics and defined size. Note unlike stock - the size should not be bound this component, you cards can have different size for example in tooltips vs hand
Now we can use css to show graphics and defined size. Note unlike Stock - the size should not be bound in this component, your cards can have different sizes (for example in tooltips vs. in the hand).


We will use the BGA card stock https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg.
We will use the BGA card stock https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg.
It has 15 columns.
It has 15 columns.


Line 121: Line 125:


== Generating Dom Elements ==
== Generating Dom Elements ==
There are multiple ways to generate dom elements, few of the methods described below - they are alternatives, i.e. you only pick one of those.


=== Static - basic ===
=== Static - basic ===
Line 131: Line 136:
     <div id="card_H_10" class="card" data-suit='H' data-rank="10"> </div>
     <div id="card_H_10" class="card" data-suit='H' data-rank="10"> </div>
     <div id="card_C_K" class="card" data-suit='C' data-rank="K"> </div>
     <div id="card_C_K" class="card" data-suit='C' data-rank="K"> </div>
... rice and repeat ...
... rince and repeat ...
   </div>
   </div>
</pre>
</pre>
Line 167: Line 172:
in js:
in js:
  let cardDiv = this.format_block('jstpl_card', {
  let cardDiv = this.format_block('jstpl_card', {
                                 suit : 'H',
                                 SUIT : 'H',
                                 rank : 'A'
                                 RANK : 'A'
                             }); // this in js code somewhere before placing it
                             }); // this in js code somewhere before placing it, then use dojo.place to place it


=== Dynamic using dojo ===
=== Dynamic using dojo ===
Line 175: Line 180:
  let suit = 'H';
  let suit = 'H';
  let rank = 'A';
  let rank = 'A';
  let cardNode = dojo.create('div', {id: `card_${suit}_${rank}`, class: `card_${suit}_${rank}`, dataSuit: suit, dataRank: rank});
  let cardNode = dojo.create('div', {id: `card_${suit}_${rank}`, class: `card card_${suit}_${rank}`, dataSuit: suit, dataRank: rank});
  dojo.place(cardNode, 'hand'); // place in hand for example
  dojo.place(cardNode, 'hand'); // place in hand for example
</pre>
</pre>
Technically you can do it all in one line as "place" is 3rd parameter of dojo.create
=== Dynamic not using dojo ===
=== Dynamic not using dojo ===
<pre>
<pre>
let suit = 'H';
let suit = 'H';
let rank = 'A';
let rank = 'A';
let div = document.createElement('div');
let div = document.createElement('div');
div.className = `card_${suit}_${rank}`';
div.id = `card_${suit}_${rank}`;
div.id = `card_${suit}_${rank}`;
div.dataSuit= suit;
div.className = `card card_${suit}_${rank}`;
div.dataRank = rank;
div.setAttribute('data-suit',suit);
div.setAttribute('data-rank',rank);
//console.log(div);
$('hand').appendChild(div);
</pre>
</pre>
As you can see this is not most compact method, so you if you do that a lot probably create a function that generates this (which also would set a click handler and tooltip for example)


== Layout ==
== Layout ==


You can use all web resources and powser of css to do any sort of fancy layout for your cards, you not bound to any predefined layouts.
You can use all web resources and power of css to do any sort of fancy layout for your cards, you not bound to any predefined layouts.
The simplest would be display: inline-block  as we already did above. You can also add margins to overlap cards for example
The simplest would be display: inline-block  as we already did above. You can also add margins to overlap cards for example.
 
More advanced layouts can be done with flex or grid:
 
<pre>
<pre>
.tableau {
.tableau {
Line 201: Line 215:
   transform: rotate(-90deg) scale(0.5);  
   transform: rotate(-90deg) scale(0.5);  
   padding: 1em;
   padding: 1em;
    display: flex;
  display: flex;
  justify-content: center;
  justify-content: center;
  flex-direction: row;
  flex-direction: row;
  flex-wrap: wrap;
  flex-wrap: wrap;
}
}


Line 238: Line 252:
   }
   }


How if you need to do some action based on select this can be your do something button bandler (this will create array of ids of cards)
Now if you need to do some action based on selection this can be in your "do something" button handler (this example below will create array of ids of cards)
<pre>
<pre>
   const card_ids = Array.from(document.querySelectorAll('.card.selected')).map(x=>x.id);
   const card_ids = Array.from(document.querySelectorAll('.card.selected')).map(x=>x.id);
Line 246: Line 260:


If you don't need selection you can hook ajax call directly to handler above.
If you don't need selection you can hook ajax call directly to handler above.


== Animation ==
== Animation ==


The animation is the only tricky part because it does not come for free. The proper animation function is pretty lengthy so I won't even put it here.
The animation is the only tricky part because it does not come for free. The proper animation function is pretty lengthy so I won't even put it here.
Basically it short you would have to reparent out object without visual movement and then you can run animation on positions, and after it is done remove absolute positioning.
Basically you would have to reparent our object without visual movement and then you can run animation on positions, and after it is done remove absolute positioning.


The simplier method is moving object itself, you can see code and example in https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js
The simplier method is moving object itself, you can see code and example in https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js
Line 259: Line 272:
And more complex method moves "phantom" object on oversurface  (which is neither parent of original object nor destination), code can found here (to use this your need 2 js methods for movement and classes for oversurface and oversurfacew > * from css):
And more complex method moves "phantom" object on oversurface  (which is neither parent of original object nor destination), code can found here (to use this your need 2 js methods for movement and classes for oversurface and oversurfacew > * from css):
https://codepen.io/VictoriaLa/pen/PojvWEV
https://codepen.io/VictoriaLa/pen/PojvWEV
[[Category:Studio]]

Latest revision as of 00:09, 16 July 2024

Overview

If the Stock component works for you then great, you don't need this article. If you start tweaking it and you are doing something slightly more complex and you cannot find an answer, it may be best to not use it. This article describes how to replace Stock. What is Stock? It's a component that does a lot of stuff at once:

  • It generates in-line attributes of an object the represents graphics and dimensions for "cards" (can be anything really).
  • It also manages the layout of a single container of such cards, and cards within it - including animation.
  • It also manages selections in that container.

Recipe to get rid of Stock (the gist):

  • Create all cards as divs in the template file (with the help of view.php as needed)
  • Create generic css classes for the cards defining images, sizing, etc. Create/generate a css class for each card type with background positioning - there are scripts in sharedcode project to generate grid positioning or you can use some online css generator (which is what Stock does under the hood)
  • Create a parent div for card placement and use dojo.place to move cards there. In css use your preferred layout for this, including margins, etc.
  • Tricky one: When cards are moved during animations you have to use a special method; none of animation methods from BGA parent classes will work, as the card will not have absolute positioning. You can use methods from the sharedcode project or create your own (the trick is that during animation the object has to have absolute positioning, which is removed after the animation).

Presentation

In modern JS the objects that we want to render will always be represented by div elements in the dom. This div will have a unique id, classes, and also custom data attributes which can be used for styling and selection.

Here is an example of some sort of card 1 of type card_21. Note that you probably don't need both card_21 as a class and as a data-num, you can use either for selection or styling.

  <div id="card_21_1" class="card card_21" data-num="21"></div>

This is the 10 of hearts and king of clubs inside a hand in a game

<div id="game" class="classic_deck">
  <div id="hand" class="hand">
    <div id="card_H_10" class="card" data-suit='H' data-rank="10"> </div>
    <div id="card_C_K" class="card" data-suit='C' data-rank="K"> </div>
  </div>
</div>

If you prefer not to use custom attributes you can do this:

    <div id="card_H_10" class="card_H_10 suit_H rank_10"> </div>

Card id, does not have to be like this either, I use this one because it maps to my db, but if you are using the Deck component for cards in a "deck" table, you can use something like:

    <div id="deck_33" class="card_H_10 suit_H rank_10"> </div>

where 33 is id which maps to database id of deck table.


Now we can use css to show graphics and defined size. Note unlike Stock - the size should not be bound in this component, your cards can have different sizes (for example in tooltips vs. in the hand).

We will use the BGA card stock https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg.

It has 15 columns.

.card {
   background-image: url('https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg');   /* don't do full url in your game, copy this file inside img folder */
   background-size: 1500% auto;  /* this mean size of background is 15 times bigger than size of card, because its sprite */
   border-radius: 5%;
   width: 10em;
   height: 13.5em;
   box-shadow: 0.1em 0.1em 0.2em 0.1em #555;
}


.card[data-rank="10"] { /* 10 is column number 10 - 2 because we start from 0 and first card is sprite is 2. The multiplier is (15 - 1) is because we have 15 columns. -1 is because % in CSS is weird like that. */
   background-position-x: calc(100% / (15 - 1) * (10 - 2));
}
.card[data-rank="K"] { /* King will be number 13 in rank */
   background-position-x: calc(100% / (15 - 1) * (13 - 2));
}

.card[data-suit="H"] { /* Hears row position is 1 (because we count from 0). Multiplier (4 - 1) is because we have 4 rows and -1 is because % in CSS is weird like that. */
   background-position-y: calc(100% / (4 - 1) * (1));
}
.card[data-suit="C"] { /* Clubs row position is 2 */
   background-position-y: calc(100% / (4 - 1) * (2));
}

Now if you want to see these cards, we can put them in some specific component for example hand, and there we can define layout and sizing just for hand

.hand .card {
  font-size: 2.4rem;
  display: inline-block;
}

Working example: https://codepen.io/VictoriaLa/pen/rNwgWrB

To finish the deck you have to finish css for all rows/columns which whould be only 10-20 records, really easy and repetitive! If you really cannot write that much css manually you can also generate it using php code (this is side kick command line - does not go to your game)

<?php
// this simple script generates sprite css
// it has no params - fix inline to change what it generates

$from=1; // from index
$to=40+4; // to index
$maxcol=4; // number of columns in the sprite
$scol=$maxcol-1;
$srow=((int)(($to-$from)/$maxcol));
for ($num=$from;$num<=$to;$num++) {
    $index=$num-$from;
    $row=(int)($index/$maxcol);
    $col=$index%$maxcol;
    echo ".tech_E_$num { background-position: calc(100% / $scol * $col) calc(100% / $srow * $row);}\n";
}
?>




And now if you want to user to have preferece for what deck style to use its just matter of change class from "classic_deck" to "fancy_deck" in our game parent div and adding in CSS

.fancy_deck .card {
   background-image: url('https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_DESIGN_COLORED.jpg');
}

Generating Dom Elements

There are multiple ways to generate dom elements, few of the methods described below - they are alternatives, i.e. you only pick one of those.

Static - basic

You basically write all your cards in .tpl file. Just cut & paste you template and fix few classes. That is easiest! In your js code you never create new element, you just move existing element inside dome. If you need a copy for tooltips, logs or button you can clone one of them change id and add/remove class if needed.

  <div id="limbo" class="limbo">
    <div id="card_H_10" class="card" data-suit='H' data-rank="10"> </div>
    <div id="card_C_K" class="card" data-suit='C' data-rank="K"> </div>
... rince and repeat ...
  </div>

Static - bga template engine

in .tpl

in view.php

       $this->page->begin_block($template, "deck_cards");
       $ranks = [2,3,4,5,6,7,8,9,10,'J','Q','K','A'];
       $suits = ['S','H','C','D'];
       foreach ($ranks as $rank) {
         foreach ($suits as $suit) {
           $this->page->insert_block("deck_cards", [
                   'SUIT' => "$suit,
                   'RANK' => "$rank,
           ]);
       }

}

Dynamic using .tpl thingy

In the js section of .tpl

      var jstpl_card = '<div id="card_{SUIT}_{RANK}" class="card card_{SUIT}_{RANK}" data-suit="{SUIT}" data-rank="{RANK}"></div>'; 

in js:

let cardDiv = this.format_block('jstpl_card', {
                               SUIT : 'H',
                               RANK : 'A'
                           }); // this in js code somewhere before placing it, then use dojo.place to place it

Dynamic using dojo

 let suit = 'H';
 let rank = 'A';
 let cardNode = dojo.create('div', {id: `card_${suit}_${rank}`, class: `card card_${suit}_${rank}`, dataSuit: suit, dataRank: rank});
 dojo.place(cardNode, 'hand'); // place in hand for example

Technically you can do it all in one line as "place" is 3rd parameter of dojo.create

Dynamic not using dojo

let suit = 'H';
let rank = 'A';
let div = document.createElement('div');
div.id = `card_${suit}_${rank}`;
div.className = `card card_${suit}_${rank}`;
div.setAttribute('data-suit',suit);
div.setAttribute('data-rank',rank);
//console.log(div);
$('hand').appendChild(div);

As you can see this is not most compact method, so you if you do that a lot probably create a function that generates this (which also would set a click handler and tooltip for example)

Layout

You can use all web resources and power of css to do any sort of fancy layout for your cards, you not bound to any predefined layouts. The simplest would be display: inline-block as we already did above. You can also add margins to overlap cards for example.

More advanced layouts can be done with flex or grid:

.tableau {
   position: relative;
   width: 50em;
   height: 30em;
   outline: dashed 1px green;
   transform: rotate(-90deg) scale(0.5); 
   padding: 1em;
   display: flex;
   justify-content: center;
   flex-direction: row;
   flex-wrap: wrap;
}

.tableau .card{
  margin-right: -2em;
}

In this example cards on tableu are smaller, rotated 90 degrees and overlaping.


https://codepen.io/VictoriaLa/pen/PojvWEV


Deck Layouts - codepen.io.png

Selection and Click Handlers

To hookup selection or clicking, you have to manually hook up handler to all cards, which is easy

document.querySelectorAll(".card").forEach(node=>node.addEventListener("click", (event) => {
  var target = event.target;
  target.classList.toggle('selected');
  }
}));

And you define class for selected in css

 .selected {
    outline: dashed blue 0.1em;
 }

Now if you need to do some action based on selection this can be in your "do something" button handler (this example below will create array of ids of cards)

  const card_ids = Array.from(document.querySelectorAll('.card.selected')).map(x=>x.id);

If you need single selection, just add a call to remove all previous 'selected' from all card, before adding it.

If you don't need selection you can hook ajax call directly to handler above.

Animation

The animation is the only tricky part because it does not come for free. The proper animation function is pretty lengthy so I won't even put it here. Basically you would have to reparent our object without visual movement and then you can run animation on positions, and after it is done remove absolute positioning.

The simplier method is moving object itself, you can see code and example in https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js (slideToObjectRelative).


And more complex method moves "phantom" object on oversurface (which is neither parent of original object nor destination), code can found here (to use this your need 2 js methods for movement and classes for oversurface and oversurfacew > * from css): https://codepen.io/VictoriaLa/pen/PojvWEV