28172 sujets

CSS et mise en forme, CSS3

Bonjour.

En décembre j’étais venu ici pour poser une question pour le calcul de valeur de positionnement de background CSS avec des variables SASS. Je n’avais pas eu de solution toute faite, mais les réponses m’avait poussé à revoir complètement le fonctionnement et j’avais promis de revenir ici pour exposer le résultat.

Le voici donc :

Environnement :
Application en webview pour tablette et smartphone en Android et iOS

Matériel :
- On a des sprites SVG qui ont l’avantage d’accepter n’importe quel redimensionnement sans perte de qualité
- Ces sprites sont verticaux (les icônes sont positionnées les unes au-dessus des autres) et chaque icône tient dans sa propre zone carrée de taille unique pour tout le sprite (ie 40 x 40 px).

But de la manœuvre :
Positionner les différentes icônes de ces sprites dans des conteneurs HTML sans avoir à calculer la position et avec une taille variable liée au ratio du device (plus petit sur smartphone) ainsi qu’au besoin du design (petites sur certaines pages, moyennes sur d’autres, voir parfois très grandes). On obtient ainsi des images de pictos tenant dans un format carré, que l'on peut dimensionner à loisir positionner comme on veut (dans du texte, dans des boutons, en flottant, en absolute…) et dont la taille s'adapte aux écrans cibles.

Architecture des fichiers :
- fichier.html
- img / image1.svg
/image2.svg
/…
- sass / functions.scss (fonctions SASS)
/ variables.scss (variables SASS)
/ default.scss (fichier d’import avec media-queries)
/ base.scss (fichier commun aux devices)
/ base_T.scss (fichier spécifique aux tablettes)
/ base_M.scss (fichier spécifique aux mobiles)
/ icons.scss (fichier spécifique aux sprites d’icônes)
/ …
- css / (fichiers css compilés du SASS)

Code HTML :
ex :
<div class="icoTousLesBidules icoBidule-machin ratioGrand"></div>


Code SASS :
- function.scss

@function vertIcoPosition($tailleIco, $ordreIco) {
    @return round($tailleIco * $ordreIco * -1); 
}
/* retourne la position absolue de l’icône dans le sprite en multipliant la hauteur de la zone d’icône par son ordre puis par -1 pour la passer en négatif.
Ainsi la 5ème icône d’un sprite avec des icône de 40px  donnera :
40 x 5 x -1 = -200px
Le sprite sera donc remonté de 200px…
*/

@mixin tailleVariableIco($tailleReelleIco, $tailleDesireeIco, $ratioCible) {
    $ratioTaille: $tailleDesireeIco / $tailleReelleIco;
    $tailleIco: round($tailleReelleIco * $ratioTaille * $ratioCible);

    width: $tailleIco;
    height: $tailleIco;
}
/* Calcul la taille d’affichage de l’icône en passant en paramètre la taille réelle du SVG, la taille désirée et le ration de l’appareil cible.
*/

@mixin positionVariableIco($tailleReelleIco, $tailleDesireeIco, $ratioCible, $ordreIco) {
    $ratioTaille: $tailleDesireeIco / $tailleReelleIco;
    $tailleIco: round($tailleReelleIco * $ratioTaille * $ratioCible);

    background-position: 0 vertIcoPosition($tailleIco, $ordreIco);
}
/* Calcul la position d’affichage de l’icône avec la fonction vertIcoPosition en passant en paramètre la taille réelle du SVG, la taille désirée, l’ordre de l’icône et le ration de l’appareil cible.
*/


- base_T.scss
$ratioCible: 1; // déclaration du ratio pour les tablettes


- base_M.scss
$ratioCible: .67; // déclaration du ratio pour les smartphones


- icons.scss

.icoTousLesBidules {
    $tailleReelleIco:   54px;
    $tailleDesireeIcoPetite: 40px;
    $tailleDesireeIcoGrande: 135px; // définition des 3 tailles possibles
    background: url('../img/image1.svg') 0 0 no-repeat;
    -webkit-background-size: 100% auto;
            background-size: 100% auto;
    -webkit-background-origin: border-box;
            background-origin: border-box;
    @include flexIcoSize($tailleReelleIco, $tailleReelleIco, $ratioCible); // fixe la taille de l’image en standard

    &.nw-ratioPetit {
        @include flexIcoSize($tailleReelleIco, $tailleDesireeIcoPetite, $ratioCible); // fixe la taille de l’image en petit
    }
    &.ratioGrand{
        @include flexIcoSize($tailleReelleIco, $tailleDesireeIcoGrande, $ratioCible); // fixe la taille de l’image en grand
    }

    &.icoBidule-machin1 {
        $ordreIco: 0; // 0 = [ordre de l’icône dans le sprite] -1
        @include flexIcoPosition($tailleReelleIco, $tailleReelleIco, $ratioCible, $ordreIco); // fixe la position de l’icône en standard

        &.ratioPetit {
            @include flexIcoPosition($tailleReelleIco, $tailleDesireeIcoPetite, $ratioCible, $ordreIco); // fixe la position de l’icône en petit
        }
        &.ratioGrand {
            @include flexIcoPosition($tailleReelleIco, $tailleDesireeIcoGrande, $ratioCible, $ordreIco);
// fixe la position de l’icône en grand
        }
    }
    &.icoBidule-machin2 {
    ... 
    }... // autant qu'il y a d'icônes dans le sprite
}



PS1 : j’espère avoir été clair et didactique.
PS2 : si quelqu’un a une idée pour améliorer / simplifier les choses, qu’il n’hésite pas…
Modifié par Derwoed (13 Mar 2015 - 08:43)
Modérateur
Bonjour,

je que je ne comprends pas, c'est qu'on peut avoir plus souple que cela rien qu'en utilisant CSS…

exemple sur codepen: http://codepen.io/anon/pen/ZYqwWG

et le code pour la postérité:

.icon {
  display: inline-block;
  width: 2em;
  height: 2em;
  background: transparent url(http://www.streamlineicons.com/img/preview/preview-free-pack.svg) no-repeat;
  background-size: 1000% auto;
}
.icon.small {
  width: 1.5em;
  height: 1.5em;
}
.icon.big {
  width: 3.5em;
  height: 3.5em;
}
.icon.super {
  width: 8em;
  height: 8em;
}

.icon.icon-bomb { background-position: 66.667% 0; }
.icon.icon-hearth { background-position: 44.444% 66.667%; }
/** etc. **/


<span class="icon icon-bomb"></span>
<span class="icon icon-hearth"></span>

<span class="icon icon-bomb small"></span>
<span class="icon icon-hearth small"></span>
<br>
<span class="icon icon-bomb big"></span>
<span class="icon icon-hearth big"></span>
<br>
<span class="icon icon-bomb super"></span>
<span class="icon icon-hearth super"></span>

<br>
<span class="icon icon-bomb" style="width: 12em; height: 12em;"></span>
<br>
<span class="icon icon-bomb" style="width: 22em; height: 22em;"></span>
Merci pour ta contribution.

Pour commencer, je précise que l'utilisation de SASS sur le projet où j'ai mis ça en place était pré-existant à la problématique des icônes. Je me suis donc servi de cet outil parce qu'il était là et peut-être que CSS me permettrait d'atteindre mon objectif sans SASS. Malheureusement, je n'y étais pas parvenu.

J'ai regardé le code que tu fournis qui est assez ressemblant à ce que j'avais fait avant en CSS. J'y retrouve 2 des problèmes que je voulais supprimer :
1. Le calcul de position à faire soit même. Avec ma méthode, ce calcul n'est pas à faire, il suffit de préciser qu'il s'agit de la 17ème icône du sprite pour que la position soit calculée par SASS. Je l'avoue, ce point est uniquement un truc de fainéant… d'autant plus que vu que je positionne en px je n'arrivais pas à des chiffres à virgule comme dans ton cas. Quoiqu'il en soit, cela me fait bien gagner du temps quand on me dit "Mets plutôt l'icône 24 à la place de la 8 !". Cela simplifie aussi la vie des Dev "non intégrateurs" quand ils doivent le faire eux-même.
Dans ton exemple, le cœur n'est pas bien centré dans le div (trop à gauche). Je ne sais pas comment tu obtiens les valeurs de positionnement, mais pour corriger il faudrait soit revoir le calcul, soit tester les valeurs dans un déboggueur CSS pour trouver la bonne valeur… cela me parait fastidieux…
2. Enfin, la vraie raison de tout ça est liée au positionnement sur mobile qui est à 67% du positionnement sur tablette. Donc une icône positionnée sur tablette à -325px devrait se positionner à -217,75px sur mobile. Donc, soit je mets une valeur entière (-218 ou -217px) et j'ai une icône mal alignée d' 1px dans tous les cas (et dans mon cas où je n'ai pas de marges entre les icônes je me retrouve avec un bout d'une autre icône visible), soit je met une valeur décimale que le moteur d'Android pré 4.4 interprétera mal… Aucune de ces 2 solutions n'était viable. Donc avant de créer cette formule, j'avais autant de déclaration de position CSS pour mobile que de cas de figure (soit, autour d'une soixantaine) -> in-maintenable sur le long terme.
Modifié par Derwoed (13 Mar 2015 - 15:12)
Modérateur
Pour les deux premières:
1) la raison du décalage est que les icônes de l'image de base que j'ai trouvée ne sont pas centré sur un quadrillage strict. Si l'image de base est bien faite pour, ça ne pose aucun problèmes (solution déjà éprouvée en production, dans un cas ou l'image de fond changeait => couleurs/tailles, même avec IE6 ^^ ). Le calcul est tout simple, peut être généré par un script, ou par SASS/LESS:

avec les numéros de lignes/colonnes commençant par 1:
position en pourcentage left: (colonne-1) * 100 / (nombre_de_colonnes-1)
position en pourcentage top: (ligne-1) * 100 / (nombre_de_lignes-1)

ce qui permet de faire aller le left de 0 à 100% et idem pour le top;
en SASS/LESS il suffit d'écrire une mixin qui retourne la position en lui passant les indices de colonnes/lignes

2)
a écrit :
Enfin, la vraie raison de tout ça est liée au positionnement sur mobile qui est à 67% du positionnement sur tablette

Ce problème là est de toute façon présent, si tu joues des tailles tu dois justes faire gaffe à avoir des tailles qui se divisent bien, mais je ne vois pas en quoi ton code améliore cela. Il fait juste un arrondi, ce qui a le même effet que les pourcentages.
Sauf qu'avec les pourcentages, le navigateurs arrondira, ou pas, selon ses capacités et sa manière de fonctionner. Au mieux ton code force toujours l'arrondi (ce qui peut être problématique) et reproduit donc le plus mauvais fonctionnement.
Modifié par kustolovic (13 Mar 2015 - 17:41)
kustolovic a écrit :
Pour les deux premières:
1) la raison du décalage est que les icônes de l'image de base que j'ai trouvée ne sont pas centré sur un quadrillage strict.


OK.

a écrit :
Le calcul est tout simple, peut être généré par un script, ou par SASS/LESS:


Donc, tu valides la possibilité de passer par du SASS ou tout autre système pour calculer les positions d'icône… Au tout départ j'avais aussi un calcul de positionnement simple en SASS (un mixin d'une ligne). Mais quand j'ai vu les problèmes rencontrés sur les smartphones Android 2.3 -> 4.3 j'ai été obligé de revenir à du CSS avec tous les problèmes de calcul et de maintenance qui en découle. De plus, avec ce système, en passant 2 ou 3 variables (la taille de l'icône, sa position dans le sprite et éventuellement la taille désirée si elle diffère de sa taille réelle) j'ai automatiquement la taille du div et la position de l'icône. Je dois passer toutes les icônes de 40 à 45 px ? Pas de problème, une variable à changer pour toute l'application.

a écrit :
2)
Ce problème là est de toute façon présent,…


Hum, avec les pourcentages, je ne sais pas, je n'ai pas testé, ils ne sont pas vraiment utilisables pour les images dans ce projet. Par contre, mon code fonctionne parfaitement quel que soit l'appareil testé. Je n'ai plus les problème de positionnement quel que soit la taille du SVG d'entrée et la taille désirée en sortie.

Prenons le cas sur smartphone de la 5ème icône d'un sprite de 54px de large que je dois afficher à 40px (sur tablette). Avec ma fonction on a une icône de 27px et le calcul suivant pour le positionnement :
Arrondi(Arrondi(54 x (40/54) x 0,67) x 5 x -1) = Arrondi(Arrondi(26,8) x 5 x -1) = Arrondi(27 x 5 x -1) = -135 qui est bien un multiple de 27

En fait, le truc qui fait que cela fonctionne toujours, est que la formule renvoie forcément un entier pour la taille et un multiple de cet entier pour le positionnement…

Par contre, en faisant cette démonstration, je me rend compte je peux sans doute alléger un peu la formule (au moins un round inutile…). Je vais revoir ça…

Maintenant, mon système fonctionne bien sur notre projet mais je n'oblige personne à l'utiliser… Smiley murf