JavaScript div resizing with aspect ratio - javascript

I'm writing a little script that allows the user to move and resize a div. I need to keep the aspect ratio and my logic doesn't work.
function resizing() {
var currentHeight = elmnt.offsetHeight;
var currentWidth = elmnt.offsetWidth;
var newHeight = currentHeight + (event.pageY - currentY);
var newWidth = currentWidth + (event.pageX - currentX);
var ratio = currentWidth / currentHeight;
if(ratio < 1) {
newwidth = parseInt(newHeight * ratio);
}
else {
newheight = parseInt(newWidth / ratio);
}
elmnt.style.height = newHeight + "px";
elmnt.style.width = newWidth + "px";
currentY = event.pageY;
currentX = event.pageX;
}
The script kind of works. But unfortunately it doesn't keep the aspect ratio completely correct. Sometimes, when I resize horizontyl only, the old height remains the same, sometimes it works, but one length gets resized with a little offset.
When I resize up and down and up and down again, the lengths gets more and more equal and when it is a proper square, everything is right.
Hwo can I fix my problems? Where is my fallacy?!

Your ratio is wrong I think.
You need to calculate this by taking the old width and dividing by the new width, or old height / new height.
e.g.
var ratio = newWidth / currentWidth;
newHeight = currentHeight * ratio;
Change it about if it is the height that is changing.

I could fiy it.
THANK YOU VERY MUCH!
My problem was that I first, had to track of which axis has more change. The second problem, which I didn't recognized was, that I had BIG problems with rounding issues.
When setting the css size using jQuery, it rounds. And I took the height for ratio calculations every single event.
That means that the inaccuracy was getting more and more bad.
Now I took this into account and figured out a way to get this working very good.
I now do this directly onclick and just update them instead of getting from the element:
currentHeight = $("#dragger").height();
currentWidth = $("#dragger").width();
So thanks again for your help! Here is my final result:
http://jsfiddle.net/julian_weinert/xUAZ5/30/

You have to do this, get the min scale (ratio). The code below is a part of my PHP script, but easily translated to JS. $iSrc = Source and $iDest is destination MAX width/height.
Your problem is you don't get the right ratio. The first line to define the ratio is where your problem will be solved. It gets the lowest ratio of the width or height. You just do width/height and forget height/width. That's why vertical scaling is not correct.
$scale = min($iDestWidth/$iSrcWidth, $iDestHeight/$iSrcHeight);
if($scale >= 1){
$iDestHeight = $iSrcHeight;
$iDestWidth = $iSrcWidth;
}else{
$iDestWidth = floor($scale*$iSrcWidth);
$iDestHeight = floor($scale*$iSrcHeight);
}

replace your if(ratio < 1) block with the following. offsetx and offsety relate to your (event.pageX - currentX) and (event.pageY - currentY):
if (Math.abs(offsetx) > Math.abs(offsety)) {
ratio = currentHeight/currentWidth;
newHeight = currentHeight + (offsetx * ratio);
newWidth = currentWidth + offsetx;
} else {
ratio = currentWidth/currentHeight;
newHeight = currentHeight + offsety;
newWidth = currentWidth + (offsety * ratio);
}
here is a quick jsfiddle of the whole thing in action: http://jsfiddle.net/8TWRV/

Related

Fill window with divs. Div pixel height displayed incorrectly in google chrome. Width works

I want to fill the window size with divs. For a specified div size in px, the screen will be filled as much as it can be, leaving a remainder edge amount of px on the side and bottom. This remainder amount is then divided by the number of cells in the row (or column) and that is then added to the height (or width) of each cell in the row (or column).
For the width this works perfectly but when the same logic is applied to the height, it breaks. Both width and height work in firefox.
Screenshot: http://i.imgur.com/mpDCM0G.png
JSfiddle of making the divs: https://jsfiddle.net/xb82c4zt/
Live: http://conwaygameoflife.heroku.com/
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var size = 100;
// Calculate the number of cells we can fit in the width
//and height (there will be extra space)
w = Math.floor(windowWidth / size);
h = Math.floor(windowHeight / size);
// Calculate the extra space
var widthDiff = windowWidth % size;
var heightDiff = windowHeight % size;
// Add the needed amount of height and width to each cell to fill the window
var widthSize = size + widthDiff / w;
var heightSize = size + heightDiff / h;
// Begin to alter the DOM
var parentDiv = document.createElement('div');
parentDiv.className = 'grid';
for(var y = 0; y < h; y++) {
for(var x = 0; x < w; x++) {
var cellDiv = document.createElement('div')
cellDiv.className = 'cellDiv'
cellDiv.style.height = heightSize + 'px';
cellDiv.style.width = widthSize + 'px';
parentDiv.appendChild(cellDiv)
}
}
document.body.appendChild(parentDiv)
In Chrome (and probably other browsers), height and width pixel values are truncated! See this stackoverflow answer with the related jsFiddle
Precentage values are truncated too, but not as severely. So, to solve this you can convert pixels to percentages as I did in this jsFiddle.
The main thing I added was:
var widthPercent = widthSize / windowWidth * 100;
var heightPercent = heightSize / windowHeight * 100;
Because we're using percentages now, the parent container must have width/height:
parentDiv.style.height = windowHeight + 'px';
parentDiv.style.width = windowWidth + 'px';
And changed the loop to:
for(var x = 0; x < w*h; x++) {
var cellDiv = document.createElement('div');
cellDiv.className = 'cellDiv';
cellDiv.style.height = heightPercent + '%';
cellDiv.style.width = widthPercent + '%';
parentDiv.appendChild(cellDiv)
}
Now this doesn't always work in chrome perfectly. However, it does make it perfect in some cases... basically depends on when (and how drastic) the truncation of percentages is.
After further reflection, it looks like percentages get resolved to fractional pixel values as well... which still get truncated in Chrome. So, let's make our math better, and figure out the biggest non-fractional pixel value we can use... it's actually really easy. See here
Basically, we just floor the values, then center the grid so that we can make it look nice.
edit: wasn't very happy with this answer, so screwed with it some more. Added a function that found the closest multiple of window size and made it so that it would prefer that number. Makes it work in most screen sizes, and has a fallback to the percentage method if it doesn't perfectly work. See here. However, because it relies on a recursive (naive) algorithm to find the closest multiple, it's really easy to screw your browser performance. Limiting to only 5-10 pixels of search space helps. The gist of it:
function closestMultiple(width, size, n, limit) {
if(n > limit) {
return {m: width/size, s:size};
}
if((width % (size+n)) == 0) {
return {m: width / (size+n), s: size+n};
} else if((width % (size-n)) == 0) {
return {m: width / (size-n), s: size-n};
}
return closestMultiple(width, size, n+1, limit);
}
It's very naive and ignores things like "an odd width will never be divisible by an even number"... so there's a ton of room for improvement. Check out this discussion and this discussion for more on this.

jQuery image/window resize--max resize constraint

I currently have a page where I have fixed-size divs that load pages as a user scrolls down (similar to Infinite Scroll) and am currently working on functionality to have the loaded images and containers dynamically resize along with the browser window. My current issue is that I'm currently using $(window).width and $(window).height, which naturally causes the images to resize to the window width.
I was wondering what I can set maxWidth and maxHeight to so that the images don't get resized any greater than their original size? I've been using $(window) just to test the function, but I basically don't want the images to become any larger than their original size. I've tried $(this), but it causes the ratio to be equal to 1, resulting in no resize.
$(window).resize(function () {
imageResize();
});
function imageResize() {
$(".added").each(function () {
var maxWidth = $(window).width();
var maxHeight = $(window).height();
var ratio = 0;
var width = $(this).width();
var height = $(this).height();
if (width > maxWidth) {
ratio = (maxWidth / width);
$(this).width(maxWidth);
$(this).height(height * ratio);
$(this).parent().height(height * ratio);
$(this).parent().width(maxWidth);
} else {
ratio = (maxWidth / width);
$(this).width(maxWidth);
$(this).height(height * ratio);
$(this).parent().height(height * ratio);
$(this).parent().width(maxWidth);
}
});
}
You want to set the max-height and max-width properties to the values you said, this will not force the image to grow further than their original height or width and will minimize it if it is larger.
Use:
$("#imageId").attr("max-width",maxWidth);
$("#imageId").attr("max-height",maxHeight);
I would change the style so:
($this).style.maxWidth = maxWidth;

How to keep track of a position no matter what screen size is?

I'm trying to make a widget that selects an area in the page using JavaScript but the issue is that when it saves the X/Y and Width/Height, it's actually only relevant to that screen size, so if we try to draw that selection on another user's computer, it'll go off the correct position.
On what to rely and how to keep track of an x and y position no matter what the user's screen size is?
obj.offsetLeft and obj.offsetTop will always be relative to the top/left corner which is 0,0.
Center x = window.innerWidth / 2;
Center y = window.innerHeight / 2;
Object width = elementNode.offsetWidth;
Object height = elementNode.offsetHeight;
Position the object in the middle of the screen:
el = document.getElementById('my_div');
el.style.position = 'absolute';
el.style.left = Math.floor((window.innerWidth / 2) - (el.offsetWidth / 2)) + 'px';
el.style.top = Math.floor((window.innerHeight / 2) - (el.offsetHeight / 2)) + 'px';
Working demo: http://jsfiddle.net/4aenr/

offsetHeight and offsetWidth calculating incorrectly on first onclick event, not second

I have written the following script to display a hidden element, then fix it's position to the center of the page.
function popUp(id,type) {
var popUpBox = document.getElementById(id);
popUpBox.style.position = "fixed";
popUpBox.style.display = "block";
popUpBox.style.zIndex = "6";
popUpBox.style.top = "50%";
popUpBox.style.left = "50%";
var height = popUpBox.offsetHeight;
var width = popUpBox.offsetWidth;
var marginTop = (height / 2) * -1;
var marginLeft = (width / 2) * -1;
popUpBox.style.marginTop = marginTop + "px";
popUpBox.style.marginLeft = marginLeft + "px";
}
When this function is called by an onclick event, the offsetHeight and offsetWidth are calculated incorrectly, thus not centering the element correctly. If I click the onclick element a second time, the offsetHeight and offsetWidth calculate correctly.
I have tried changing the order in every way I can imagine, and this is driving me crazy! Any help is very much appreciated!
I am guessing your height and width are not defined on the parent. See this fiddle where it works fine. Boy I'm smart. http://jsfiddle.net/mrtsherman/SdTEf/1/
Old Answer
I think this can be done a lot more simply. You are setting the top and left properties to 50%. This will place the fixed element slight off from the center. I think you are then trying to pull it back into the correct position using negative margins. Instead - just calculate the correct top/left values from the start and don't worry about margin. Here is a jQuery solution, but it can be easily adapted to plain js. I also think your current code won't work if the window has been scrolled at all.
//this code will center the following element on the screen
$('#elementid').click(function() {
$(this).css('position','fixed');
$(this).css('top', (($(window).height() - $(this).outerHeight()) / 2) + $(window).scrollTop() + 'px');
$(this).css('left', (($(window).width() - $(this).outerWidth()) / 2) + $(window).scrollLeft() + 'px');
});

jQuery image hover effect

I'm trying to achieve this effect with jQuery.
I wrote some of the code, but it's buggy (move to the bottom-right corder and you'll see).
check it out
Basically, if there's an already-built jQuery plugin that you know of that does this, I'd be very happy using it, if not, any help with my formula would be appreciated. This is what I get for not paying attention in Maths classes :)
Thanks in advance.
Maikel
Overall I think this is what you're looking for:
$.fn.sexyImageHover = function() {
var p = this, // parent
i = this.children('img'); // image
i.load(function(){
// get image and parent width/height info
var pw = p.width(),
ph = p.height(),
w = i.width(),
h = i.height();
// check if the image is actually larger than the parent
if (w > pw || h > ph) {
var w_offset = w - pw,
h_offset = h - ph;
// center the image in the view by default
i.css({ 'margin-top':(h_offset / 2) * -1, 'margin-left':(w_offset / 2) * -1 });
p.mousemove(function(e){
var new_x = 0 - w_offset * e.offsetX / w,
new_y = 0 - h_offset * e.offsetY / h;
i.css({ 'margin-top':new_y, 'margin-left':new_x });
});
}
});
}
You can test it here.
Notable changes:
new_x and new_y should be divided by the images height/width, not the container's height/width, which is wider.
this is already a jQuery object in a $.fn.plugin function, no need to wrap it.
i and p were also jQuery objects, no need to keep wrapping them
no need to bind mousemove on mouseenter (which rebinds) the mousemove will only occur when you're inside anyway.
Nick Craver beat me to an answer by about 10 minutes, but this is my code for this, using background-image to position the image instead of an actual image.
var img = $('#outer'),
imgWidth = 1600,
imgHeight = 1200,
eleWidth = img.width(),
eleHeight = img.height(),
offsetX = img.offset().left,
offsetY = img.offset().top,
moveRatioX = imgWidth / eleWidth - 1,
moveRatioY = imgHeight / eleHeight - 1;
img.mousemove(function(e){
var x = imgWidth - ((e.pageX - offsetX) * moveRatioX),
y = imgHeight - ((e.pageY - offsetY) * moveRatioY);
this.style.backgroundPosition = x + 'px ' + y + 'px';
});
The huge amount of variables are there because the mousemove event handler has to be as efficient as possible. It's slightly more restrictive, because you need to know the dimensions, but I think the code can be easily altered to work with imgs for which the size can be calculated easily.
A simple demo of this: http://www.jsfiddle.net/yijiang/fq2te/1/

Categories