HTML5 and CSS3 Porthole

UPDATE: Here’s the finished version…

Show an image behind another image on your site…

By no means is this new, noteworthy or otherwise important. However, my solution was fairly simple, so it’s a good candidate for a tutorial.

First off, here’s the demo (I know it doesn’t look good in safari/opera and probably won’t work at all in IE…but it works on the browsers I use):

DEMO

When the user loads the page, they see one image. When they hover over “#zoomer” they see the other image, inside the magnifying glass. It works by masking the second image to a small circle, making the circle follow your mouse and overlaying a magnifying glass (which also follows your mouse). Aight, let’s get into it…

The HTML…

Make a wrapper called “zoomer” and put two of the same sized image wrapped in divs inside. we’ll monitor “zoomer” for mousemove events. Then when someone is moving the mouse over it we’ll adjust the positions of the second image and the magnifying glass.

<div id="zoomer">

                <div class="small-window">
                    <img class="small-image" src="images/small.jpg" alt="small lorizzle" />
                </div>

                <div class="big-window">
                    <img class="big-image" src="images/big.jpg" alt="big lorizzle" />
                </div>

                <img src="images/magnifying-glass.png" alt="" id="mag" />

            </div>

The CSS…

We’ll set the width and height of “#zoomer” to the width and height of our images. Make sure to set the width and height of the second image as well. Then set “#zoomer” to position: relative so we can keep everything contained. Set the images to position: absolute to get them to overlay eachother. Set the second image window to an opacity: zero so we can make sure it doesn’t show on top of the base image. Since we want to set a border radius to the second image, but we also need to set it to position: absolute, we’ll need an image mask because this breaks in chrome (don’t ask how I know that, it just takes years of experimentation). We’ll set the magnifying glass to position: absolute so we can move it around easily with javascript and we’ll add some transitions to the two images so they fade in and out. As the user hovers off “#zoomer” we’ll set the opacity: 0 with javascript and when they hover on, we’ll fade the images and magnifying glass in by setting opacity to 1. The transitions will make it look like its fading in and out.

#zoomer {
    position: relative;
    width: 440px;
    height: 600px;
    margin: 0 auto;
}

.small-image {
    position: absolute;
    top: 0;
    left: 0;
}

.big-image {
    position: absolute;
    top: 0;
    left: 0;
    width: 440px;
    height: 600px;
}

.big-window {
    position: absolute;  /* this breaks overflow: hidden in webkit (chrome/safari)*/
    opacity: 0;

    -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);

    overflow: hidden;

    transition: opacity .4s;
    -moz-transition: opacity .4s;
    -webkit-transition: opacity .4s;
    -o-transition: opacity .4s;

}

#mag {
    opacity: 0;
    width: 219px;
    height: 139px;
    position: absolute;

    transition: opacity .4s;
    -moz-transition: opacity .4s;
    -webkit-transition: opacity .4s;
    -o-transition: opacity .4s;
}

Now for the fun part (Javascript)…

Follow the comments to see what’s going on, I tried to explain it the best I can. Basically, we grab some elements, set up out radius and border-radius based on our radius setting (size of the porthole or viewport). Then we listen for a mouse moving over our “#zoomer” element and grab the position of the mouse. We get the page offset so we can tell where the mouse is relative to our element. We detect if the mouse is still between the bounds of #zoomer and if it is we adjust the magnifying glass and our porthole to follow the mouse. Finally, if the mouse leaves, we fade everything out.

// grab the elements and do some calculations on the size of the "porthole", then set the porthole
                var zoomer = $('#zoomer');
                var porthole = $('.big-window');
                var secondImage = $('.big-image');
                var magImage = $('#mag');
                var diameter = 100;
                var radius = diameter / 2;

                porthole.css({
                    width: diameter,
                    height: diameter,
                    'border-radius': radius + 'px',
                    '-moz-border-radius': radius + 'px',
                    '-webkit-border-radius': radius + 'px',
                    '-o-border-radius': radius + 'px'
                });

                // as soon as someone starts moving the mouse over #zoomer
                zoomer.on('mousemove', function(evt) {

                    // get the page offsets (since we centered our #zoomer)
                    var oLeft = evt.currentTarget.offsetLeft;
                    var oTop = evt.currentTarget.offsetTop;

                    // get the position of the mouse over #zoomer
                    var posLeft = (evt.pageX - oLeft);
                    var posTop = (evt.pageY - oTop);

                    // detect whether or not the mouse has moved past the left or right barriers of #zoomer
                    var limitLeft = ((posLeft - radius) > - radius) ? false : true;
                    var limitRight = ((posLeft + radius) < (440 + radius)) ? false : true;

                    // if we're in bounds of #zoomer
                    if(!limitLeft && !limitRight) {

                        // account for the radius of the "porthole"
                        var posTop = posTop - radius;
                        var posLeft = posLeft - radius;

                        // move the porthole to the mouse's position
                        porthole.css({
                            top: posTop,
                            left: posLeft,
                            opacity: 1
                        });

                        // adjust the second image so it moves opposite the mouse movement (so it looks like its staying still)
                        secondImage.css({
                            top: -1 * (posTop),
                            left: -1 * (posLeft)
                        });

                        // move the magnifying glass with the mouse
                        magImage.css({
                            top: posTop,
                            left: posLeft,
                            opacity: 1
                        });

                    } else {
                        // fade the porthole out
                        porthole.css({
                            opacity: 0
                        });

                        // fade the magnifying glass out
                        magImage.css({
                            opacity: 0
                        });
                    }

                });

That’s it!

Feel free to download the zip file or view the demo (link at the top of the page).

Download

 

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *