mulllhausen
mulllhausen

Reputation: 4435

css crop an image then scale by percentage

I am trying to crop a (non background) image then scale that cropped image by a percentage of the body. The idea I have is to combine all logos and basic graphics on my website into one image so that the browser can cache the image (quicker after the fist download). I then want to scale relative to the body width so that my website will look the same ratio of the page width no matter what the user's monitor size.

This may be easier if I give the html & css then explain it after:

<html>
<head>
    <style>
    body
    {
        padding: 0px;
        margin: 0px;
        width: 100%
    }
    #crop1
    {
        float: left;
        overflow: hidden;
        border: 1px solid red;
        clear: both;
    }
    #crop1 img
    {
        vertical-align: middle;
        margin: -28px 0px -88px -189px; /*top right bottom left*/
    }
    #scale1
    {
        width: 10%;
        border: 1px solid blue;
    }
    #scale1 img
    {
        vertical-align: middle;
        width: 100%;
    }
    #crop2
    {
        float: left;
        overflow: hidden;
        border: 1px solid green;
        width: 100%;
    }
    #crop2 img
    {
        vertical-align: middle;
        margin: -28px 0px -88px -189px; /*top right bottom left*/
    }
    #scale2
    {
        width: 10%;
        border: 1px solid orange;
        clear: both;
    }
    </style>
</head>
<body>
    <img src="http://www.mathleague.com/help/geometry/IMG00088.gif">
    <div id="scale1"><img src="http://www.mathleague.com/help/geometry/IMG00088.gif"></div>
    <div id="crop1"><img src="http://www.mathleague.com/help/geometry/IMG00088.gif"></div>
    <div id="scale2"><div id="crop2"><img src="http://www.mathleague.com/help/geometry/IMG00088.gif"></div></div>
</body>
</html>

I've used the image http://www.mathleague.com/help/geometry/IMG00088.gif in this example. I'm not going to use this image in my website, but the principle will be the same.

So first I just put in the original image, unchanged, to give a size reference while debugging:

<img src="http://www.mathleague.com/help/geometry/IMG00088.gif">

Scaling a separate image down to 10% of the page width works fine:

<div id="scale1">
    <img src="http://www.mathleague.com/help/geometry/IMG00088.gif">
</div>

And cropping the image down to show just the oval shape also works fine:

<div id="crop1">
    <img src="http://www.mathleague.com/help/geometry/IMG00088.gif">
</div>

But I cannot shrink just the oval down to 10% of the page width:

<div id="scale2">
    <div id="crop2">
        <img src="http://www.mathleague.com/help/geometry/IMG00088.gif">
    </div>
</div>

(This crops the oval down to the 10% width, instead of scaling it). Maybe I'm missing some simple css properties on this last line, or maybe I need to add more divs. I'm stuck.

Here is the example image:
Example image

note: the solution must be cross-browser compatible

Upvotes: 0

Views: 2845

Answers (4)

ScottS
ScottS

Reputation: 72271

Updated with working example (margins needed to be percentage)

To do it the way you are headed is going to be a bit more complex than you realize. For your final sizing, you need to readjust the img size based on the inverse of the percentage that the piece takes up on the whole. Then you have to take that and multiply it with the scaling to then multiply the offsets of your margins.

EDIT The following code is far more exact, as I was able to check and correct my math, and calculate based off your easier image (knowing the size and offsets is critical). The new example gets far closer to correct sizing calculations. I've adjusted below to show the math. Come to find out, the calculation is "easier" than I thought, but what I partly failed to account for was that even the offsets top and bottom should be set off the original width of the image, since width is what is scaling the whole image.

#scale1
{
    width: 10%;
    padding: 0;
    margin: 0;
}

#crop1
{
    overflow: hidden;
    border: 1px solid green;
    width: 100%;
    padding: 0;
    margin: 0;
}

/*Image: 300 W x 209 H
  Offsets: oT = 8, oR = 56, oB = 75, oL = 204
  Icon: 40 w x 126 h */

#crop1 img
{
    width: 750% /* 1 / (40 / 300) [inverse of icon width / image width] */;
    vertical-align: middle;
    /*using the offsets, each has a percentage calculated based solely off 
      the image width, then adjusted based off the width % of the img as 
      calculated above */
    margin: -20%    /* (8 / 300) x 7.5 [the 750%] = 100 x 8 / 40*/
            0       /* right margin seems unnecessary, but if found otherwise, the
                 calculation would be (56 /300) x 7.5 = 100 x 56 / 40*/
            -187.5% /* (75 /300) x 7.5  = 100 x 75 / 40*/
            -510%;  /* (204 / 300)  x 7.5  = 100 x 204 / 40*/
    padding: 0;
    border: 0;
}

Upvotes: 1

mulllhausen
mulllhausen

Reputation: 4435

in the end i went for the following method:

<!DOCTYPE html>
<html>
<head>
    <style>
    body
    {
        padding: 0px;
        margin: 0px;
        width: 100%
    }
    #d2
    {
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        position: absolute;
        overflow: hidden;
    }
    #d1
    {
        bottom: 0;
        right: 0;
        width: 251.59%; /*B*/
        height: 152.31%; /*C*/
        position: absolute;
    }
    #d0
    {
        top: 0;
        left: 0;
        width: 100%; /*A*/
        position: absolute;
    }
    #d0 img
    {
        width: 100%;
        float: left;
    }
    #container
    {
        top: 100px; /*final position of the copped section from the page top (in px)*/
        left: 40%; /*final left position of the copped section relative to page width*/
        display: inline-block;
        position: relative;
        width: 30%; /*final width of the copped selection relative to the page width*/
    }
    #dummy
    {
        margin-top: 51.587%; /*R*/
    }
    </style>
</head>
<body>
    <div id="container"><div id="dummy"></div>
        <div id="d2"><div id="d1"><div id="d0"><img src="https://i.sstatic.net/MLWNd.gif"></div></div></div>
    </div>
</body>
</html>

the required inputs are: selection_width, selection_height, selection_top, selection_left and image_width, like so:

image to be cropped and scaled

(image_width is the width of the entire image shown here). so:

selection_top:    34 (just inside the boundary so that we do not show the border)
selection_left:   191 (also just inside the boundary so that we do not show the border)
selection_width:  126 (=317-191 where 317 is also just inside the right-side boundary)
selection_height: 65 (=99-34 where 99 is also just inside the bottom boundary)
image_width:      318

and the required outputs are A, B, C, R (from the comments in the code above). crunching the numbers gives:

A: 100.32%
B: 251.59%
C: 152.31%
R: 51.587%

now the browser window can be un-maximised and resized and the oval will retain its original shape while always being a percentage of the page width and and positioned from the left by a percentage of the page width. in other words, the browser will always look the same width on any screen. note that due to rendering inaccuracies, the border around the oval does appear faintly, even though selection_* specify that the final image will be just inside the shown selection border. for this reason it is best to leave a few pixels between crop selections in the final single image.

now you can put all your theme images for the entire website onto one single image and use this technique to extract the various components and position and scale them as required. this gets around the problem of limited parallel downloads (present in most browsers). i have done an example of all the shapes from this single image, aligned to fit the width of the page here: http://jsfiddle.net/KsNku/ (if anybody comes across a browser which does not render this code like so: https://i.sstatic.net/O50vX.png please let me know and i will edit the css to fix the issue). try resizing the jsfiddle browser window and see how everything appears to remain stationary, without any javascript!!

of course a website entirely consisting of this type of layout would require careful design - the user would not be able to zoom in to anything, unless they can maximise the screen further. this would mean that mobile devices will show the page as absolutely tiny. if this is a problem then the technique can be used in limited areas (eg for images in a popup). if you know that all of your users will not be accessing this site with a mobile device (possibly because you have a separate subdomain for mobile phone users) then this technique should be fine.

Upvotes: 0

trickyzter
trickyzter

Reputation: 1591

At present the image is not inheriting the parent's width, you can be explicit and force this by declaring 'width: inherit;' on the image:

#crop2 img
{
    vertical-align: middle;
    margin: -28px 0px -88px -189px; /*top right bottom left*/
    width: inherit;
}

Forgot to mention, you'll need to re-adjust the margin to accomodate the newly sized image.

Upvotes: 1

Ozzy
Ozzy

Reputation: 8322

Use background-position and background-size properties instead.

Example for you:

<!DOCTYPE html>

<style type="text/css">
.pentagon, .pentagon_large {
    background: url(IMG00088.GIF);
}
.pentagon {
    width: 110px;
    height: 110px;
    background-position: 0px 0px;
    background-repeat: no-repeat;
}
.pentagon_large {
    width: 220px;
    height: 220px;
    background-position: 0px 0px;
    background-repeat: no-repeat;
    background-size: 300%;
}
</style>

<body>

<div class="pentagon">&nbsp;</div>
<div class="pentagon_large">&nbsp;</div>

Output:

Output with this code

Note: With the F12 developer tool in IE9 you can edit the values (see picture) to quickly find the right size.

Upvotes: 0

Related Questions