Discussione inserita tra le pillole dei link utili CSS


Questo è il remake, con descrizioni e codici aggiornati, di una vecchia pillola. La ripropongo come nuova discussione perché quella vecchia non voleva saperne di essere editata.

Si tratta di qualche snippet in puro CSS/HTML, utilizzabile per creare delle carte da gioco che al click vengono animate con un effetto flip. Il tutto utilizzando qualche proprietà CSS3 per la gestione del 3D.

Chiaramente non c'è nulla di nuovo, con qualche googleata è possibile trovare svariato materiale a riguardo.
Giusto qualche link:
https://davidwalsh.name/css-flip
http://www.gleenk.com/realizzare-una...rd-con-i-css3/
https://24ways.org/2010/intro-to-css-3d-transforms/
http://css3.bradshawenterprises.com/flip/

Ciò che ho voluto fare di diverso è una migliore elaborazione del design, cercando di dare un aspetto realistico (giocando con transizioni, luci e ombre), e dare la possibilità di organizzare una serie di carte come fossero disposte sopra un tavolo da gioco, pronte da girare e rigirare cliccandoci sopra; il tutto in puro CSS, niente JavaScript.

Qui un primo snippet di base in cui è possibile personalizzare i contenuti delle carte modificando direttamente la parte html:
codice:
<!DOCTYPE HTML>
<html>
  <head>
    <title>Pure CSS Flip Playing Cards - Custom Text</title>
    <meta charset="utf-8">
    <style type="text/css">
      /* --- MAIN --- */

      body {
         background: #000408;
         text-align: center;
      }

      h1 {
         position: relative;
         font: bolder 22px/30px "Arial Black", Gadget, sans-serif;
         color: #fff;
         z-index: 1;
      }

      .table {
         position: relative;
         width: 650px;
         height: 480px;
         background: #0470d2;
         background: linear-gradient(#039cfc, #053fa0);
         margin: 0 auto;
         padding: 18px 18px 30px;
         display: table;
         line-height: 0;
         border-radius: 15px;
         box-sizing: border-box;
         box-shadow: 1px 1px 4px 1px rgba(255, 250, 255, .5) inset, -1px -1px 4px 1px #002 inset, 3px 3px 80px 15px #000;
      }

      .deck {
         display: table-cell;
         vertical-align: middle;
      }

      /* --- CARD SETTINGS --- */

      .deck>input,
      .deck>u,
      .deck>b {
         width: 86px;
         height: 129px;
         position: relative;
      }

      .deck>input {
         margin: 0 5px 10px;
         z-index: 999;
         cursor: pointer;
         opacity: 0;
      }

      .deck>u,
      .deck>b {
         left: -5px; /* Subtract margin value */
         margin-left: -86px; /* Subtract width value */
         display: inline-block;
         text-align: center;
         vertical-align: top;
         border: 5px solid #f8fff9;
         box-sizing: border-box;
         border-radius: 8px;
         -webkit-user-select: none;
            -moz-user-select: none;
             -ms-user-select: none;
                 user-select: none;
      }

      /* --- FRONT CONTENT --- */

      .deck>u{
         background: #f8fff9;
         text-decoration: none;
         font: italic 12px Arial, Helvetica, sans-serif;
         color: grey;
      }
      
      .deck>u::first-line{
         font: 50px/70px Arial, Helvetica, sans-serif;
         color: orange;
      }

      .deck>u>b {
         font: bold 14px "Comic Sans MS", cursive, sans-serif;
         color: teal;
         display: block;
         margin-top: -10px;
      }

      /* --- BACK CONTENT --- */

      .deck>b {
         background: #ebe119;
         background: linear-gradient(#fede1e, #dabb1e);
      }

      .deck>b::before,
      .deck>b::after {
         content: '';
         display: block;
         position: absolute;
         right: 0;
         left: 0;
         top: 0;
         bottom: 0;
         background: url('http://forum.html.it/forum/images/statusicon/forum_new-48.png') center/36px;
         border-radius: inherit;
         opacity: .2;
      }

      .deck>b::after {
         background-position: calc(50% + 18px) calc(50% + 18px);
      }

      /* --- ANIMATION AND 3D SETTINGS --- */

      .deck {
         perspective: 1450px;
      }

      .deck>u,
      .deck>b {
         transition: all .8s;
         backface-visibility: hidden;
         transform-style: preserve-3d;
         z-index: 0;
      }

      .deck>input:checked + u,
      .deck>input:checked + u + b {
         z-index: 100;
      }

      .deck>input:focus + u,
      .deck>input:focus + u + b {
         transition-property:  transform, filter, box-shadow; /* Exclude z-index to immediately overlay */
         outline: 0;
         z-index: 900;
      }

      /* --- FRONT ANIMATION --- */

      .deck>u {
         transform: translateX(160%) rotateY(-180deg);
         transform-origin: -30% center;
         box-shadow: 130px 30px 40px -20px rgba(0, 0, 0, 0);
         filter: brightness(3);
      }

      .deck>input:checked + u {
         transform: translateX(0%) rotateY(0deg);
         box-shadow: 1px 1px 2px rgba(0, 0, 0, .4);
         filter: brightness(1);
      }

      /* --- BACK ANIMATION --- */

      .deck>b {
         transform: translateX(0%) rotateY(0deg);
         transform-origin: 130% center;
         box-shadow: 1px 1px 2px rgba(0, 0, 0, .4);
         filter: brightness(1);
      }

      .deck>input:checked + u + b {
         transform: translateX(-160%) rotateY(180deg);
         box-shadow: 80px 10px 20px -40px rgba(0, 0, 0, 0);
         filter: brightness(0);
      }

      /* Edge hack - Prevent click during transition, otherwise the animation breaks */

      @supports (-ms-ime-align:auto) {
         .deck>input {
            animation: step-end .8s; // Time is the same as the main transition
         }
         .deck>u,
         .deck>b {
            cursor: pointer;
         }

         .deck>input:checked {
            animation-name: noclick1;
         }

         .deck>input:not(:checked) {
            animation-name: noclick2;
         }

         @keyframes noclick1 {
            0%   { visibility: hidden; }
            100% { visibility: visible; }
         }

         @keyframes noclick2 {
            0%   { visibility: hidden; }
            100% { visibility: visible; }
         }
      }
    </style>
  </head>
  <body>
   <h1>Pure CSS Flip Playing Cards<br><small>Custom Text</small></h1>
   <div class="table">
      <div class="deck">

             <input type="checkbox"><u>☼<b>SUN</b>clear</u><b>
         </b><input type="checkbox"><u>☺<b>SMILE</b>happy</u><b>
         </b><input type="checkbox"><u>♫<b>NOTE</b>music</u><b>
         </b><input type="checkbox"><u>♥<b>HEART</b>love</u><b>
         </b>

      </div>
    </div>
  </body>
</html>
Qui un mio esempio su CodePen: https://codepen.io/OpenDec/pen/dZmdYM

Testato su attuali versioni di CH (62), FF (57) e Edge (40).

Il tutto è inglobato dentro un primo contenitore .table, dove è definita la grafica del piano di gioco, e un contenitore generale deck che, in sostanza, contiene le carte disposte sul tavolo.

Il sistema di interazione e animazione avviene tramite dei checkbox e la loro proprietà checked. Quando la carta è scoperta (girata con la parte frontale visibile) il checkbox risulta :checked. Il css si appoggia su questo comportamento per cui, cliccando sulle singole carte, avviene un "toggle" con effetto "flip", cioè un'animazione di ribaltamento della carta, la quale può essere rigirata cliccandoci nuovamente sopra.

A seguire l'elemento checkbox ci sono due elementi, <u> e <b> posti uno di seguito all'altro, che identificano rispettivamente fronte e retro della carta. Dentro l'elemento <u> (fronte) è quindi possibile inserire, per ogni singola carta, i contenuti che saranno mostrati sulla parte frontale, quando la carta è scoperta; mentre la grafica del retro sarà impostata, per l'elemento <b>, con qualche regola CSS (uguale per tutte le carte).

Da notare che ho utilizzato questi due tag per una semplice questione di "sinteticità" del codice. In teoria si potrebbero anche utilizzare due semplici <span>, come contenitori generici, anziché utilizzare degli elementi che, nello specifico, nascono per la formattazione del testo. In questo caso, però, ho preferito ottenere un codice più stringato. Ad ogni modo tutto il codice qui proposto risulta essere validato.

I due elementi <u> e <b> sono resi, tramite CSS, come inline-block (mentre di norma sono inline), quindi dimensionati e riposizionati (assieme al checkbox) in modo da sovrapporsi l'uno con l'altro creando "virtualmente" un oggetto unico per ogni singola carta.

Da tenere presente che gli elementi inline-block (come quelli inline) seguono il flusso del testo. Per questo, se tra l'uno e l'altro fossero presenti degli spazi (come caratteri testuali), il browser li terrebbe in considerazione distanziando, appunto, gli elementi tra loro. Questo potrebbe creare degli "errori" inattesi sul posizionamento; per evitare quindi che si presentino degli spazi non voluti (sia tra gli elementi di una singola carta sia tra le diverse carte), è necessario disporre tutti gli elementi uno accanto all'altro, senza lasciare ne spazi ne caratteri di ritorno a capo. Ecco perché sull'esempio ho usato la tecnica (una tra varie) di portare a capo la chiusura dell'ultimo tag per ogni riga, così che questi risultino attaccati l'uno con l'altro.

L'aspetto dei contenuti delle carte è gestito dal css nella parte sotto il commento "--- FRONT CONTENT ---".

In questo esempio ho voluto usare delle "icon font" adoperando semplicemente i simboli grafici disponibili sul font di sistema. E' comunque possibile usare un qualsiasi font, anche font personalizzati incorporati sulla pagina, per avere diverse altre icone.

Oltre l'icona è possibile definire un testo/titolo (che potrebbe identificare, ad esempio, il nome della figura, come nell'esempio) da inserire tra i tag <b></b> (quelli dentro <u></u>) e un ulteriore testo in cui specificare qualcos'altro.