web-dev-qa-db-fra.com

Comment utiliser transform: translateX pour déplacer un élément enfant horizontalement à 100% sur le parent

Tout,

J'aimerais pouvoir utiliser translateX pour animer un élément enfant à 100% à travers son parent (c'est-à-dire du bord gauche au bord droit).

Le défi est que les pourcentages dans translateX se réfèrent à l'élément lui-même, pas au parent.

Ainsi, par exemple, si mon html ressemble à ceci:

<div id="parent">
    <div id="child">
</div>

Et mon CSS comme celui-ci (préfixes du fournisseur omis):

#parent {
    position: relative;
    width: 300px;
    height: 100px;
    background-color: black;
}
#child {
    position: absolute;
    width: 20px;
    height: 100px;
    background-color:red;
    transform: translateX(100%);
}

Cela ne fonctionne pas - l'enfant ne se déplace que de 20 pixels (100% de lui-même), pas tout le long du parent. (Vous pouvez le voir sur jsfiddle ):

enter image description here

Je peux le faire:

#child {
    position: absolute;
    width: 20px;
    height: 100px;
    background-color:red;
    -webkit-transform: translateX(300px) translateX(-100%);
    transform: translateX(300px) translateX(-100%);
}

Cela fonctionne (vu ici encore sur jsfiddle ), car il déplace d'abord l'enfant 300px (la pleine largeur du parent), moins 20px (la largeur de l'enfant). Cependant, cela dépend du parent ayant une dimension de pixel connue et fixe.

enter image description here

Cependant, dans ma conception réactive - je ne connais pas la largeur du parent, et cela changera.

Je sais que je peux utiliser left:0 et right:0, mais les performances d'animation de gauche/droite sont bien pires que translateX ( Merci Paul Irish! ).

Y a-t-il un moyen de faire cela?

Merci d'avance.

32
mattstuehler

Je n'ai pas publié mon idée à l'origine, car elle implique la création d'une couche HTML supplémentaire, et je m'attendais à de meilleures solutions à venir.

Comme cela ne s'est pas produit, j'explique mon commentaire. Je voulais dire ceci:

#parent {
    position: relative;
    width: 300px;
    height: 100px;
    background-color: black;
}
#wrapper {
    position: absolute;
    width: 100%;
    height: 100px;
    border: solid 1px green;
    transition: all 1s;
}
#wrapper:hover {
    -webkit-transform: translateX(100%);
    transform: translateX(100%);
}
#child {
    position: absolute;
    width: 20px;
    height: 100px;
    background-color:red;
}
#wrapper:hover #child {
    -webkit-transform: translateX(-100%);
    transform: translateX(-100%);
}

Étant donné que l'encapsuleur est à 100% de la largeur du parent, sa traduction à 100% fonctionne comme prévu.

violon

Notez que le wrapper est en cours de traduction à 100% comme vous l'avez indiqué. Cependant, il semble que ce que vous voulez vraiment, c'est déplacer l'élément à 100% de largeur. Pour ce faire, vous devez également traduire l'enfant à 100% (maintenant cela s'applique à la largeur de l'enfant) dans la direction opposée.

Correction: l'enfant doit partager la propriété de transition du wrapper:

#parent {
    position: relative;
    width: 300px;
    height: 100px;
    background-color: black;
}
#wrapper {
    position: absolute;
    width: 100%;
    height: 100px;
    border: solid 1px green;
    transition: all 5s;
}
#wrapper:hover {
    transform: translateX(100%);
}
#child {
    position: absolute;
    width: 50px;
    height: 100px;
    background-color:red;
    transition: inherit;
}
#wrapper:hover #child {
    transform: translateX(-100%);
}
<div id="parent">
    <div id="wrapper">
        <div id="child"></div>
    </div>
</div>
28
vals

Il existe une solution assez cool à ce problème en utilisant Flexbox. La clé est de profiter de la propriété flex-grow .

Supposons que vous ayez du code HTML qui ressemble à ceci:

<div class="flex-container">
  <div class="flex-spacer"></div>
  <div class="slider"></div>
</div>

Tout d'abord, donnez à .flex-container la propriété de base display: flex , et définissez sa flex-direction à ligne . Définissez le positionnement des éléments enfants sur relatif , afin qu'ils se placent côte à côte dans .flex-container.

Par défaut, la propriété flex-grow est définie sur 0 , ce qui c'est exactement ce que nous voulons au début. Cela signifie que .flex-spacer et .slider n'auront que leurs dimensions normales pour commencer. Nous gardons simplement .flex-spacer vide, et il aura une largeur de 0 .

Maintenant pour l'animation. Nous n'avons besoin que de deux règles CSS pour le faire fonctionner: ajoutez une transition à .flex-spacer et définissez flex- augmenter à 1 sur .flex-spacer pendant un événement. La deuxième règle donne toute la largeur inutilisée à l'intérieur du .flex-container à la largeur de .flex-spacer, et la première règle anime le changement de largeur. L'élément .slider est poussé jusqu'au bord du conteneur .flex.

Le CSS ressemble à ceci - j'ai ajouté un arrière-plan à .flex-spacer pour rendre sa présence un peu plus évidente, et définissez flex-grow sur 1 lorsque l'utilisateur survole le .flex-container:

body * {
  box-sizing: border-box;
}

.flex-container {
  cursor: pointer;
  display: flex;
  flex-flow: row nowrap;
  width: 100%;
  border: 2px solid #444;
  border-radius: 3px;
}

.flex-spacer,
.slider {
  flex-grow: 0;
  position: relative;
}

.slider {
  padding: 25px;
  background-color: #0DD;
}

.flex-spacer {
  background-color: #DDD;
  transition: all .4s ease-out;
}

.flex-container:hover .flex-spacer {
  flex-grow: 1;
}
<div class="flex-container">
  <div class="flex-spacer"></div>
  <div class="slider"></div>
</div>

Flexbox rend également cela assez configurable. Par exemple, disons que nous voulons que .slider se déplace de droite à gauche à la place. Tout ce que nous avons à faire est de basculer la propriété flex-direction dans .flex-container sur row-reverse , et nous avons terminé!

N'hésitez pas à jouer avec lui dans ce stylo .

Gardez à l'esprit que les choses peuvent devenir un peu plus compliquées si nous voulons des animations pour différents types d'événements. Par exemple, je suis tombé sur ce problème en essayant d'animer une étiquette lorsqu'un utilisateur tape dans un élément d'entrée. Un peu plus de HTML et CSS sont nécessaires pour le faire fonctionner (j'ai également utilisé du JS), mais le concept est le même.

Voici un autre stylo , cette fois dans le contexte d'un formulaire avec entrée.

10
Brady Lambert

Si vous utilisez la solution wrapper (comme dans l'approche des fourgonnettes utilisateur), je préfère faire référence à 1% au lieu de 100% de la largeur parent et multiplier la valeur traduite par 100. De cette façon, vous évitez les problèmes avec les éléments cliquables à côté de l'élément enfant .

Par exemple, si vous souhaitez traduire votre élément enfant au centre de l'élément parent:

#wrapper {
  position: absolute;
  left: 0;
  top: 0;

  width: 1%;
  overflow: visible;

  transform: translateX(5000%);
}

#child {
  transform: translateX(-50%);
}

html

<div id="parent">
  <div id="wrapper">
    <div id="child">

    </div>
  </div>
  <a onclick="alert('im still clickable')">
    click me
  </a>
</div>

http://codepen.io/anon/pen/BLxYXO

3
Martin Cremer