puzzle with pictures

As it was new Years Day and it was too wet to go shift earth in the garden I thought I’d play a bit with Professor Alan’s puzzle square. I’ve had a ‘make your own’ version for years, but you had to chop an image into bits give them special names, etc. Now it works much more easily with any image (try it yourself). This are a couple I made with my own photos:

needs Javascript   needs Javascript

The key is that it is I am now using the CSS clip property which allows you to show selected parts of an image (or in fact any HTML element). This was made a little more complicated due to the fact that the W3C pages for clip give running examples for every other kind of visual effect … but not clip! Googling was a nightmare as it turns up page after page in forums saying “I can’t get clip to work”!

Happily I found seifi.org (a blog that looks like a really great web resource) and a post on Creating Thumbnails Using the CSS Clip Property. This was full of meticulously laid out examples … Mojo Seifi, you are a star!

The critical bit of CSS in the above example (left one) looks like:

div.clip { position:relative; width: 24px; height: 32px; overflow:hidden;
        padding:0px; margin:0px; line-height: 0px; }
div.clip img { position:absolute; }
.clip_r1_c1 {   top: 0px; left: 0px; clip: rect(0px 24px 32px 0px);    }
.clip_r1_c2 {   top: 0px; left: -24px; clip: rect(0px 48px 32px 24px);    }
// .... more the same ...
.clip_r3_c2 {   top: -64px; left: -24px; clip: rect(64px 48px 96px 24px);    }
.clip_r3_c3 {   top: -64px; left: -48px; clip: rect(64px 72px 96px 48px);    }

These styles are applied to image tiles, which look like:

<div class="clip" >
   <img class="clip_r3_c2"  id="img_id_r3_c2"
        src="http://www.comp.lancs.ac.uk/~dixa/big_alan.jpg" 
        width="72" height="96" />
</div>

Notice the outer div and inner img tag. The clip property only works inside an enclosing positioned div (absolute or relative). This is sized for the clipped size of the image (in this case 24×32) and also has “overflow:hidden” otherwise the invisible portions of the image leak out and I have had problems with mouse clicks being attributed to the wrong area. In contrast the inner img tag is give the size of the full unclipped image as the clipping model is about hiding parts of the image not cropping them.

The style for “div.clip img” was just to avoid including the common elements in each clip_r{x}_c{y} style. The clipping itself does not reduce the image size, just makes some parts invisible. So in order to get a small cropped image you have to both clip it and position it relative to the outer div, hence the absolute positioning of the img tag.

The style clip_r{x}_c{y} is for the tile in column x row y. It consists of a clip rectangle in the (very confusing) CSS-standard order top right bottom left. Strictly it should have commas, but there seems to have been problems with this in some versions of IE, so spaces while wrong are safer (this is CSS!). Also it has the top and left set in order to position the clipped area at the top left of the enclosing area – these are negative offset as the image origin needs to be somewhere to the left and above the origin of the enclosing div in order to get the clipped region in the right place.

I used named styles for this rather then generating styles inline as this means I can swop tiles around by simply changing the classes of the image tags 🙂 … and this also works in facebook 🙂 🙂 Also the styles and image ids are actually suffixed with unique tags (e.g. .clip_r1_c1_217906) so that if there are several puzzles on the same page (as there are above), they do not interfere with one anther.

Gotchas:

The W3C pages seem to imply that the right and bottom clip parameters are effectively margins, so that clip ( 10px 10px 10px 10px ) would clip off a 10 pixel margin around the image – this is NOT the case. The seifi.org pages make it clear that these are the actual right and bottom edges relative to the origin, so that clip ( 10px 10px 10px 10px ) means clip to nothing at all!

In addition, the right and bottom mean the margin just outside the clipped region, so that clip ( 10px 50px 30px 5px ) creates a clipped region that starts at pixel 5 on the left and includes pixel 49 but not pixel 50 on the right.

The lineheight property is also essential as without it small images end up with tiny margins. The same thing can happen even with larger images depending on their positioning relative to the line lineheight:0px seems extreme, but does the trick.