Reputation: 36954
One of my customer wanted a small form, that finally looks like this (please ignore the resizable corner, just some tests):
Given to his requirements, I needed to fit it to the background image, so 494px. But, he integrated it on a widget-style app, where every widget can be resized.
For this project specifically, I think I can work with max-width
property and play with percentages; but I was wondering if there is a way to resize a div and its contents, so everything preserves aspect ratio. In such way, if I reduce the div's width / height, I will get smaller text and margins.
I did not found any jquery plugin to do that, so I tried to develop a proof of concept to check the main difficulties (see jsfiddle and/or the code below). That's a bit buggy, but you can see what I mean by resizing slowly the black div.
So here is my question:
Is there a way to resize a div, preserving aspect ratio of its content?
You can see the following fiddle: http://jsfiddle.net/8Vhn3/
Code:
CSS
#container {
width: 200px;
height: 200px;
border: 5px solid black;
}
#text {
font-size: 24px;
margin-left: 10px;
}
#some-content {
width: 50px;
height: 50px;
border: 3px solid red;
}
HTML
<div id="container">
<p id="text">some text</p>
<div id="some-content"></div>
</div>
JavaScript (jQuery, jQuery UI)
$(document).ready(function() {
$('#container').resizable({
resize: function(e, ui) {
var el = ui.element;
el.super_resize(ui.originalSize, ui.size);
}
});
});
;(function($, window) {
$.fn.super_resize = function(originalSize, currentSize) {
var that = $(this);
if (that.data('last-width') === undefined) {
that.data('last-width', originalSize.width);
}
var lastWidth = that.data('last-width');
if (that.data('last-height') === undefined) {
that.data('last-height', originalSize.height);
}
var lastHeight = that.data('last-height');
var ratio_w = currentSize.width / lastWidth;
var ratio_h = currentSize.height / lastHeight;
that.data('last-width', currentSize.width);
that.data('last-height', currentSize.height);
var cssPropertiesWidth = [
'width', 'margin-left'
];
var cssPropertiesHeight = [
'height'
];
var cssPropertiesAvgRatios = [
'border', 'font-size'
];
var recursiveResize = function(data, ratio_w, ratio_h) {
$.each(cssPropertiesWidth, function(i, property) {
var oldValue = data.css(property);
if (!oldValue) {
return true;
}
var newValue = parseInt(oldValue) * ratio_w;
data.css(property, newValue + 'px');
});
$.each(cssPropertiesHeight, function(i, property) {
var oldValue = data.css(property);
if (!oldValue) {
return true;
}
var newValue = parseInt(oldValue) * ratio_h;
data.css(property, newValue + 'px');
});
$.each(cssPropertiesAvgRatios, function(i, property) {
var oldValue = data.css(property);
if (!oldValue) {
return true;
}
var newValue = parseInt(oldValue) * ((ratio_w + ratio_h) / 2);
data.css(property, newValue + 'px');
});
data.children().each(function() {
recursiveResize($(this), ratio_w, ratio_h);
});
}; // recursiveResize
recursiveResize($(this), ratio_w, ratio_h);
}; // $.fn.super_resize
})(jQuery, window);
Upvotes: 0
Views: 1654
Reputation: 36954
I found a solution. This is really not the cleanest solution ever, but at least it works, even with font-sizes.
I first did my widget with defined sizes in pixels, on a simple style.css file:
CSS
.imc-error {
width: 95%;
color: #FF0185;
font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
border: 1px solid #FF0185;
padding: 5px 5px 5px 5px;
text-align: center;
margin-bottom: 10px;
}
Then, I developped a script that takes the whole CSS file, and generates jquery instructions to replace all "NNpx" by a dynamic value, for example:
JavaScript / jQuery
$('.imc-error').css('font-size', Math.max(ratio * 15, 8) + 'px');
$('.imc-error').css('border', ratio * 1 + 'px solid #FF0185');
$('.imc-error').css('padding', ratio * 5 + 'px ' + ratio * 5 + 'px ' + ratio * 5 + 'px ' + ratio * 5 + 'px');
$('.imc-error').css('margin-bottom', ratio * 10 + 'px');
Then, I just needed to calculate my ratio
variable, using the formula:
$('#container').width() / 494;
494
being my original width.
Here is the code if you feel interested.
PHP
$file = "css/style.css";
$maxFontSize = 8;
$out = array();
$content = str_replace(array("\n", "\r", "\t"), '', file_get_contents($file));
preg_match_all("|\.([^\{]+)\{([^\}]+)\}|", $content, $out, PREG_PATTERN_ORDER);
$count = count($out[0]);
for ($i = 0; ($i < $count); $i++)
{
list($name, $content) = array(trim($out[1][$i]), $out[2][$i]);
$propsValues = explode(';', $content);
foreach ($propsValues as $propValue)
{
if (empty($propValue))
{
continue ;
}
$propValue = explode(':', $propValue);
$property = trim($propValue[0]);
$values = explode(' ', trim($propValue[1]));
$needProportion = false;
$newValue = null;
foreach ($values as $value)
{
if (!is_null($newValue))
{
$newValue .= ' ';
}
if (substr($value, -2) == 'px')
{
$needProportion = true;
if ($property == 'font-size')
{
$newValue .= "' + Math.max(ratio * " . intval($value) . ", {$maxFontSize}) + 'px";
}
else
{
$newValue .= "' + ratio * " . intval($value) . " + 'px";
}
}
else
{
$newValue .= $value;
}
}
if ($needProportion)
{
$newValue = substr($newValue, strlen("' + "));
echo "\$('.{$name}').css('{$property}', {$newValue}');<br/>";
}
}
}
This method is not really maintainable, as you need to maintain both css and javascript files. Use this way wisely.
Upvotes: 0
Reputation: 2667
The key is padding.
Padding is dependent upon the WIDTH of the containing element when defined in %.
For example, if padding-top: 10%;
you are actually saying that the padding top should be 10% of the WIDTH of the specific element.
width: 20%;
height: 0;
padding-bottom: 20%;
The height needs to be zero so that the height is produced from the setted padding.
For further reading, I really recommend this website that covers most alternatives to maintain aspect ratio of the content inside a div:
http://cjwainwright.co.uk/webdev/aspectratio/
Good luck.
Upvotes: 3