So I have a container that I want to scale up and down (zoom in and out) but to also have its expanded/shrunk form to take up space rather than just overlapping other stuff.
Update
There is an image with which there are absolute
divs that are placed in coordinates, they must retain their relative positions when sizing up and down (hence why I'm using scale).
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>
Would rather not have third party libraries (beside jQuery) but may consider.
6 Answers
Answers 1
CSS3 scale
transitions work like that. Unfortunately, scaling would overlap other elements as it takes the contents of the container out of the flow by creating a new stacking context (essentially putting all its contents positioned relative to the container) - see the relevant doc description:
If the property has a value different than none, a stacking context will be created.
Source: MDN
See a demo below scaling all the elements by brute force:
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>
Answers 2
I tried it with the width and height, i think that will works the same way as you want, add a small animation and you can use it.
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>
Answers 3
Edit: sorry rushed it, I meant update the container
You can, add a container. Store the initial size of this container. Now when you scale in the inner element, update the container width/height to the same ratio.
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>
Answers 4
If your use case is as simple as your example, I think you may be over-complicating things. If you need your child elements to maintain their relative position to the parent, you just need to define their locations using relative units rather than absolute units and let the CSS rendering engine do its thing.
Here I've set the width of the parent (rather than the scale), have the image auto sizing withing to container to set its height, and have used percentages rather than pixels.
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>
Answers 5
Best idea is to use dynamic percent (%
) height, width and positions for every child element.
We just divide all pixel values with width or height as required and in JS simple change the width of parent container and watch the magic happen ;)
Please find the code below, appropriate comments added where values changed.
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.S. to show marker as box we set its width as padding top (which when set as a percentage is relative to parent width) so it always shows as a square. ;)
This, in my opinion, is far more manageable as you don't have to process each element in JS.
Answers 6
I would use other attributes than scale, that unlike scale affect the flow. You could change the div's height, width, margin, padding, font-size etc.
Edit: If you really want to use scale to change the size of everything inside the element in a uniform way, you could have an outer element which you change width and height of, and an inner element that you change scale on to match the outer elements width and height. The outer element will affect the flow of the content. But I don't really think this is desirable as it's not going to look that nice anyway if you just scale up all eventual icons and text inside the container, you probably want to keep a lot of elements inside the container at the same size regardless of the container's size.
0 comments:
Post a Comment