Scale group of images to fit parent width/height - javascript

I have a div with a variously-sized images in it, which is inside of a parent container.
<div id="parentContainer">
<div id="boxToScale">
<img src="http://placehold.it/350x150" />
<img src="http://placehold.it/150x350" />
<img src="http://placehold.it/200x200" />
<img src="http://placehold.it/50x200" />
</div>
</div>
I need to scale #boxToScale so that it fits inside #parentContainer (both width and height-wise), while all of the images inside of it keep their aspect ratios. All of the images should be scaled at the same factor, and remain on the same line.
Here's a jsfiddle showing the setup: http://jsfiddle.net/32sm4/
I've found lots of stuff for scaling a single image proportionally, but not for scaling a group of images proportionally. I don't know beforehand what the size of the images will be, only the size of #parentContainer.
Javascript/jquery solutions are fine if it can't be done with just css. Thanks!
Edit: Here's (roughly) what I want it to look like after #boxToScale has been scaled:

Updated my answer after I saw your picture how you would your pictures be like.
This is based on this jsfiddle.net/7dpHK/2 (charlietfl commented this on your question). It's almost working like you wanted, but it was bugging because of those 4px borders/paddings around images. There was some confusing CSS too.
So you have to calculate your borders like:
var border = $("#boxToScale img").length * 4;
And then just subtract it from parentW:
parentW = $box.width() - border
Working example.
JS:
var border = $("#boxToScale img").length * 4; // 4px padding around every image
var $box = $('#boxToScale'),
parentW = $box.width() - border,
parentH = $box.height(),
imageTotalW = 0,
imageTotalH = 0,
$imgs = $box.children();
$imgs.each(function () {
var img = $(this),
ht = img.height(),
w = img.outerWidth()
imageTotalW += w;
img.data('w', w);
});
var img_width_ratio = parentW / imageTotalW;
$imgs.each(function (idx) {
var $this = $(this),
$prev = $this.prev();
$this.width($this.outerWidth() * img_width_ratio);
});
DEMO
JS:
function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
ratio = Math.min(ratio[0], ratio[1]);
return { width:srcWidth*ratio, height:srcHeight*ratio };
}
imagesInContainer = $("#boxToScale img").length;
var toWidth = $("#parentContainer").width() / imagesInContainer;
var toHeight = $("#parentContainer").height() / imagesInContainer;
$("#boxToScale img").each(function(){
var imageWidth = $(this).width();
var imageHeight = $(this).height();
var getRatio = calculateAspectRatioFit(imageWidth, imageHeight, toWidth, toHeight);
$(this).width(getRatio.width);
$(this).height(getRatio.height);
});
Note: Your 1px border in images may bug this code. See an example here without borders.

#boxToScale {
border: solid blue 1px;
white-space: nowrap;
}
#boxToScale img {
width:20%;
height:auto;
display:inline-block;
}
crazy.. but might work... hope it helps.

Related

How to calculate rotated image position after zoom

I have looked through a lot of answers for similar problems but was not able to find any that actually seem to work for me. My problem is very simple, I have two images occupying the exact same spot, however they are at different angles from each other. When I zoom in or out, the images should still occupy the same spot as each other, however, the rotated image always moves.
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
.building {
width:40px;
height:40px;
position:absolute;
left:100px;
top:100px;
}
</style>
<div style="display:flex;justifty-content:space-evenly;width:80%;">
<div id="container" style="position:relative;width:200px;height:200px;border:1px solid red;">
<img src="image1.png" class="building">
<img src="image2.png" class="building" style="transform:rotate(45deg);">
</div>
<div>Scale:<input id="scale" type="text" onChange="changeScale(this.value)"></div>
</div>
<script>
var oldScale = 1;
function changeScale(_scale) {
var zoom = _scale;
var zdelta = (_scale/oldScale);
oldScale=_scale;
var w = $("#container").width();
var h = $("#container").height();
$("#container").width(w*zdelta);
$("#container").height(h*zdelta);
$(".building").each(function() {
var p = $(this).position();
w = $(this).width()*zdelta;
h = $(this).height()*zdelta;
var r = getCurrentRotation(this);
$(this).css({top: (p.top*zdelta), left: (p.left*zdelta)});
$(this).width(w);
$(this).height(h);
});
}
</script>
I have tried to use the following to adjust the rotated image:
$(this).css({top: (p.top*zdelta)+( (h/2)*Math.cos(r) ), left: (p.left*zdelta)+( (w/2)*Math.sin(r) ) });
as well as
function GetArrowLeftShift(w,degrees )
{
var result = (w / 2) * Math.cos( degrees * Math.PI / 180 );
return result;
}
as well as Math.sqrt( Math.pow(w/2,2)+Math.pow(h/2,2) );
Note: I have tried to make the problem easier by using the center position of the square with equal sized images. The real application with have the images in any position, and their sizes will vary but they will need to retain their exact positions in relation to the other images in the square.
So, since I could never find a good mathematical solution to solve this problem, I have ended up doing the following:
var r = getCurrentRotation(this);
$(this).css({transform:"rotate(0deg)"});
var p = $(this).position();
var w = $(this).width()*zdelta;
var h = $(this).height()*zdelta;
$(this).css({top:p.top*zdelta,left:p.left*zdelta,transform:"rotate(+"r+"deg)"});
In essence, I set the rotation to 0, then scale the image and reposition the image and set its rotation back to its original angle.

jQuery - get tallest image's height, apply other images' height difference to top margin

I'm needing to loop through a series of images (number unknown) and get the tallest image's outerHeight value. Then I need to go through all the other images and get their outerHeight and subtract it from the tallest outerHeight, THEN apply that difference to that image's top margin. The end result will be all images bottom-aligned, and yes, I know this can be accomplished with CSS. Here's what I have so far:
The HTML
<ul class="hlist">
<li>
<figure>
<img src="any-size.jpg" />
</figure>
</li>
</ul>
The jQuery
// This is what I have so far, most likely not right...
function createGrid() {
var imgs = $('.hlist figure > img');
var imgHeight = $(this).outerHeight();
var maxImgHeight = 0;
imgs.each(function () {
maxImgHeight = maxImgHeight > imgHeight ? maxImgHeight : imgHeight;
});
}
createGrid();
So I think maxImgHeight should have the tallest image's height at this point (not certain about that), but beyond this my lack of JS skills start to shine. I believe I need to loop through the images again and test each height against maxImgHeight, then apply that difference to the top margin.
Any help here would be hugely appreciated, especially if it was a well-commented and well-explained bit of help :) thanks!
Try this:
function createGrid() {
var imgs = $('.hlist figure > img');
var maxImgHeight = 0;
imgs.each(function () {
var imgHeight = $(this).outerHeight();
maxImgHeight = maxImgHeight > imgHeight ? maxImgHeight : imgHeight;
});
imgs.each(function () {
var margin = maxImgHeight > $(this).outerHeight() ? (maxImgHeight - $(this).outerHeight()) : 0;
$(this).css("margin-top", (margin + "px"));
});
}
The first each loop looks for the tallest height and stores it in maxImgHeight, as you originally had planned. The second each loop calculates and applies the margin for each image. The conditional assignment will cause the margin-top for the tallest image to be 0.
function createGrid() {
var imgs = $('.hlist figure > img'),
maxImgHeight = 0;
imgs.each(function () {
var imgHeight = $(this).outerHeight(true);
if (imgHeight > maxImgHeight) {
maxImgHeight = imgHeight;
}
});
imgs.each(function () {
this.css('margin-top', (maxHeight - $(this).outerHeight(true)) + "px");
});
}
createGrid();

How can I resize and crop/letterbox image to completely fill div with image whatever resize

This is what I need:
The image must completely fill 100% the area the div covers - left to
right and top to bottom.
the image must not be squashed or streched - just be cropped or
must overflow.
The image must be kept as small as possible, so whatever the resize - you
can still see either the very sides OR the very top and bottom.
The div itself will be adjusting in height and width as both are a percentage of the main window.
I have found a little bit of JavaScript here that is manipulating the image just how I want when the window is resized, but is displaying it in the whole window.
<html>
<head>
<title>test</title>
<script type="text/javascript">
function resizeImage()
{
var window_height = document.body.clientHeight
var window_width = document.body.clientWidth
var image_width = document.images[0].width
var image_height = document.images[0].height
var height_ratio = image_height / window_height
var width_ratio = image_width / window_width
if (height_ratio > width_ratio)
{
document.images[0].style.width = "100%"
document.images[0].style.height = "auto"
}
else
{
document.images[0].style.width = "auto"
document.images[0].style.height = "100%"
}
}
</script>
</head>
<body onresize="resizeImage()">
<img onload="resizeImage()" src="f/a.jpg">
</body>
</html>
Here is a demo
Please don't just answer that all I need is:
<img style="width : 100%;">
This is so much more than that.
It's not too easy to explain but check the demo and drag the corner of the window around and that'll be worth 1000 words...!
Can it (or something like it) be made to work the same way within a % sized div?
I wrote a jQuery plugin that does exactly this. Check out my blog post here and the demo here
jQuery.fn.resizeToParent = function(options) {
var defaults = {
parent: 'div'
}
var options = jQuery.extend(defaults, options);
return this.each(function() {
var o = options;
var obj = jQuery(this);
// bind to load of image
obj.load(function() {
// dimensions of the parent
var parentWidth = obj.parents(o.parent).width();
var parentHeight = obj.parents(o.parent).height();
// dimensions of the image
var imageWidth = obj.width();
var imageHeight = obj.height();
// step 1 - calculate the percentage difference between image width and container width
var diff = imageWidth / parentWidth;
// step 2 - if height divided by difference is smaller than container height, resize by height. otherwise resize by width
if ((imageHeight / diff) < parentHeight) {
obj.css({'width': 'auto', 'height': parentHeight});
// set image variables to new dimensions
imageWidth = imageWidth / (imageHeight / parentHeight);
imageHeight = parentHeight;
}
else {
obj.css({'height': 'auto', 'width': parentWidth});
// set image variables to new dimensions
imageWidth = parentWidth;
imageHeight = imageHeight / diff;
}
// step 3 - center image in container
var leftOffset = (imageWidth - parentWidth) / -2;
var topOffset = (imageHeight - parentHeight) / -2;
obj.css({'left': leftOffset, 'top': topOffset});
});
// force ie to run the load function if the image is cached
if (this.complete) {
obj.trigger('load');
}
});
}
And if you want the image to resize when the window is resized, just bind a resize handler to the window:
$(window).resize(function() {
$('img').resizeToParent();
});
Ok I've been playing around with it:
<html>
<head>
<title>test</title>
<style>
#imgarea {
position:absolute;
right:0px;
height:75%;
width:70%;
top:25%;
}
</style>
<script type="text/javascript">
function resizeImage()
{
var window_height = document.body.clientHeight
var window_width = document.body.clientWidth
var image_width = document.images[0].width
var image_height = document.images[0].height
var area_width = window_width * 0.7
var area_height = window_height * 0.75
var height_ratio = image_height / area_height
var width_ratio = image_width / area_width
if (height_ratio > width_ratio)
{
document.images[0].style.width = "100%"
document.images[0].style.height = "auto"
}
else
{
document.images[0].style.width = "auto"
document.images[0].style.height = "100%"
}
}
</script>
</head>
<body onresize="resizeImage()">
<div id="imgarea">
<img onload="resizeImage()" src="f/a.jpg">
</div>
</body>
</html>
It keeps resizing as the div resizes - as mentioned the div is
resizing with the window - this one keeps working seemlesly.
It seems to be OK across IE9, Fire Fox, Oprea, Chrome, and safari
over xp and 7
so really it answers my question perfectly, its just - now i've seen Christian's centering version i wish i had the know-how to make this do it i've tried a few things but am now stuck. Any Ideas?
P.S. if you dont know the width and height % of the div when you right the script i think it could be got with GetElementById - somehow... beyond me though;)

jQuery image manipulation

I want to draw gridlines of varying frequency on top of a static image in jquery, any suggestions as to the best/easiest approach - thanks!
// Specify the number of boxes
var verticalBoxes = 10;
var horizontalBoxes = 10;
var countBoxes = verticalBoxes*horizontalBoxes;
var imageHeight = img.height();
var imageWidth = img.width();
var boxHeight = imageHeight / verticalBoxes;
var boxWidth = imageWidth / horizontalBoxes;
// #grid needs to be relatively positioned
var grid = $('#grid').detach();
for(i = 0; i < countBoxes ; i++){
grid.append('<div class=\'boxes\'></div>');
}
// This is the absolutely positioned container overlaying the image
$('#grid-container').append(grid);
$('head').append('<style>.boxes {outline:1px solid black; height:'+boxHeight+'px; width:'+boxWidth+'px; float:left; }</style>');
I believe this version is more performant, relies on the CSS box model rather than placing each box individually. However it specifies the number of boxes rather than box size...
Here's a very quick mockup that should help give you an idea of my take
var top = image.top;
var left = image.left;
var width = image.width;
var height = image.height;
var boxHeight = 10; //change this how tall you want each grid box to be
var boxWidth = 10; //same for width
gridtop = top;
gridleft = left;
while (gridtop < top + height); {
gridtop += boxHeight;
$('body').append('<div></div>').css('position', 'absolute').css('width', width).css('top', gridtop).css('left', left);
}
while (gridleft < left + width); {
gridleft += boxWidth;
$('body').append('<div></div>').css('position', 'absolute').css('height', height).css('left', gridleft).css('top', top);
}

Real image width with JavaScript

I have the next function:
function setImagesWidth(id,width) {
var images = document.getElementById(id).getElementsByTagName("img");
for(var i = 0; i < images.length;i++) {
// If the real width is bigger than width parameter
images[i].style.width=width;
//}
}
}
I would like to set the css width attribute of all my img tags to a particular value only when the image real width is bigger than the attribute value. If it is possible, i would like a solution which does not use any particular framework.
images[i].offsetWidth returns 111 for an image of 109px width. Is this because 1px each side border?
Here is, hopefully, enough sample code to give you what you want:
var myImage = document.getElementById("myImagesId");
var imageWidth = myImage.offsetWidth;
var imageHeight = myImage.offsetHeight;
That should give you the numbers you need to derive the solution you want. I think you can write the rest of the code yourself. :)
EDIT: Here, I couldn't help myself - is this what you are after?
function setImagesWidth(id,width) {
var images = document.getElementById(id).getElementsByTagName("img");
for(var i = 0; i < images.length;i++) {
if(images[i].offsetWidth > width) {
images[i].style.width= (width + "px");
}
}
}
#Sergio del Amo: Indeed, if you check out my link you'll see that you want clientWidth instead.
#Sergio del Amo: You cannot, unfortunately, accept your own answer. But you do have an extraneous period in the "px" suffix, so let's go with this, including the clientWidth change:
// width in pixels
function setImagesWidth(id, width)
{
var images = document.getElementById(id).getElementsByTagName("img");
var newWidth = width + "px";
for (var i = 0; i < images.length; ++i)
{
if (images[i].clientWidth > width)
{
images[i].style.width = newWidth;
}
}
}
Careful, it looks like you might rather want clientWidth:
http://developer.mozilla.org/en/Determining_the_dimensions_of_elements
EDIT: Can i accept somehow this answer as the final one?
Since offsetWidth does not return any unit, the .px ending must be concatenated for the css attribute.
// width in pixels
function setImagesWidth(id,width) {
var images = document.getElementById(id).getElementsByTagName("img");
for(var i = 0; i < images.length;i++) {
if(images[i].offsetWidth > width) {
images[i].style.width= (width+".px");
}
}
}
Just in case you, the reader, came here from google looking for a way to tell what is actual image file pixel width and height, this is how:
var img = new Image("path...");
var width = image.naturalWidth;
var height = image.naturalHeight;
This becomes quite usefull when dealing with all kinds of drawing on scaled images.
var img = document.getElementById("img");
var width = img.naturalWidth;
var height = img.naturalHeight;
document.getElementById("info").innerHTML = "HTML Dimensions: "+img.width+" x "+img.height +
"\nReal pixel dimensions:"+
width+" x "+height;
<img id="img" src="http://upload.wikimedia.org/wikipedia/commons/0/03/Circle-withsegments.svg" width="100">
<pre id="info">
</pre>

Categories

Resources