web-dev-qa-db-fra.com

Transformation CSS avec redimensionnement d'élément

J'ai trouvé ceci: largeur/hauteur après transformation

et plusieurs autres, mais rien n'est tout à fait ce que je cherche. Ce que je veux, c'est redimensionner quelque chose à 50% de sa taille (avec la transition animée de Nice bien sûr) et faire en sorte que la mise en page soit réajustée à la nouvelle taille (visuelle) de l'élément. Ce qui semble se produire par défaut est que l'élément conserve sa taille d'origine dans la structure de présentation et qu'il est simplement dessiné avec les transformations associées. 

En gros, je veux que l'utilisateur clique sur un bloc de texte (ou autre contenu), puis redimensionne ce texte à 50% (ou autre) de sa taille et le colle dans un panneau ci-dessous pour indiquer qu'il a été sélectionné. Je ne veux pas que 50% des espaces soient autour de l'élément après l'avoir déplacé. 

J'ai essayé de redéfinir la hauteur/largeur, mais cela entraîne une nouvelle mise en page de l'élément, puis l'échelle est appliquée à la nouvelle mise en page, ce qui me laisse toujours 50% d'espace vide une fois que tout est terminé. Mon code spécifique à la mozilla (pour la simplicité) ressemble à ceci:

<script type="text/javascript">
 function zap(e) {

  var height = e.parentNode.offsetHeight/2 + 'px';
  var width = e.parentNode.offsetWidth/2 + 'px';
  e.parentNode.style.MozTransform='scale(0.0)'; 
  e.parentNode.addEventListener(
    'transitionend', 
    function() { 
      var selectionsArea = document.getElementById("selected");
      selectionsArea.appendChild(e.parentNode);
      e.parentNode.style.MozTransform='scale(0.5,0.5)'; 
      e.parentNode.style.display = 'inline-block';
      e.parentNode.style.height = height;
      e.parentNode.style.width = width;
    },
    true) 
}
</script>

J'espère vraiment que je n'aurai pas à me mêler des marges négatives ou d'un positionnement relatif pour y parvenir ... 

Edit: Depuis que j'ai écrit ceci, j'ai trouvé une question très similaire sans commentaires ni réponses. Cela diffère légèrement car je me moque de ce qu’il s’agisse d’une solution spécifique à html5, j’imagine que la solution n’existe pas ou réside dans CSS/javascript, quel que soit le niveau html.

Échelle/zoom d'un élément DOM et de l'espace qu'il occupe à l'aide de l'échelle de transformation CSS3 ()

Edit 2: (une autre tentative infructueuse)

J'ai aussi essayé cela, mais l'échelle et les fonctions de traduction semblent interagir de manière à rendre impossible la prédiction de la position finale ... et l'échelle avant transformation ne semble pas être la même chose que transformation puis échelle. Malheureusement, il ne suffit pas d'ajuster la traduction par le facteur d'échelle ...

function posX(e) {
    return e.offsetLeft + (e.offsetParent ? posX(e.offsetParent) : 0)
}

function posY(e) {
    return e.offsetTop + (e.offsetParent ? posY(e.offsetParent) : 0)
}

function placeThumbNail(e, container, x, y, scale) {
    var contX = posX(container);
    var contY = posY(container);
    var eX = posX(e);
    var eY = posY(e);
    var eW = e.offsetWidth;
    var eH = e.offsetHeight;

    var margin = 10;
    var dx = ((contX - eX) + margin + (x * eW * scale));
    var dy = ((contY - eY) + margin + (y * eH * scale));

    // assumes identical objects     
    var trans = 'translate(' + dx + 'px, ' + dy + 'px) ';
    var scale = 'scale(' + scale + ',' + scale + ') ';
    e.style.MozTransform =  trans + scale ; 
}

Même si cela fonctionnait, ce qui précède nécessiterait tout de même que je déplace d'abord l'élément au bas de la page avant d'exécuter ce code (pour supprimer les espaces), et que je devrais en outre recalculer la transformation lors du redimensionnement. donc je n’étais pas trop content de cette tentative de commencer de toute façon.

Notez également que si vous utilisez scale + trans vs trans + scale ci-dessus, le résultat est radicalement différent.

Edit 3: J'ai découvert les problèmes de placement ... les transformations sont appliquées dans l'ordre spécifié, et scale(0.5,0.5) modifie la taille d'un pixel de moitié par rapport à ce qu'il était à l'origine (ce qui suggère probablement une mise en œuvre interne) . Si je mets en premier lieu, traduire un peu moins/plus pour tenir compte du changement de position du coin supérieur gauche causé par l'échelle fera l'affaire, mais gérer l'emplacement des objets et ajuster les transformations lorsque le dom change manière raisonnable m’échappe toujours. Je me sens mal en ce sens parce que je m'efforce d'écrire mon propre gestionnaire de disposition en javascript pour obtenir cet effet.

50
Gus

Le problème que j’ai remarqué est que, lorsque l’élément est mis à l’échelle, le navigateur modifie son ratio de pixels, et non sa quantité de pixels. L'élément est plus petit mais il ne change pas sa taille réelle en pixels dans DOM. Pour cette raison, je ne pense pas que la solution basée sur CSS existe.

J'ai mis un élément évolutif dans un conteneur qui conserve le même taux de pixels que le reste des éléments. En utilisant Java Script, je change simplement la taille du conteneur. Tout est toujours basé sur la transformation CSS3: scale. Le code JS pourrait peut-être être plus simple, mais maintenant tout est une idée (juste une preuve de concept);) Fiddle avec deux exemples: http://jsfiddle.net/qA5Tb/9/

HTML:

<div class="overall-scalable">
    <div class="scalable" scalex='0.5' scaley='0.5'>
        Nunc et nisi ante. Integer in blandit nisi. Nulla facilisi. Vestibulum vulputate sapien eget mauris elementum sollicitudin. Nullam id lobortis dolor. Nulla vitae nibh vitae sem volutpat pretium. Nunc et nisi ante. Integer in blandit nisi. Nulla facilisi. Vestibulum vulputate sapien eget mauris elementum sollicitudin. Nullam id lobortis dolor. Nulla vitae nibh vitae sem volutpat pretium.
    </div>
</div>

CSS:

.overall-scalable {width: 350px; height: 150px; overflow: hidden; -webkit-transition: all 1s;}
.scalable {color: #666; width: 350px; height: 150px; -webkit-transform-Origin: top left; -webkit-transition: all 1s;}

JS:

$('button').click(function() {
    $('.scalable').each(function(){
        rescale($(this));
    })
});

function rescale(elem) {

    var height = parseInt(elem.css('height'));
    var width = parseInt(elem.css('width'));
    var scalex = parseFloat(elem.attr('scalex'));
    var scaley = parseFloat(elem.attr('scaley'));

    if (!elem.hasClass('rescaled')){
        var ratioX = scalex;
        var ratioY = scaley;
    }else{          
        var ratioX = 1;
        var ratioY = 1;
    }

    elem.toggleClass('rescaled');
    elem.css('-webkit-transform', 'scale('+ratioX +', '+ratioY+')');        
    elem.parent().css('width', parseInt(width*ratioX) + 'px');
    elem.parent().css('height', parseInt(height*ratioY) + 'px');
}​
39
Sófka

J'ai finalement trouvé une solution simple et qui ressemble beaucoup à celle de mon point de départ. 

J'admettrai qu'il a fallu plusieurs tentatives pour comprendre cela (au cours de la dernière année) pour enfin le comprendre. Je cherchais en fait une réponse à cette question. J'ai eu des problèmes avec cela avant. En tout cas, j'ai recommencé à travailler dessus et j'ai trouvé une solution simple. 

Quoi qu'il en soit, l'astuce consistait à utiliser le bas de la marge avec le script CSS calc. 

De plus, vous devez avoir une hauteur absolue déclarée pour l'objet (div). Je ne pense pas que cela fonctionnera si la division peut s’agrandir, sinon, après de nombreuses tentatives et des heures, j’ai enfin une solution très simple, propre et efficace. 

Ainsi, par exemple, height = 300px (pas 30%, ni aucune hauteur). Je mettrais même overflow-y = caché si vous le pouvez. Si vous voulez que la div grandisse (pas une hauteur absolue), je pense que vous aurez besoin de JavaScript. Je ne l'ai pas essayé. Sinon, cela fonctionne correctement dans tous les tests. 

Je suis sûr que cela fonctionnera bien avec SASS ou MOINS puisque vous pouvez déclarer certaines des choses répétées en utilisant des variables dans le CSS. Je n'ai pas encore essayé.

Quelques commentaires importants sur ma solution:

  1. Notez que j'utilise la hauteur de l'objet et une méthode de calcul CSS avec la taille de transformation pour déterminer la marge inférieure.
  2. J'ai codé mon travail en couleur pour montrer ce qui se passe.
  3. la division wrap a un rembourrage ne sont là que pour montrer qu'il fonctionne. Ils sont absolus et cela n'affecte pas l'objet interne.
  4. Si vous étiez en train de créer une page responsive, comme moi, vous pouvez utiliser des requêtes de média vs des classes pour obtenir le même effet.
  5. J'utilise transform-Origin: 50% 0; Ce code a fini par ne pas être aussi important que ce que je pensais au départ. J'utilise la marge inférieure pour résoudre les problèmes que j'ai eus.
  6. Dans cet exemple, je ne fais que centrer le div dans le div parent. Si vous devez placer la div, c'est un peu plus compliqué, mais une solution similaire fonctionnera. Je ne l'ai pas essayé.
  7. Vous pouvez utiliser cette même solution avec jQuery vs CSS. L'idée est la même. Vous recherchez tous les éléments avec la transformation dans la page et ajoutez le CSS si nécessaire.

J'ai également mis en place un Codepen pour tester cela avec et pour le partage.

https://codepen.io/cyansmiles/details/yvGaVP

.box-wrap {
  display: block;
  position: relative;
  box-sizing: border-box;
  width: 100%;
  margin: auto;
  text-align: center;
  margin: 30px 10px;
  padding: 40px;
  background: yellow;
}

.box-wrap:before {
  content: "";
  display: block;
  box-sizing: border-box;
  position: absolute;
  background: rgba(0, 0, 0, 0.5);
  width: calc(100% - 80px);
  height: calc(100% - 80px);
  top: 40px;
  left: 40px;
}

.box {
  display: inline-block;
  box-sizing: border-box;
  position: relative;
  transform-Origin: 50% 0;
  width: 300px;
  height: 150px;
  background: red;
}

.box.size-80 {
  transform: scale(0.8);
  margin: 0 auto calc(-150px * (1 - 0.8));
}

.box.size-70 {
  transform: scale(0.7);
  margin: 0 auto calc(-150px * (1 - 0.7));
}

.box.size-60 {
  transform: scale(0.6);
  margin: 0 auto calc(-150px * (1 - 0.6));
}

.box.size-50 {
  transform: scale(0.5);
  margin: 0 auto calc(-150px * (1 - 0.5));
}

//etc
<div class="box-wrap">
  <div class="box">
    <h1>Box at 100%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-80">
    <h1>Box at 80%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-70">
    <h1>Box at 70%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-60">
    <h1>Box at 60%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-50">
    <h1>Box at 50%</h1>
  </div>
</div>

1
Nathan Sharfi

Je devais modifier un peu mon code d'en haut.

Cela fonctionne maintenant de tous les côtés (pas seulement de haut en bas). Je vois un petit montage, je pense que c'est un problème CSS (vous pouvez ajouter un autre pixel au code pour résoudre ce problème).

Voici le lien de travail Codepen. N'hésitez pas à me le faire savoir si vous l'utilisez. Ça faisait du bien de finalement comprendre cela.

https://codepen.io/cyansmiles/pen/bLOqZo

ps. Je demande à mon codepen d’ajouter un CSS déplaçable.

.box-wrap {
  display: block;
  position: relative;
  box-sizing: border-box;
  width: 100%;
  margin: auto;
  text-align: center;
  margin: 30px 10px;
  padding: 40px;
  background: yellow;
}

.box-wrap:before {
  content: "";
  display: block;
  box-sizing: border-box;
  position: absolute;
  background: rgba(0, 0, 0, 0.5);
  width: calc(100% - 80px);
  height: calc(100% - 80px);
  top: 40px;
  left: 40px;
}

.box {
  display: inline-block;
  box-sizing: border-box;
  position: relative;
  transform-Origin: 50% 0;
  width: 300px;
  height: 150px;
  background: red;
  color: #fff;
}

.box.size-80 {
  background: red;
  transform: scale(0.8);
  margin: 0 calc((-300px * (1 - 0.8)) / 2) calc(-150px * (1 - 0.8));
}

.box.size-70 {
  background: blue;
  transform: scale(0.7);
  margin: 0 calc((-300px * (1 - 0.7)) / 2) calc(-150px * (1 - 0.7));
}

.box.size-60 {
  background: green;
  transform: scale(0.6);
  margin: 0 calc((-300px * (1 - 0.6)) / 2) calc(-150px * (1 - 0.6));
}

.box.size-50 {
  background: orange;
  transform: scale(0.5);
  margin: 0 calc((-300px * (1 - 0.5)) / 2) calc(-150px * (1 - 0.5));
}

//etc
<div class="box-wrap">
  <h1>This is a bunch of boxes!</h1>
  <div class="box size-50">
    <h1>Box at 50%</h1>
  </div>
  <div class="box size-60">
    <h1>Box at 60%</h1>
  </div>
  <div class="box size-80">
    <h1>Box at 80%</h1>
  </div>
  <div class="box size-70">
    <h1>Box at 70%</h1>
  </div>
  <div class="box size-50">
    <h1>Box at 50%</h1>
  </div>
  <div class="box size-60">
    <h1>Box at 60%</h1>
  </div>
  <div class="box size-50">
    <h1>Box at 50%</h1>
  </div>
  <div class="box size-60">
    <h1>Box at 60%</h1>
  </div>
  <div class="box size-80">
    <h1>Box at 80%</h1>
  </div>
  <div class="box size-70">
    <h1>Box at 70%</h1>
  </div>
  <div class="box size-50">
    <h1>Box at 50%</h1>
  </div>
  <div class="box size-60">
    <h1>Box at 60%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box">
    <h1>Box at 100%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-80">
    <h1>Box at 80%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-70">
    <h1>Box at 70%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-60">
    <h1>Box at 60%</h1>
  </div>
</div>

<div class="box-wrap">
  <div class="box size-50">
    <h1>Box at 50%</h1>
  </div>
</div>

0
Nathan Sharfi