Alain
Alain

Reputation: 36954

Is there a way to resize a div, and preserve aspect ratio of its content?

One of my customer wanted a small form, that finally looks like this (please ignore the resizable corner, just some tests):

enter image description here

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.

enter image description here

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

Answers (2)

Alain
Alain

Reputation: 36954

I found a solution. This is really not the cleanest solution ever, but at least it works, even with font-sizes.

enter image description here

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

urbz
urbz

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

Related Questions