EPUB3 Reflowable-Fixed Layout Dynamic Sizing of Text to Div - javascript

I have created this jsFiddle:
https://jsfiddle.net/j994tnu2/4/
if(textH < (parentH - deviation) || textH > (parentH + deviation)) {
text.style.transform = "scale(1, " + frameScale + ')';
//alert("transform");
}
https://jsfiddle.net/j994tnu2/5/
if(textH < (parentH - deviation) || textH > (parentH + deviation)) {
//text.style.transform = "scale(1, " + frameScale + ')';
//alert("transform");
}
Version 4 has 1 line uncommented which allows for a tranform: scale() of the div directly containing the text.
Version 5 has this 1 line commented which disallows this to happen.
My concern is that the way I've coded the text to resize is...
textScale1 = 0.78;
textScale2 = 1;
textScale3 = 1.4;
//textScale4 = fontSize2 / fontSize;
//applies the master frameScale once to a single style of a class
fontSize2 = Math.round(10 * fontSize2 * frameScale) / 10;
//uses the relative textScales to this element to style the rest
fontSize = Math.round(10 * fontSize2 / textScale1) / 10;
lineH = fontSize;
margin = Math.round(10 * fontSize2 / textScale2) / 10;
lineH2 = fontSize2;
margin2 = Math.round(10 * fontSize2 / textScale3) / 10;
by manually checking the font-size and margins of every element and changing them to a scale both relative to themselves in the text AND relative to the outer div container size. This is actually the good part which makes the text stay true to itself relative to the original format. However,
The problem I have is the difference between the onload = function and the addEventListener(resize, function). They are coded exactly the same but have "different" results.
If you resize the window you'll see that after about 3 resizes, the text fits the container on an absolute font-size level much more closely and has much less (or none at all) transform: scale() stretching or squashing.
But every time the onload = function gets called, the text will always be way too big or small for the container and will always get stretched or squashed by an unacceptable amount.
How can I code this up to make the font-sizes in the onload = function be true to the starting outer div height?
Thanks for looking into it.
EDIT: It's interesting. Commenting out the onload=function and letting the resize function do the first resize, you will get the exact same result of the onload=function. Which, consistency is good. But why does subsequent resizing increase the accuracy of the font-sizes? Even if I resize up and then back down to near the same spot the text will look less squished and more true to its proportions. The initial resize sucks. Why? How is it possible that it gains in accuracy over time?

So I've kept working at it and saw that the ratio of the outer text div to the inner text div (frameScale in the jsFiddle) would determine the percent deviation at the end. This is what I mean:
However far from 1.0, that frameScale would deviate would determine according to some odd exponential function how far the resultant frameScale was from 1. So if you started from 1 (meaning the outer text div was just as large as the inner text div) then the resultant ratio would also be one. If it was 1.3 then the ratio plummeted to 0.84. If it was 1.6 then it went to 0.72. If it was 2.00 then it went to 0.5 and so on. I couldn't figure it out so I decided to do a workaround.
If resizing it multiple times made the font-sizes more true then I decided to just resize it with the resizeListener function I was already calling. All I needed to do was resize to the grandparent element in the first part of the function and then resize to the parent element in the second part. The one kicker was that the grandparent-to-child height ratio could not be the same as the frameScale (parent-to-child). So I did this:
if(masterScale = frameScale) {
masterScale = masterScale - 0.5;
}
For whatever reason, 0.5 seems to work well. Maybe this will fail in many different situations but for now it's a good workaround. All I did was resize the container twice. Here is the jsFiddle:
https://jsfiddle.net/j994tnu2/6/
EDIT: This doesn't answer the question though. Why is the beginning frameScale's deviation from 1 determine increasing deviations from 1 after the transformation? If I wanted it perfect I could create an if() else if() tower adjusting for this all the way up to some ratio that wouldn't occur naturally:
1.0-1.04 >>> 1.0
1.05-1.29 >>> 0.94
1.3-1.34 >>> 0.84
1.35-1.6 >>> 0.8
1.61-??? >>> 0.7
1.99-??? >>> 0.49
For whatever reason, subtracting the amount needed to make 1.0 for the resultant ratio from the beginning ratio will adjust the resultant ratio to 1; which doesn't make sense. Here is a jsFiddle doing this very thing and with much better results than resizing to the grandparent element:
https://jsfiddle.net/j994tnu2/9/

Related

JS function - Math optimization, off by 1 in some cases

I'm still wet behind the ears with web dev, not the best at math, and have problems moving on when something is still broken. Hopefully you guys can help.
Quick: I'm using Jquery to make some (dynamic in number) divs in my header overlap by 30%, filling the entire width of the container. My current iteration rounds up one too many times, so my last element goes beneath the rest.
I have X elements filling the full width of my header container. Each element overlaps by 30% on either side. In an equation, I can work out the math no problem. Ensuring pixel precision with these numbers has proven more difficult. This is what I'm using to determine the width of each element.
width of element = [container width] / ((.7 * ([# of elements] - 1)) + 1)
left margin of element = [width of element] * .3
I make variables I call extraWidth and extraMargin which are the width and margin % 1 respectively. The default element width I use now is width-(width%1). For every element, I add the extraWidth and extraMargin to running total variables. Any time the total of either of these variables exceeds .5, that particular element has its width or margin set 1 higher than the default.
So I don't run on any longer, here's a JSFiddle with everything necessary to see what I'm dealing with. It runs fine most of the time, but at certain widths I'm 1 pixel too wide.
p.s.
Ran the JSFiddle, didn't work the same way as my live sandbox site, so check that out here. I feel like I included all the necessary bits, but I can't say for sure. On my Chrome, when window size is 575px (among many other widths) it's messed up.
EDIT
It should be noted that I'm making changes to my live site without updating this post. I'm not deleting any functions just yet though, just making new ones/minor alterations to existing ones.
Recursion! Recursion was the most elegant answer (which appears to work in ALL cases) I could come up with.
Iterating through my jQuery object one element at a time and calculating the width and margin based on the remaining container width rather than the whole container width makes this much easier to calculate.
function circleWidth(circles, containerWidth) {
var width = containerWidth / ((.7 * (circles.length - 1)) + 1);
var pxWidth = Math.round(width);
var margin = width * .3;
var pxMargin = Math.round(margin);
$(circles[0]).css({
'width': pxWidth + "px",
'margin-left': "-" + pxMargin + "px"
});
containerWidth -= (pxWidth - pxMargin);
if (circles.length > 1) {
circleWidth(circles.slice(1), containerWidth);
}
}
function circleSize(circles, containerWidth) {
var height = Math.ceil(containerWidth / ((.7 * (circles.length - 1)) + 1));
circles.each(function() {
$(this).css({
'height': height + "px"
});
});
circleWidth(circles, containerWidth);
$(circles[circles.length]).css({
'margin-left': $(circles[0]).css('margin-left')
});
$(circles[0]).css({
'margin-left': 0
});
}
Here's the fiddle with my final result. I'm sure I still have some optimization to do, but at least it's working now.
You have 2 choices:
Calculate pixelMargin as next integer. like:
var pixelMargin = Math.ceil(circleMargin);
or you can use pixelMargin in %.
1st one worked for me.

Math to dynamically shift elements within a div for fit

I’m working on a card game in JS. The player’s cards are dynamically displayed as elements inside a div of a set width. As cards are added to the player’s hand, I’m using right: % and z-index to shift all cards left and have them overlap each other, thereby making room to display more cards within the div (which is set to white-space: nowrap so content inside it won't break to a new line).
I’m using JS to calculate and set the percentage for the cards to shift left, based on each card’s original position and the total number of cards that need to be displayed.
The general plan is working, but I’m having trouble figuring out how to calculate a percentage that will work for any number of cards. Ideally, I'd like the cards to shift left just enough so that no matter how many there are they never extend outside the div but always fill it completely.
I thought the following would work:
function displayHand(cards) {
for(var i = 0; i < cards.length; i++) {
var cardDiv = document.createElement("div");
document.getElementById(“playerHand”).appendChild(cardDiv);
if(cards.length > 7) {
cardDiv.setAttribute("style", "z-index: " + i + "; right: "
+ (i * cards.length) * (0.04815 * cards.length) + "%");
}
}
However, I seem to be getting this formula wrong: (i * cards.length) * (0.04815 * cards.length). (Note that the 0.04815 number came from tinkering and trying to get the percentage right.)
By increasing the 0.04815 number I can make a small number of cards (i.e. 8-12) fit well but a larger number will be shifted much too close together, filling only a small portion of the div. If I decrease the number, 12 cards will overflow the div but a larger number will fit well inside it.
The problem comes from the fact that you don't need the first card to move. Your cards.length factor is too high.
Instead, you need only consider cards.length-1.
For instance, let's say I can fit in two cards exactly. To add a third card, I would need to move the cards left for a total of 1 card width by the time two cards (after the first) have been placed. That is, at 50% and 100%. Adding a fourth card would result in the cards being shifted at 1/3, 2/3 and 3/3. And so on.
So it should simply be a case of moving it by i * (cards.length-1) * (card width here)
OK, I figured it out. The following formula works for any number of elements, shifting them over just enough to make room without ever over- or under-filling the container div: i * (((cards.length - 7) * (0.1428 * 100)) / (cards.length - 1))
Here 7 represents the original number of elements in the container, 0.1428 represents the width of the elements, and -1 represents the first element, furthest to the left, which doesn't move.

Best way to layout a variable number of resizable child elements inside a parent <div>?

I have a fixed-size parent element that can contain a changing number of child elements, all of which need to be displayed at the same size and with a fixed x/y ratio; and all of which need to be displayed as large as possible, without overflowing the size of the parent element. (Got that?)
It'll be a lot clearer if you look at the jsfiddle I created that behaves the way I want it to:
http://jsfiddle.net/knbbd/15/
The key bit is this function here:
function layout(parent, parentHeight, parentWidth, children, ratio) {
var totalArea = parentHeight * parentWidth;
var elements = children.length;
var height = 0, width = 0, area = 0, cols = 0, rows = 0;
for (height = parentHeight; height > 0; height--) {
width = height * ratio;
area = width * height * elements;
cols = Math.floor(parentWidth / width);
rows = Math.ceil(elements / cols);
if (area <= totalArea && cols * width < parentWidth && rows * height < parentHeight) {
break;
}
}
$(children).width(width - 10).height(height - 10);
$(parent).width(parentWidth).height(parentHeight);
}
And it works, but it seems pretty clunky. I keep feeling that there should be a way to do this just in CSS, but CSS layout rules always make my head hurt, and at any rate, I can't wrap my head around what it would be. Or failing that, it seems there ought to be a way to do it that didn't require manually trying a hundred different values before settling on one that works.
Suggestions on how to improve this?
From my research, it doesn't appear that dynamic width/height re-sizing of child elements relative to the parent in order to maintain maximum visibility of child elements is possible without some JavaScript. May need to reword that sentence.
Some ideas.
It seems the child elements could get only so small before they become unusable. Why not determine all the ratios before hand and store them in an array? That would save the calculations of determining the maximum ratio. That's assuming the parent width and height are not dynamic, of course. I made that assumption based on your use of the word "video."
Width Height Max Children
189.5 123 2
138.5 89 4
122 78 6
89 56 12
69.5 43 15
63.5 39 20
56 34 24
48.5 29 30
Another option would be to use a CSS grid framework. Here you could re-assign all the child elements with a particular class once a specific number was reached. This maximum number may need to be determine before hand. Or, there may be a simple way to calculate this max number on the fly.
https://stackoverflow.com/questions/76996/what-is-the-best-css-grid-framework
http://speckyboy.com/2011/11/17/15-responsive-css-frameworks-worth-considering/
http://fluidable.com/
This one has a grid with video ratios: http://www.allapis.com/The-Golden-Grid/golden3.html

Place and fit n of elements between two existing elements

Have two elements with fixed width (in %).
First element positioned left: 0, second element positioned right: 0;
Need to append some N of elements between these two elements, so each of the new appended elements have same width (depending on available space between main elements).
http://jsfiddle.net/hXUyh/1/
The problem is that new elements are positioned NOT accurately (crossing each other or leaving some extra space between) and NOT consistently with different browser's window sizes.
Please help.
I understand that different browsers will give different output, but this script will be limited to Google Chrome use only.
Try this:
$(document).ready(function() {
for (var i = 0; i < 9; i++) {
$('<div/>').appendTo('body')
}
$(window).resize(function() {
var firstWidth = $('#element-0').width();
var r = ($(window).width() - (firstWidth * 2) - 2) / 9;
$('div').slice(2, 11).each(function(i) {
$(this).css({
left: i == 0 ? firstWidth : firstWidth + r * (i),
width: r
})
})
}).resize()
});​
http://jsfiddle.net/yav9Q/
I had similar requirements not too long ago. Without questioning or changing your strategy/code, here is a fiddle showing as close as I can get it:
http://jsfiddle.net/hXUyh/3/
(Note that I haven't catered for resizing as your original code didn't)
The issue that the width() jquery function will round. So your maths will always be a little bit off. I've improved this by using calculations on window.innerWidth, it will be a little bit misaligned because of the floating point widths. Using floating point widths for pixel perfect alignment is not the way to go.
If you want perfect alignment, use padding. Here is an example using the smallest padding possible: http://jsfiddle.net/hXUyh/8/
The maths is much easier if you don't need a border.

jQuery calculation doesn't add up as expected when toggling height

I have the following function for calculating the height of .node. It then takes away the height of a possible image, .node-image, from the height of the .node, and sets a column, .node-content-column to have a height that is the difference (i.e. 500 - 50 = 450; column becomes 450 in height).
function initColumnSizer() {
imageHeight = $('.node-image').outerHeight(true);
resizeHeight = ($('.node').outerHeight() + 75) - imageHeight;
$('.node-content-column').removeAttr('style');
$('.node-content-column').css('min-height', resizeHeight);
$('.node-content-column').css('height', 'auto !important');
$('.node-content-column').css('height', resizeHeight);
}
This function gets called on page load, and resizes .node-content-column as expected.
It also gets called when a div within .node is toggled using jQuery.toggle(), but this calculation returns a larger number everytime, instead of reverting back to the original once this toggle is reverted.
Can anyone see where I am going wrong with this calculation? Or if I am going about it the wrong way?
Thanks in advance!
Karl
1) Maybe the problem is in outerHeight() function (it takes into account padding and border). Try using just height or clientHeight:
var img = document.getElementById('imageid');
//or however you get a handle to the IMG
var width = img.clientWidth;
var height = img.clientHeight;
2) why do you need to cleanup the whole elements' style?
and then you try to assign height = auto, and after that: height = resizeHeight - what's the purpose for that ? check the logic of your code.
outerHeight(true) will return height + padding + border + margin. Possibly, you might want to use height() ?
Most possible is that "larger number everytime" have always constant difference -- for example 75.
May be you just have some dependecies between .node-content-column and .node?
If your nodes like .node-content-column, .node and .node-image are all singles, then it's better to use IDs for them -- not CSS classes.

Categories