Cómo escalar para que el contenedor también crezca y ocupe espacio


Así que tengo un contenedor que quiero escalar hacia arriba y hacia abajo (acercar y alejar), pero también tener su forma expandida/encogida para ocupar espacio en lugar de superponer otras cosas.

Actualizar

Hay una imagen con la que hay absolute divs que se colocan en coordenadas, deben conservar sus posiciones relativas al dimensionar hacia arriba y hacia abajo (de ahí por qué estoy usando escala).

var b = document.getElementById("outer");
var scale = 1;

function increase() {
  scale += 0.1
  b.style.transform = `scale(${scale})`;
}

function decrease() {
  scale -= 0.1
  b.style.transform = `scale(${scale})`;
}
#outer {
  overflow-x: auto position: relative;
  transform-origin: left top;
}

.pointer {
  width: 20px;
  height: 20px;
  background-color: orange;
  position: absolute;
}

#a1 {
  top: 50px;
  left: 150px;
}

#a2 {
  top: 150px;
  left: 50px;
}

#a3 {
  top: 250px;
  left: 550px;
}
<div>
  <button onclick="increase()">Increase</button>
  <button onclick="decrease()">Decrease</button>
</div>
<div id=outer>
  <img src="http://via.placeholder.com/600x350" />
  <div id="a1" class='pointer'>
  </div>
  <div id="a2" class='pointer'>
  </div>
  <div id="a3" class='pointer'>
  </div>
</div>
<div>
  please don't cover me
</div>

Preferiría no tener bibliotecas de terceros (al lado jQuery) pero puede considerar.

Author: Paolo Forgia, 2017-09-11

6 answers

CSS3 scalelas transiciones funcionan como que. Desafortunadamente, escalar superpondría otros elementos ya que elimina el contenido del contenedor del flujo creando un nuevo contexto de apilamiento (esencialmente colocando todo su contenido posicionado relativo al contenedor) - consulte la descripción del documento pertinente:

Si la propiedad tiene un valor diferente a none, un contexto de apilamiento será creado.

Fuente: MDN

Vea una demostración a continuación escalando todos los elementos por fuerza bruta:

var b, scale = 1, offset, pointers;

window.onload = function() {
  b = document.getElementById("outer");
  offset = b.getBoundingClientRect();
  pointers = Array.prototype.map.call(b.querySelectorAll('.pointer'), function(e) {
    return {
      el: e,
      offset: e.getBoundingClientRect()
    }
  });
}

function increase() {
  scale += 0.1;
  scaleIt();
}

function decrease() {
  scale -= 0.1;
  scaleIt();
}

function scaleIt() {
  b.style.width = scale * offset.width + 'px';
  b.style.height = scale * offset.height + 'px';
  Array.prototype.forEach.call(pointers, function(e) {
    e.el.style.width = scale * e.offset.width + 'px';
    e.el.style.height = scale * e.offset.height + 'px';
    e.el.style.top = scale * e.offset.top + 'px';
    e.el.style.left = scale * e.offset.left + 'px';
  });
}
#outer {
  /*overflow-x: auto;*/
  position: relative;
  transform-origin: left top;
}
.pointer {
  width: 20px;
  height: 20px;
  background-color: orange;
  position: absolute;
}
#outer > img {
  height: 100%;
}

#a1 {
  top: 50px;
  left: 150px;
}
#a2 {
  top: 150px;
  left: 50px;
}
#a3 {
  top: 250px;
  left: 550px;
}
<div>
    <button onclick="increase()">Increase</button>
    <button onclick="decrease()">Decrease</button>
</div>
<div id=outer>
  <img src="http://via.placeholder.com/600x350" />
  <div id="a1" class='pointer'>
  </div>
  <div id="a2" class='pointer'>
  </div>
  <div id="a3" class='pointer'>
  </div>
</div>
<div>
    please don't cover me
</div>
 21
Author: kukkuz,
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
2017-09-15 00:15:32

Lo probé con el ancho y el alto, creo que funcionará de la misma manera que quieras, agrega una pequeña animación y puedes usarla.

var b = document.getElementById("outer");
var b_width = document.getElementById("outer").offsetWidth;
var b_height = document.getElementById("outer").offsetHeight;

function increase()
{
    b_width += 10
    b_height += 10
    b.style.width = b_width+"px";
    b.style.height = b_height+"px";
}

function decrease()
{
    b_width -= 10
    b_height -= 10
    b.style.width = b_width+"px";
    b.style.height = b_height+"px";
}
#outer {
  background-color: red;
  padding: 1em;
  height: 80px;
  width: 80px;
  transform-origin: left top;
}

#inner {
  background-color: yellow;
  height: 50px;
  width: 100%;
}
<div>
    <button onclick="increase()">Increase</button>
    <button onclick="decrease()">Decrease</button>
</div>
<div id=outer>
  <div id=inner>
  </div>
</div>
<div>
    please don't cover me
</div>
 6
Author: Awsme Sandy,
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
2017-09-11 11:56:16

Editar: lo siento se apresuró, me refería a actualizar el contenedor

Puede agregar un contenedor. Almacene el tamaño inicial de este contenedor. Ahora cuando escala en el elemento interno, actualice el contenedor ancho/alto a la misma proporción.

var a = document.getElementById("outer-wrapper");
var b = document.getElementById("outer");
var scale = 1;
var aWidth = a.offsetWidth;
var aHeight = a.offsetHeight;

function increase()
{
    scale += 0.1
    b.style.transform = `scale(${scale})`;
    a.style.width = `${scale * aWidth}px`;
    a.style.height = `${scale * aHeight}px`;
}

function decrease()
{
    scale -= 0.1
    b.style.transform = `scale(${scale})`;
    a.style.width = `${scale * aWidth}px`;
    a.style.height = `${scale * aHeight}px`;
}
#outer {
  position: relative;
  transform-origin: left top;
}

.pointer {
  width: 20px;
  height: 20px;
  background-color: orange;
  position: absolute;
}

#a1 {
  top: 50px;
  left: 150px;
}
#a2 {
  top: 150px;
  left: 50px;
}
#a3 {
  top: 250px;
  left: 550px;
}
<div>
    <button onclick="increase()">Increase</button>
    <button onclick="decrease()">Decrease</button>
</div>
<div id="outer-wrapper">
  <div id="outer">
    <img src="http://via.placeholder.com/600x350" />
    <div id="a1" class='pointer'>
    </div>
    <div id="a2" class='pointer'>
    </div>
    <div id="a3" class='pointer'>
    </div>
  </div>
</div>
<div>
    please don't cover me
</div>
 5
Author: sahbeewah,
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
2017-09-15 02:03:13

Si su caso de uso es tan simple como su ejemplo, creo que puede estar complicando demasiado las cosas. Si necesita que sus elementos secundarios mantengan su posición relativa con respecto al padre, solo necesita definir sus ubicaciones utilizando unidades relativas en lugar de unidades absolutas y dejar que el motor de renderizado CSS haga su trabajo.

Aquí he establecido el ancho del padre (en lugar de la escala), tengo el tamaño automático de la imagen dentro del contenedor para establecer su altura, y he utilizado porcentajes en lugar de pixel.

var b = document.getElementById("outer");
var width = 600;

function increase() {
  width = width * 1.1
  b.style.width = width + 'px';
}

function decrease() {
  width = width / 1.1
  b.style.width = width + 'px';
}
#outer {
  position: relative;
}

#outer img {
    width: 100%;
    height: auto;
}

.pointer {
  width: 3.333%;
  height: 5.714%;
  background-color: orange;
  position: absolute;
}

#a1 {
  top: 8.333%;
  left: 14.285%;
}

#a2 {
  top: 42.857%;
  left: 8.333%;
}

#a3 {
  top: 71.428%;
  left: 91.667%;
}
<div>
  <button onclick="increase()">Increase</button>
  <button onclick="decrease()">Decrease</button>
</div>
<div id=outer>
  <img src="http://via.placeholder.com/600x350" />
  <div id="a1" class='pointer'>
  </div>
  <div id="a2" class='pointer'>
  </div>
  <div id="a3" class='pointer'>
  </div>
</div>
<div>
  please don't cover me
</div>
 4
Author: LightBender,
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
2017-09-15 12:54:59

La mejor idea es usar el porcentaje dinámico (%) de altura, anchura y posiciones para cada elemento secundario.

Simplemente dividimos todos los valores de píxeles con ancho o alto según sea necesario y en JS simple cambiamos el ancho del contenedor padre y vemos cómo sucede la magia;)

Por favor, encuentre el código a continuación, los comentarios apropiados agregados cuando los valores cambiaron.

var b = document.getElementById("outer");
var scale = 1;

function increase() {
  scale += 0.1
  // Just change the width of parent
  b.style.width = 600 * scale + "px";
}

function decrease() {
  scale -= 0.1
  // Just change the width of parent
  b.style.width = 600 * scale + "px";
}
#outer {
  overflow-x: auto;
  position: relative;
  transform-origin: left top;
  width: 600px; /* Give div static width for children to position correctly */
}

#outer img {
  width: 100%; /* Set image to stretch full width */
}

.pointer {
  width: 3.33%; /* 20px/600px*100% */
  padding-top: 3.33%;/* Don't use static height */
  background-color: orange;
  position: absolute;
}

#a1 {
  top: 14.28%;/* 50px/350px*100% */
  left: 25%;/* 150px/600px*100% */
}

#a2 {
  top: 42.85%;/* 150px/350px*100% */
  left: 8.33%;/* 50px/600px*100% */
}

#a3 {
  top: 71.42%;/* 250px/350px*100% */
  left: 91.66%; /* 550px/600px*100% */
}
<div>
  <button onclick="increase()">Increase</button>
  <button onclick="decrease()">Decrease</button>
</div>
<div id=outer>
  <img src="http://via.placeholder.com/600x350" />
  <div id="a1" class='pointer'>
  </div>
  <div id="a2" class='pointer'>
  </div>
  <div id="a3" class='pointer'>
  </div>
</div>
<div>
  please don't cover me
</div>

P.d. para mostrar el marcador como caja, establecemos su ancho como relleno superior (que cuando se establece como porcentaje es relativo al ancho del padre) por lo que siempre se muestra como un cuadrado. ;)

Esto, en mi opinión, es mucho más manejable ya que no tiene que procesar cada elemento en JS.

 3
Author: shramee,
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
2017-09-19 04:08:55

Usaría otros atributos que la escala, que a diferencia de la escala afectan el flujo. Puede cambiar la altura, el ancho, el margen, el relleno, el tamaño de la fuente, etc. del div.

Editar: Si realmente desea usar escala para cambiar el tamaño de todo lo que está dentro del elemento de una manera uniforme, puede tener un elemento exterior del que cambie el ancho y el alto, y un elemento interior en el que cambie la escala para que coincida con el ancho y el alto de los elementos exteriores. El elemento externo afectará el flujo del contenido. Pero yo realmente no creo que esto sea deseable, ya que no se verá tan bien de todos modos si solo escala todos los iconos eventuales y el texto dentro del contenedor, es probable que desee mantener una gran cantidad de elementos dentro del contenedor en el mismo tamaño, independientemente del tamaño del contenedor.

 2
Author: Lee Comstock,
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
2017-09-11 12:07:36