Search code examples
csstransformscale

Is it possible to collapse an outer <div> around CSS scaled content?


I have scaled content within an unscaled <div>:

<style>
#outer { 
    background-color: blue; 
    padding: 3px;
}
#inner { 
    background-color: red;
    width: 400px; 
    height: 400px; 
    -moz-transform: scale(0.5);
    -webkit-transform: scale(0.5);
    transform: scale(0.5);
}
</style>

<div id=outer>
    <div id=inner>
    </div>
</div>

It looks like this:

Outer div wapping original inner div size

Here's a JS Fiddle too.

The problem is that outer <div> - following the transform: scale the inner <div> is 200px in size, but the outer <div> still wraps the original size of 400px.

I want to collapse the size of the outer <div> to wrap the scaled content, so it should look something like this:

Outer div collapses to the scaled size with padding

In the application the scale property is changed dynamically, and the div contains content that should scale with it.

Is there any way to do this with CSS?

Failing that is there any way to do this with Javascript?


Solution

  • I've got a solution in Javascript, but it's horrible:

    1. To everything outside of the scaled content it's still its original size.
    2. It's still positioned from its original size/location.

    So we need to dynamically for the parent's size (to get round 1) and then apply a negative offset to the inner content with an absolute position (to get round 2).

    The solution looks something like this:

    function collapseScale(inner, scale)
    {
        var $inner = $(inner);
    
        // Get the width and height pre-scaling, including borders
        var x = $inner.outerWidth(),
            y = $inner.outerHeight();
    
        // Get the scaled width and height
        var xs = x * scale,
            ys = y * scale;
    
        // The offset will be half the difference (either side) of the difference between the original size and the scaled size
        var xo = (xs - x)/2,
            yo = (ys - y)/2;
    
        // Set the parent to be the same size, with no overflow
        $inner.parent().css({
            width: xs,
            height: ys,
            overflow: 'hidden',
            position: 'relative'
        });
    
        // Now absolutelty position the inner with a negative offset
        $inner.css({
            position: 'absolute',
            top: '' + yo + 'px',
            left: '' + xo + 'px'
        });
    }
    

    Here's the fiddle, it appears to work in most of the browsers that support transform: scale.

    Update

    I've found a better way to do part of this - issue (2) above can be alleviated with transform-origin:

    #inner {
        -webkit-transform-origin: left top;
        -ms-transform-origin: left top;
        transform-origin: left top;
    }
    

    That lets me drop the dynamic absolute positioning, but Javascript is still needed to resize the outer content:

    function collapseScale(inner, scale) {
        var $inner = $(inner);
    
        // Set the parent to be the same size
        $inner.parent().css({
            width: $inner.outerWidth() * scale,
            height: $inner.outerHeight() * scale
        });
    
        var scaleCss = 'scale(' + scale + ')'
        $inner.css({
            '-moz-transform': scaleCss,
            '-webkit-transform': scaleCss,
            'transform': scaleCss
        });
    }
    

    Here's the updated fiddle, with a jQuery UI slider to test different scale amounts.