Programmatically calculate width and height given area and ratio in JavaScript - javascript

OK, so I need to create an "auto fit" feature when a button is clicked.
Pretend you have an area, which is variable in size due to the responsive nature of the page.
This area can contain a number of rectangles (specified by how many logged in clients there are).
What I need to calculate, programmatically in JavaScript, is the appropriate width and height of each client in order to "fit" them all within that area.
var area = $('.client-container').width() * $('.client-container').height();
var noOfClients = 3; // normally calculated dynamically
// Ratio (1.25:1)
var r1 = 1.25;
var r2 = 1;
How can I work out what width and height I should apply to each client rectangle?

I assume you have full freedom to resize the N clients and that their natural aspect ratio can simply reflect that of the screen. You won't always be able to make all clients the same size without wasting space. Take the square root of N, round it down, and that's your number of rows R. It's also your number of columns except that X=N-R*R clients don't fit. X is less than R. Just choose the bottom X rows and divide them into R+1 columns instead.

Related

How to map a dynamic value to a range with a min and max

Within my IntersectionObserver I am storing the Y position of each target element:
const targetPosition = entry.target.getBoundingClientRect().y;
And when the target is in view, I am applying a translateX() synced with that changing Y position. It essentially works, but I am trying to take that dynamic Y position of the target and map it to a range defined by me so I can more easily control the translateX() (and in both left and right directions).
I thought it might be as simple as
const transformRange = Math.max(Math.min(targetPosition,90),0);
but I understand why that doesn't do what I want (see edit below).
I also have the following working...
function scale (number, inMin, inMax, outMin, outMax) {
return (number - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
const num = targetPosition;
const transformRange = scale(num, -100, 1000, 0, 90);
but I don't want to be required to set a range for the source value as well (inMin and inMax) as that is changing.
The intent is to take that dynamic Y position* of the target element (*user scrolls, Y position decreases as the element approaches top of viewport) and convert that changing value to a range that I define.
EDIT
To provide more clarity, Math.max(Math.min(targetPosition,90),0) does not work because it is only limiting the stored Y position of the target element. I am trying to convert the entire scroll range of that element to an output range I define. For example, as the user scrolls, that element could be at say 658px as it enters the phone's viewport and will continue to decrease until it is out of view at the top of the viewport per the IntersectionObserver which would be somewhere around -103px (but obv will be a different set of numbers for different screens).
That 658px to -103px I need to convert/map/chain to a range I define such as 0-90. So 658 to -103 would be 1:1 with 0 to 90. That 0-90 I will then apply as the translateX().

Decrease element size by a factor then increase by same factor to original size

First of all, I know the root of my problem here is rounding in Javascript, I just don't know how to mitigate for it in this particular instance.
I have an element which I want to allow the user to decrease in height by clicking a 'zoom out' button. The decrease in height will be as a proportion of the element's current height - for example, if the element is 100px high, zooming out with the zoom factor set to 0.3 once would reduce its height to 70px. Zooming out again would reduce its height to 49px, and so on.
There is also a 'zoom in' button on the page. After zooming out, the user should be able to use the 'zoom in' button to increase the element's height again. Critically, each increment of the element's height on zooming back in should match the increments seen when zooming out. For example, if the element's heights on zooming out were: 100, 70, 49, 34.3, 24.01 then when zooming back in again, the element's heights would be 24.01, 34.3, 49, 70 100.
I've created a fiddle demonstrating this functionality here. You'll notice that if you run it and initially click the 'zoom out' button (labelled '-') seven times in a row, then click the 'zoom in' button seven times in a row, rather than returning the element's height to 100px, it ends up as 95.7142...px, and the other heights on zooming in don't match those on zooming out either.
$(function() {
var zoomAmount = 0.3;
var $el = $("#testDiv");
$("#zoomIn").click(function() {
var currentHeight = $el.height();
var newHeight = currentHeight * (1 / (1 - zoomAmount));
//newHeight = Math.round(newHeight * 10000 / 10000);
$el.height(newHeight);
$el.html(newHeight);
if (newHeight >= 100) {
$("#zoomIn").prop("disabled", true);
}
});
$("#zoomOut").click(function() {
$("#zoomIn").prop("disabled", false);
var currentHeight = $el.height();
var newHeight = currentHeight * (1 - zoomAmount);
//newHeight = Math.round(newHeight * 10000 / 10000);
$el.height(newHeight);
$el.html(newHeight);
});
});
I understand that this is caused by an accumulation of rounding in the calculation used to zoom out and then back in, but I don't know how to avoid it. You can see that I've commented out a couple of lines which were attempting to round the element's height to 2 decimal places each time, but they don't help. With them uncommented, the element's height ends up as 96px, which suggests to me that even though I'm rounding to 2dp before setting the value as the element's height, it's still aware of the full, unrounded number somehow and is using it when calculating the next new height.
The only solution I can think of is to store the values of the element's height in an array as I zoom in and out, so that when I zoom in the opposite direction the new height is looked up from the array rather than re-calculated, but that doesn't feel very elegant. Additionally, in my actual application there are dozens of the elements on page at a time, each with a different height, so I'd have to track the height of each one in its own array, which seems even less elegant.
Is there a way to calculate the height of the element as I zoom out and in (in practice you could zoom in then out too, but that requires more code so I simplified it) which will ensure that at each 'zoom increment' the height of the element is always the same?
Use an exponential function, and keep your zoomAmount as an integer (a better name would then be something like zoomLevel). So, instead of modifying a float which will accumulate rounding error, you can simply do zoomAmount++ or zoomAmount-- , and then get the real scale of your elements by elevating a floating-point number to zoomAmount (typically the numbere, you can then adjust your graphics with some constants as you see fit).
For example:
$el.height = base_height * pow(2.7182, zoomAmount)
Make sure you start with zoomAmount = 0, since e^0 = 1.
As no rounding errors exist with integers, you are guaranteed to have consistent zoom levels. Another advantage of this solution is that it has a low memory footprint, since you only need to save a single base height for every element of your document.
You could store the previous values and pop back on zoom in (fiddle).
$(function() {
var values = [];
$("#zoomIn").click(function() {
var newHeight = values.pop();
// ...
});
$("#zoomOut").click(function() {
var currentHeight = $el.height(); // this value gets stored
values.push(currentHeight);
// ...
});
});

banner design creator - measurements

I'm creating a banner design creator (so people would be able to make their own designs with it's own texts, background, images, shapes and so on). I have various of sizes of my producs for example: 800x2000 mm, A4(210 x 297mm), 3300x2200mm.
For painting I'm using html canvas. I stucked on sizing the canvas. What's the best way how to handle different measurements with proper user experience? (canvas with width 3300 would not be the good).
Currently I have this code:
var proportion = variant.width >= variant.height ? variant.width / variant.height : variant.height / variant.width;
canvas.setDimensions({width: variant.width * proportion, height: variant.height * proportion});
This is one way to have your banner creator be responsive to display size:
Calculate the proportional scaling factor required to make your banner fit on the display size.
var displayWidth=1366;
var displayHeight=768;
var bannerWidth=3300;
var bannerHeight=2200;
// calculate the scaling factor required to make the banner fit on the display
var scalingFactor = Math.min((displayWidth/bannerWidth),(displayHeight/bannerHeight));
Resize your canvas (it now fits the display but has the same proportions as your banner).
canvas.width=bannerWidth*scalingFactor;
canvas.height=bannerHeight*scalingFactor;
Apply a background color to your banner (if desired).
context.fillStyle='Gainsboro';
context.fillRect(0,0,canvas.width,canvas.height);
Apply the scaling factor to your actual banner text font size.
// Scale a real banner 300pt font to display proportionally on the canvas
// The text on the canvas will be proportionally sized to the real banner size
var fontSizeInPoints=300;
fontSizeInPoints*=scalingFactor;
context.font=fontSizeInPoints+'pt Verdana';
Let the user position text on the banner.
// draw the text "Fun!" at the mouse position
context.textAlign='left';
context.textBaseline='top';
context.fillText('Fun!',mouseX,mouseY);
After the user has positioned their text on the scaled-down canvas, you can convert their mouse position back to "real world" coordinates by dividing the mouse coordinates by the scaling factor.
// convert mouse coordinates back to coordinates on the real banner size
var realBannerX = mouseX/scaleFactor;
var realBannerY = mouseY/scaleFactor;

Zoom-in and Zoom-out image when Mouse Scroll

I want to zoom-in and zoom-out image on mouse scroll in HTML. There are multiple img tag without ID. So how can I do it using JavaScript or Ajax?
Just throwing the answer for the ones that will search for an answer to this question.
First, you will need to find a system to detect the mouse scroll.
If you are courageous, you can develop it yourself.
If you're not, you can find some pretty good libraries (ex : MouseWheel with JQuery).
Next, you will find another two ways to zoom in and out.
Easy way
First, let's cheat a bit.
When you will have to zoom, just multiply the height and width of your image by a factor you will decide.
To have height and width into a variable (JQuery)
var height = $('#image').height();
var width = $('#image').width();
For each scroll you will receive, you will only have 2 choices.
Once you are able to know if the mousewheel goes up or down, you will just have to do something like this (JQuery)
height *= 2;
width *= 2;
This way, by doubling the size of your image, you will have the impression to zoom in.
Less easy way
If you want to zoom in as you would do in a GMap object, you can do something like that.
var firstHeight = $('#image').height();
height *= 2;
width *= 2;
scalechange = (actualHeight / firstHeight) - 1;
offsetX = -(coordX * scalechange);
offsetY = -(coordY * scalechange);
$("#image").css('top', offsetY + 'px');
$("#image").css('left', offsetX + 'px');
First, you have to have the first height of your image.
Next, you will double the size of your image (zoom effect).
Next step is to calculate the scalechange. You will be able to find multiple explanations and many way to calculate it, my method is as good as another.
The two offsets presented are the new positions that your image will adopt (simple factor calculation, it's like making x percent on a price).
Last part is to set the new values of your image.
In the end, you will be able to zoom and unzoom with ou without centering the image at your mouse position.
Be careful : The calculation above in only to zoom-in. You will have to do some maths to get the zoom-out!
Go further ?
Another way to go further would be to place your image in a div.
<div id="imageContainer" style="overflow:hidden;">
<img id="image" src="YourImage">
</div>
By setting
"overflow:hidden;"
to your div, your image will zoom.
But everything that will overflow your div will be hidden.
If you set your div to the original size of your image, like this (JQuery)
$("#imageContainer").css('height', $('#image').height());
$("#imageContainer").css('width', $('#image').width());
Then you will have an image displayed that will always be at the same size, but your zoom will be effective.
If you combine this to a drag'n'drop method, you have a GMap object-like (zoom in-out, moove the zoomed image, ...)
Hope it will help someone!

How to create CSS/JavaScript circles grid

I need to do something like this:
This may look quite easy, but there are some requirements:
- the width of the containing div should depend on the text length (is it possible at all in CSS?)
- all circles should be positioned randomly - this is the most diffucult part for me.
As I'm using border-radius for creating circles (setting height, width and border-radius of 50%) I try to create some kind of grid in JavaScript where I iterate through each element and get its dimensions. Then I get the position of previous element (if any) and add them to the current element dimensions. Additionally, adding some margins will help avoid collisions. Is it correct approach?
I'm just looking for a suggestion how to solve my two issues.
Circles that scale based on size of content.
This is something you will need to solve first, because you wont be able to place them anywhere without first knowing their dimensions.
Naturally the size of a DIV expands first by width, then by height. That is, the maximum width of a container must first be utilized before moving on to the height constraint. Because of this, making a circle scale with equal radius may prove to be quite difficult without using a relative averaging.
Relative averaging is finding the average dimensions of your height / width based of the exhisting area of the contianer bounding your content. For example:
The width and height of the DIV bounding your content can be detected with javascript. Let's say youve discovered those properties too be 200px x 20px respectively.
Your total area is width * height so 4000px; But we are trying to acheive a square so we can apply rounded corners and form a rounded circle. We want to find dimensions of a rectangle that will be equal to the same area and then apply those new dimensions.
To acheive the same area with an equal width * height you can do something like:
√ 4000 = 63.2455532
Thus: 63.2455532 x 63.2455532 = 4000
Random placement of DIVs, and avoid collisons between DIVs.
After finding dimensions, you will be able to use a rand on your (X,Y) coordinates for the placement. Push these coordinates and radius onto an array. Use recursion too place the remaining circles on collsion failures. A collision failure would come from an element that has overlapping (X,Y)+radius relative too elements in the array that were pushed successfully.

Categories

Resources