Transformación CSS con cambio de tamaño de elementos


Encontré esto: ancho / alto después de transformar

Y varios otros, pero nada no es exactamente lo que estoy buscando. Lo que quiero es escalar algo al 50% de su tamaño (con una agradable transición animada, por supuesto) y ajustar el diseño de la página al nuevo tamaño (visual) del elemento. Lo que parece suceder de forma predeterminada es que el elemento aún conserva su tamaño original en la estructura de diseño, y simplemente se dibuja con las transformaciones asociadas.

Básicamente quiero que el usuario haga clic en un bloque de texto (u otro contenido), y luego escalar ese texto al 50% (o lo que sea) de su tamaño y pegarlo en un panel a continuación para indicar que ha sido seleccionado. No quiero un 50% de espacio en blanco alrededor del elemento después de moverlo.

He intentado volver a configurar el alto / ancho, pero eso hace que el elemento iself se someta a un nuevo diseño y luego la escala se aplica al nuevo diseño y esto todavía me deja con 50% de espacio en blanco después de todo es terminar. Mi código específico de Mozilla (por simplicidad) se ve así:

<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>

Realmente espero no tener que meterme con márgenes negativos o posicionamiento relativo para lograr esto...

Edit: Desde que escribí esto encontré una pregunta muy similar sin comentarios ni respuestas. Difiere ligeramente ya que no me importa que sea una solución específica de html5, sospecho que la solución no existe o se encuentra en CSS/javascript independientemente del html nivel.

Escala / zoom un elemento DOM y el espacio que ocupa usando CSS3 transform scale()

Edit 2: (otro intento que no funciona)

También he intentado esto, pero la escala y las funciones de traducción parecen interactuar de una manera que hace que la posición final sea imposible de predecir... y escalar antes de transformar no parece ser lo mismo que transformar luego escalar. Desafortunadamente no es solo una cuestión de ajustar la traducción por el factor de escala ...

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 ; 
}

Incluso si funcionara, lo anterior me requeriría mover primero el elemento al final de la página antes de ejecutar este código (para deshacerme del espacio en blanco), y además necesitaría recalcular la transformación al cambiar el tamaño... así que no estaba muy contento con este intento de empezar de todos modos.

También tenga en cuenta que si utiliza scale + trans vs trans + scale por encima del resultado es drásticamente diferente.

Editar 3: Me di cuenta de los problemas de colocación... las transformaciones se aplican en el orden especificado, y scale(0.5,0.5) esencialmente cambia el tamaño de un píxel a la mitad de lo que era originalmente (probablemente insinuando cómo lo implementaron internamente). Si pongo traducir primero, traducir un poco menos/más para tener en cuenta el cambio en la posición de la esquina superior izquierda causado por la escala hará el truco, pero administrar la ubicación de los objetos y ajustar las transformaciones cuando el dom cambia de una manera razonable todavía me está eludiendo. Esto se siente mal porque estoy a la deriva en la dirección de escribir mi propio gestor de diseño en javascript solo para obtener este efecto.

Author: Community, 2012-06-02

3 answers

El problema que noté es que cuando el elemento escala, el navegador cambia su relación de píxeles, no la cantidad de píxeles. El elemento es más pequeño pero no cambia su tamaño de píxel real en DOM. Debido a eso, no creo que exista una solución de solo CSS.

Pongo el elemento escalable en el contenedor que mantiene la misma relación de píxeles que el resto de los elementos. Usando Java Script simplemente cambio el tamaño del contenedor. Todo sigue basado en CSS3 transform: scale. Tal vez el código JS podría ser más simple, pero ahora es todo sobre la idea (solo una prueba de concepto);) Juguetea con dos ejemplos: 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');
}​
 37
Author: Sófka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-20 01:22:05

Finalmente descubrí una solución simple y es muy similar a donde empecé.

Admitiré que se necesitaron muchos intentos para resolver esto (durante el último año más o menos) para finalmente resolverlo. En realidad estaba buscando una respuesta a esta pregunta. He tenido problemas con esto antes. De todos modos, empecé a trabajar en esto de nuevo y encontré una solución simple.

De todos modos, el truco era usar el margen inferior junto con el script CSS calc.

Además, debe tener que tener la altura absoluta declarada para el objeto (div). No creo que esto funcione si el div puede expandirse, de lo contrario, después de muchos intentos y horas en esto finalmente tengo una solución muy simple, limpia y funcional.

Así, por ejemplo, altura = 300px (no 30%, o ninguna altura). Incluso establecería overflow - y = hidden si puedes. Si quieres que el div crezca (no tenga una altura absoluta), creo que necesitarás JavaScript. No lo intenté. De lo contrario esto está funcionando correctamente en todas las pruebas.

Estoy seguro de que esto funcionará bien con SASS o MENOS, ya que podría declarar algunas de las cosas repetidas usando variables en el CSS. Aún no lo he intentado.

Algunos comentarios importantes sobre mi solución:

  1. Observe que estoy usando la altura del objeto y un método CSS calc junto con el tamaño de transformación para calcular el margen inferior.
  2. Codifiqué por colores mi trabajo para ayudar a mostrar lo que está sucediendo.
  3. el wrap div tiene relleno solo para mostrar que está funcionando. Son absolutas y no afectan al objeto interno.
  4. Si estabas creando una página responsiva, como yo, puedes usar media queries vs classes para obtener el mismo efecto.
  5. Estoy usando transform-origin: 50% 0; Este código terminó no siendo tan importante como originalmente pensé que sería. Estoy usando el margen inferior para resolver los problemas que había tenido.
  6. En este ejemplo, solo estoy centrando el div en el div padre. Si necesita colocar el div, es un poco más complicado, pero una solución similar funcionará. No lo intenté.
  7. Podría usar esta misma solución con jQuery vs CSS. La idea es la misma. Buscaría todos los elementos con la transformación en la página y agregaría el CSS según sea necesario.

También he configurado un Codepen para probar esto con y para compartir.

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
Author: Nathan Sharfi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-28 02:33:48

Tuve que modificar mi código un poco desde arriba.

Ahora funciona en todos los lados (no solo hacia arriba y hacia abajo). Veo una pequeña edición, creo que es un problema CSS (podría agregar otro píxel al código para resolver esto).

Aquí está el enlace Codepen de trabajo. Siéntete libre de avisarme si lo usas. Se sintió bien finalmente resolver esto.

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

Ps. Estoy bifurcando mi codepen para agregar un móvil CSS.

.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
Author: Nathan Sharfi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-28 02:33:12