How to Make an Image Magnifier in jQuery



Join the Fan Club!

You are currently viewing a premium “Member’s Only” post from the Web Design Fan Club. To continue viewing this awesome content after June 16, you can become a member for the heavily discounted of $36/year or join us on a monthly basis for $9/month after June 16.

Did you read that right? Yes, yes you did. $36 for the year (8 months free) if you become a yearly subscriber before June 16. Seems kind of foolish to pass something like that up, eh?

Joining the Web Design Fan Club grants you access to all the incredible resources and knowledge you need to become an expert web designer. From WordPress themes to premium icon sets and vectors, advanced tutorials to educational resources and files, the Web Design Fan Club delivers. Not only that, but you gain access to interviews with some of the best web designers and developers working today.

moneyback


How to Make an Image Magnifier in jQuery

  1. Introduction and Demo
  2. Setting Up the Function
  3. The CSS
  4. Inner Magnification Elements
  5. Blur and Tint Magnification Elements
  6. Shared Element Manipulation
  7. The Inner Magnification Algorithm
  8. The Blur and Tint Algorithms
  9. Conclusion

Introduction

This is a tutorial for what is the most complicated jQuery program I have written to date. It is a beautiful script with a simple output that has many potential uses. Undoubtedly, it caused me more stress than it should have, as I debated the most efficient method of displaying a cropped image. Should I use background position or image clipping, scaling or thumbnailing? Which of the many options between methods and styles are cross-browser compatible? After days of debate and testing, I found the perfect combination for a code that supports all high-request browsers (including Internet Explorer 6).

This script takes an image and its thumbnail, then lets the user to magnify in on a specific area of that thumbnail. This allows you to condense an otherwise large image into a small space. Below, for example, is a 1024×768 image of a dog condense down to 320×240.

Demo: Image Magnifier Demo

I feel the need to note that this does not magically transform a low-resolution image into a high-resolution image. It requires both a high-resolution and low-resolution version beforehand. It also supports many different forms of customization, which I tried to emphasize with the examples – from custom colors to custom “animations” (inner, blurred, tinted, and blurred tinted). This tutorial will teach you to make these scripts yourself in the ever-beloved jQuery by walking you through the algorithms used.

Setting Up the Function

Of course, the first thing we’ll need to do is set up the function so that we may call the image magnifier easily. In order to create the function, we must first determine its parameters; and in order to determine its parameters, we must first determine what is likely to change on a per-call basis.

The dynamic features of each box are: whether or not it blurs, whether or not it tints, whether or not it magnifies inside the thumbnail (inner), the low-resolution image and its dimensions, the high-resolution image and its dimensions, the border width of the boxes, the border color of each box, and the border style of each box. Considering there are three boxes (the thumbnail, the popup that contains the high-resolution image, and the box that surrounds the mouse to show you which area is magnified), that is a lot of variables!

Thankfully, we can use objects to condense similar variables in a single variable. We are then left with the effect, the low-resolution image, the high resolution image, and the style information of the boxes. Four variables.

var imageMagnifier = function(effect, popup, thumbnail, css)
{
};

An example of these variables being sent to the function:

imageMagnifier(
{borderWidth: 4, blur: false, inner: false, tint: "#ff0000"},
image = {src: "image-magnifier.jpg", height: 768, width: 1024},
thumbnail = {src: "image-magnifier-thumbnail.jpg", height: 240, width: 320},
{
popup: {borderColor: "#906030", borderStyle: "outset"},
thumbnail: {borderColor: "#604020", borderStyle: "solid"}
}
);

Border Width is not included in the CSS for one simple reason: aesthetics and calculability. If the border width of the mouse box (the box that surrounds the mouse when magnifying) is not the same as the border width of the thumbnail, its border cannot overlap the thumbnail border appropriately. I can think of no scenario where the mouse box border would need to differ from the thumbnail’s, and many errors and nuisances occurring as a result of having to change it within every CSS object.

You should also make sure all the effect variables are set. This allows for shorter code. Instead of having to check to see if the variable is defined and true (typeof(effect.blur) != "undefined" && effect.blur), you can merely check to see if it is true.

var imageMagnifier = function(effect, popup, thumbnail, css)
{
var effects = ["blur", "inner", "tint"];
for (var x = 0; x < 3; x++)
{
if (typeof(effect[effects[x]]) == "undefined")
effect[effects[x]] = false;
}
};

Lastly, an extremely important variable needs to be established: the ratio of width to height between the thumbnail and the full-scale image (referred to herein as “popup,” since it pops up). This is necessary to determine both the size of the mouse box and how far to scroll the popup as your mouse scrolls across the thumbnail.

var imageMagnifier = function(effect, popup, thumbnail, css)
{
var effects = ["blur", "inner", "tint"],
ratio = {
height: popup.height / thumbnail.height,
width: popup.width / thumbnail.width
};
for (var x = 0; x < 3; x++)
{
if (typeof(effect[effects[x]]) == "undefined")
effect[effects[x]] = false;
}
};

The CSS

Before getting deep into the algorithms, it won’t hurt to cover and establish the CSS involved with the image magnifier. The static CSS is very short and to-the-point so will not take long to
cover.

First is the very outer element, div.image-magnify, and its three most important inner elements: div.box (the mouse box), div.popup (the full-scale image), and div.thumbnail.

We’ll want to float the thumbnail and popup to the left so that they stack horizontally next to each other.

div.image-magnify, div.image-magnify div.popup, div.image-magnify div.thumbnail { float : left; }

The box, popup, blur effect, and tint effect should be hidden by default, since they do not appear until you mouse over the thumbnail.

div.image-magnify div.blur div, div.image-magnify div.box, div.image-magnify div.popup, div.image-magnify div.tint { display : none; }

The box needs to have a relative position so that it can shift around as the mouse moves.

div.image-magnify div.box { position : relative; }

For aesthetic purposes, we’ll give div.thumbnail the curser of “move.”

div.image-magnify div.thumbnail { cursor : move }

Now to explain the blur “algorithm” and its CSS. It’s a very simple idea, really, made especially easy with jQuery’s built-in translucency handler. You merely take a translucent image (I use 80% visibility for this magnifier) that’s been shifted to the left 3px, and place it over the same translucent image shifted up 3px. Then place those two images over the same translucent image shifted to the right 3px. Then place those three images over the same translucent image shifted down 3px. Seeing partially through each image will result in a blur effect. Here it is one image added at a time:

Now that you understand how it blurs, you’ll easily understand the CSS for it:

div.image-magnify div.blur > div { background-position : -3px 0 }
div.image-magnify div.blur > div > div { background-position : 0 -3px }
div.image-magnify div.blur > div > div > div { background-position : 3px 0 }
div.image-magnify div.blur > div > div > div > div { background-position : 0 3px }

We also have some dynamic CSS [in that it changes on a per-magnifier basis] that is referenced very often in the JavaScript. I’ve compressed it into a quick-access variable since that makes it easier to use each time than typing out each string.

var imageMagnifier = function(effect, popup, thumbnail, css)
{
var cssCommon = {
background: "background-image : url('" + thumbnail.src.replace(/\'/g, "\\\'") + "');",
backgroundLarge: "background-image : url('" + popup.src.replace(/\'/g, "\\\'") + "');",
dimensions: "height : " + thumbnail.height + "px; width : " + thumbnail.width + "px;"
},
effects = ["blur", "inner", "tint"],
ratio = {
height: popup.height / thumbnail.height,
width: popup.width / thumbnail.width
};
cssCommon.thumbnail = cssCommon.background + " " + cssCommon.dimensions;
for (var x = 0; x < 3; x++)
{
if (typeof(effect[effects[x]]) == "undefined")
effect[effects[x]] = false;
}
};

cssCommon.background is the background image of the thumbnail, as it will be needed for div.thumbnail, div.box, and every div involved in blur. It becomes, then, much easier to reference it each time as cssCommon.background than its string equivalent.

cssCommon.backgroundLarge is the background image of the popup. This variable is only used twice (once for the inner algorithm and once for the other effect algorithms), so is not nearly as common as the thumbnail background.

cssCommon.dimensions is merely the dimensions of the thumbnail. Just like the background image, this applies to div.thumbnail and the blur divs. It does not apply to div.box, since its width is determined by the dimension ratio between the low-resolution and high-resolution images; but it does apply to div.tint which will need to overlay the thumbnail.

Lastly in cssCommon, we have cssCommon.thumbnail, which is merely a combination of the background and dimensions.

In summation, these are shorthand CSS applications for:

  • cssCommon.thumbnail: div.blur div, div.thumbnail
  • cssCommon.background: div.box
  • cssCommon.backgroundLarge: div.popup
  • cssCommon.dimensions: div.tint

As the final bit of CSS, each item in div.image-magnify will also lack a repeating background. We only want it to appear the one time. This is especially true for div.blur that has a shifted background position. When it shifts upward 3px, we don’t want those top 3px to appear at the bottom of the div, and so on and so forth for each direction that each blur element shifts.

div.image-magnify div { background-repeat : no-repeat; }

The Elements

Before all else, of course, we need to create the elements in order to assign event handlers to them. The inner magnify algorithm, unfortunately, is not compatible with the blur and tint algorithsm. This is simply due to the fact that the thumbnail is hidden when inner-magnified, so it cannot blur or tint. Because of this, and the position of the elements involved (div.popup being shifted to the left, div.box being non-existent), the inner magnify algorithm is split into an entirely different section of code than the blur and tint algorithms.

var imageMagnifier = function(effect, popup, thumbnail, css)
{
var cssCommon = {
background: "background-image : url('" + thumbnail.src.replace(/\'/g, "\\\'") + "');",
backgroundLarge: "background-image : url('" + popup.src.replace(/\'/g, "\\\'") + "');",
dimensions: "height : " + thumbnail.height + "px; width : " + thumbnail.width + "px;"
},
effects = ["blur", "inner", "tint"],
ratio = {
height: popup.height / thumbnail.height,
width: popup.width / thumbnail.width
};
cssCommon.thumbnail = cssCommon.background + " " + cssCommon.dimensions;
for (var x = 0; x < 3; x++)
{
if (typeof(effect[effects[x]]) == "undefined")
effect[effects[x]] = false;
}
if (effect.inner)
{
/* inner algorithm will go here */
}
else
{
/* blur and tint algorithms will go here */
}
};

Inner Magnification Elements

The most efficient setup for inner magnification is:

<div class="image-magnify inner">
<div class="thumbnail">
<div class="popup"></div>
</div>
</div>

While div.inner-magnify.inner is never used in this tutorial, it is placed there simply for convenience if you desire to add other CSS effects.

Unlike blur and tint, div.popup is going to appear over div.thumbnail. For this, it is appropriately placed within div.thumbnail. Now to port this basic HTML layout to JavaScript to insert it into the function:

if (effect.inner)
{
document.write(
'<div class="image-magnify inner">' +
'<div class="thumbnail" style="' + cssCommon.thumbnail + '">' +
'<div class="popup" style="' + cssCommon.backgroundLarge + " " + cssCommon.dimensions + '">' +
'<\/div><\/div><\/div>'
);
}

Here I went one step further than the basic HTML layout: I applied the dynamic CSS discussed earlier. div.thumbnail receives the background image and dimensions of the thumbnail, and div.popup receives the background image of the high-resolution image and the dimensions of the thumbnail.

Now to put the finishing touches on div.popup. Due to the potential border on div.thumbnail, div.popup will need to be shifted up to the left by the same number of pixels as the border width. Otherwise, it will appear inside of div.thumbnail‘s border, whereas we need it to appear on top of div.thumbnail‘s border.

if (effect.inner)
{
document.write(
'<div class="image-magnify inner">' +
'<div class="thumbnail" style="' + cssCommon.thumbnail + '">' +
'<div class="popup" style="' + cssCommon.backgroundLarge + " " + cssCommon.dimensions + '">' +
'<\/div><\/div><\/div>'
);

// If a border exists, shift div.popup over it.
if (effect.borderWidth > 0)
$("div.image-magnify:last div.popup").css("margin", "-" + effect.borderWidth + "px 0 0 -" + effect.borderWidth + "px");
}

Blur and Tint Magnification Elements

Unlike inner magnification, blur and tint will need a completely separate layout. They need to be designed in a way for them to both work at the same time, since they are compatible with each other. The most efficient setup is:

<div class="image-magnify blur tint">
<div class="thumbnail">
<div class="blur">
<!-- blur background shifted left 3px -->
<div>
<!-- blur background shifted up 3px -->
<div>
<!-- blur background shifted right 3px -->
<div>
<!-- blur background shifted down 3px -->
<div>
<div class="tint"></div>
</div>
</div>
</div>
</div>
</div>
<div class="box"></div>
</div>
<div class="popup"></div>
</div>

Like div.image-magnify.inner, div.image-magnify.blur (not to be confused with div.image-magnify div.blur) and div.image-magnify.tint are not used in this tutorial. They are listed merely for the convenience of custom CSS.

In this, the tinted element is placed on top of the blurred element so as to tint the blur (wheras the four blur images placed above the tint color would make the tint color barely noticeable). The box is placed inside the thumbnail, of course; and the popup is placed next to the thumbnail. Of course, if there is no blur or tint, the blur and tint elements won’t need to exist. This is handled in the JavaScript.

if (effect.inner) { /* ... */ }
else
{
document.write(

// If the image is blurred and/or tinted, apply the blur and/or tint class.
'<div class="image-magnify' + (effect.blur ? " blur" : "") + (effect.tint ? " tint" : "") + '">' +

// Give the thumbnail its background image and dimensions.
'<div class="thumbnail" style="' + cssCommon.thumbnail + '">' +

// If the thumbnail is to be blurred, add the to-be-blurred thumbnails.
(effect.blur ? '<div class="blur"><div style="' + cssCommon.thumbnail + '"><div style="' + cssCommon.thumbnail + '"><div style="' + cssCommon.thumbnail + '"><div style="' + cssCommon.thumbnail + '">' : '') +

// If the thumbnail is to be tinted, add an element of the appropriate color and dimensions.
(effect.tint ? '<div class="tint" style="background-color : ' + effect.tint + '; ' + cssCommon.dimensions + '"><\/div>' : '') +

// If the thumbnail is blurred, close the blur elements.
// The opening and closing elements are separated so that div.tint will be inside of them.
(effect.blur ? '<\/div><\/div><\/div><\/div><\/div>' : '') +

/*
If the thumbnail is neither blurred, tinted, or inner, add a placeholder of the thumbnail dimensions.
If no placeholder exists, div.box will be thumbnail.height pixels higher than if a blur or tint element exists.
The placeholder will allow for div.box to use the same position algorithm regardless of effect used,
instead of having to make an exception for no-effect magnification.
*/
(!effect.blur && !effect.tint ? '<div style="' + cssCommon.dimensions + '"><\/div>' : '') +

// Add the mouse box with the thumbnail background. It needs its own background so that it is not affected by the blur or tint.
'<div class="box" style="' + cssCommon.background + ' height : ' + Math.round(thumbnail.height / ratio.height) + 'px; top : -' + thumbnail.height + 'px; width : ' + Math.round(thumbnail.width / ratio.width) + 'px;"><\/div>' +

// Close div.thumbnail.
'<\/div>' +

// Add the popup with the full-scale background image and same dimensions as the thumbnail.
// Use negative right and bottom margins so that adjascent content is not affected by its position.
'<div class="popup" style="' + cssCommon.backgroundLarge + ' margin-right : -' + (thumbnail.width + effect.borderWidth) + 'px; margin-bottom : -' + (thumbnail.height + effect.borderWidth) + 'px; ' + cssCommon.dimensions + '"><\/div>' +

// Close div.image-magnify.
'<\/div>'
);
}

For those of you wondering about the height and width attributes of div.box, fear not. It was simply too long to explain the comments. As hard as it is to explain mathematics, I shall attempt to do so here.

The box’s height and width need to be preportionate to the high-resolution image. That is to say that if the thumbnail is three times smaller than the popup, then the box needs to be three times smaller than the thumbnail. If the thumbnail is the same size as the popup, then the box needs to be the same size as the thumbnail. In other words, the box needs to be representative of what percentage of the popup is going to be shown.

The ratio variable is a measurement of popup to thumbnail. So a popup of 500 width and a thumbnail of 250 width would have a ratio.width of 2. thumbnail.x / ratio.x (where x is height or width) means that 1 / ratio.x is the percentage of the popup that will be shown. If the ratio of popup to thumbnail is 2:1, then 1/2 of the popup is what will be shown when magnified. For this ratio, the box should be 1/2 of the thumbnail. If the ratio of popup to thumbnail is 5:1, then 1/5 of the popup is what wlil be shown when magnified. For this ratio, the box should be 1/5 of the thumbnail. Thus, the box needs to be 1/ratio.x * thumbnail.x (or thumbnail.x / ratio.x).

Shared Element Manipulation

There are also small manipulations that apply to both the inner, blur, and tint effects. Instead of redundently placing these snippets in both the if and else statements, we’ll execute them outside of the statements. They will need to go after the conditional because div.image-magnify must first exist in order for it to be manipulated.

var imageMagnifier = function(effect, popup, thumbnail, css)
{
/* ... */
if (effect.inner) { /* ... */ }
else { /* ... */ }

// Assign important data that we'll need later.
$("div.image-magnify:last").data({
borderWidth: effect.borderWidth,
popupHeight: popup.height,
popupWidth: popup.width,
thumbnailHeight: thumbnail.height,
thumbnailWidth: thumbnail.width
});

// Apply the sent CSS.
$("div.image-magnify:last div.box, div.image-magnify:last div.popup, div.image-magnify:last div.thumbnail").css("border-width", effect.borderWidth + "px");
if (typeof(css) == "object")
{
for (x in css)
$("div.image-magnify:last div." + x).css(css[x]);
}
};

data() is a very useful jQuery method that allows you to assign various attributes to an element. One cannot assign the attribute (attr()) of popupHeight to div.image-magnify because that is not a valid HTML attribute. Thankfully, jQuery will store it in that element’s data() so that one may access it later via $("#the-element").data("popupHeight"). effect.borderWidth, popup.height, popup.width, thumbnail.height, and thumbnail.width are all variables you will need to access in the future. They will be needed for calculations in various event handlers, which won’t be able to read the data sent to the imageMagnifier() function, so we thus store them in an easy-to-access location: the div.image-magnify‘s data().

Lastly, we apply the CSS. This includes effect.borderWidth and the css variable. Each element gets the same border width, and any custom CSS sent via the fourth parameter (e.g. custom border colors, border styles) will be assigned here as well.

And you’re done! At least with setting up the HTML. All that’s left is the algorithms for displaying specific regions of the box and popup.

The Inner Magnification Algorithm

For easier reading, I will leave out the already-established function and variable declarations and merely discuss the coding that needs to be appended within the if (effect.inner) { } brackets.

We’ll need the trigger to make div.popup appear and disappear as needed. This part is very basic.

$("div.image-magnify:last div.thumbnail")
// When you put your mouse over the thumbnail,
.mouseenter(
function(e)
{
// fade the popup to fully visible within a third of a second.
$(this).children("div.popup").fadeTo(333, 1);
}
)
// When you remove your mouse from the thumbnail,
.mouseleave(
function()
{
// fade the popup to fully invisible within a third of a second.
$(this).children("div.popup").fadeTo(333, 0);
}
);

Lastly, the ever-so-important algorithm itself. This is the part that moves the background of the full-resolution popup to match the background of the low-resolution thumbnail.

$("div.image-magnify:last div.thumbnail")
.mousemove(
function(e)
{

// Get the data stored to the div.image-magnify image.
var data = $(this).parent().data(),

// Get the position of the thumbnail to determine the position of the mouse.
offset = $(this).offset();

// Re-calculate the ratio of popup to thumbnail.
var ratio = {
height: data.popupHeight / data.thumbnailHeight,
width: data.popupWidth / data.thumbnailWidth
},

// Get the X and Y coordinations of the mouse in relation to the thumbnail.
x = e.pageX - offset.left - data.borderWidth,
y = e.pageY - offset.top - data.borderWidth;

// Don't scroll beyond the thumbnail.
// Used to prevent scrolling when the mouse is on the border.
if (x < 0)
x = 0;
if (x > data.thumbnailWidth)
x = data.thumbnailWidth;
if (y < 0)
y = 0;
if (y > data.thumbnailHeight)
y = data.thumbnailHeight;

// Shift the background image of the popup
$(this).children("div.popup").css("background-position", (x > 0 ? "-" + (x * (ratio.width - 1)) + "px" : 0) + " " + (y > 0 ? "-" + (y * (ratio.height - 1)) + "px" : 0));
}
);

For those unaware, I’ll explain the variables used to calculate x and y. e.pageX is the mouse’s horizontal position on the page: the number of pixels the mouse is from the far left of the browser window or frame. offset.left is the thumbnail’s horizontal position on the page: the number of pixels the thumbnail is from the far left of the browser window or frame. By subtracting offset.left and data.borderWidth (the border width of the thumbnail) from e.pageX, you’re left with the mouse’s position in relation to the thumbnail. For example, if your mouse is positioned at the top left of the thumbnail, x will be 0 and y will be 0; if the mouse is positioned at the bottom middle of a 320×240 thumbnail, x will be 160 and y will be 240.

div.popup‘s background positioning is a bit more complicated. The CSS for background position – unlike padding, margin, etc. – is comprised of the left position of the background followed by the top position of the background. Since the thumbnail is smaller than the high-resolution popup, this is where ratio comes into play. For every one pixel the mouse scrolls over the thumbnail, we’ll want to move ratio.x pixels in the popup; e.g. if the popup is three times the size of the thumbnail, we’ll want it to move three pixels for every pixel the mouse moves over the thumbnail. Your algorithm would then be x * ratio.width and y * ratio.height.

Unfortunately, it’s not quite that easy. x * ratio.width will place the pixel in the center of the mouse on the far left of the popup, when we want it in the center of the popup. Using that code, when you place your mouse on the far right of the thumbnail, the background position for the popup will be negative the width of the entire full-resolution image. Take for example, a thumbnail of 320×240 and a popup of 640×480: when your mouse is placed at the 320 x-coordinate of the thumbnail, the background position will be -640px (the mouse position [320] times the ratio [2]). If a 640px image is shifted to the left by 640px, you won’t be able to see it anymore! This isn’t what we want.

So how do we move the position from the far left to the center? You must first note the negative before the value in the CSS. The number we are calculating is how many pixels to the right the image should move; by using a negative, we thus make the image shift to the left. Beware the double negative in the following algorithm. -(ab) actually means -a + b (moved a pixels to the left, then b pixels to the right). The uncondensed algorithm for determining the number of pixels to move the image to the left is this: x * ratio.width - x / data.thumbnailWidth * data.thumbnailWidth. That is to say, the pixel’s position in the full-resolution image (x * ratio.width) minus the percentage of the image scrolled (x / data.thumbnailWidth; we’re at pixel x out of data.thumbnailWidth pixels) times the number of pixels total (data.thumbnailWidth). In English, the further we scroll to the right, the more we’ll want to substract from x * ratio.width, up to 100% of the thumbnail width. When x is equal to data.thumbnailWidth (i.e. the mouse is at the far right of the thumbnail), we’ll be left with x * ratio.width - 1 * data.thumbnailWidth.

This is a fairly hard algorithm to grasp. If you do not understand how it works, it may serve well to toy with the numbers involved and watch the resulting effect for yourself.

Lastly, if the x or y coordinates are 0, simply use a 0 instead of the negative pixel value, because “-0px” is invalid CSS; thus the inline conditional statements, (x > 0 ? "-Zpx" : 0).

The Blur and Tint Algorithms

I will leave out the parts of the code already established to do away with redundancy and pointless scrolling. The following snippets are to be appended to the else { } block of code.

Like the inner algorithm, we must unhide the elements involved when the thumbnail is moused over. Very simple, but slightly longer than the inner algorithm due to the fact that more elements are involved (the mouse box, the blur elements, and the tint elements).

$("div.image-magnify:last div.thumbnail")
.mouseenter(
function()
{
$(this).children("div.box").fadeTo(333, 1);
$(this).find("div.tint").fadeTo(333, 0.5);
$(this).find("div.blur div:not(div.tint)").fadeTo(333, 0.8);
$(this).parent().children("div.popup").css("display", "block").fadeTo(333, 1);
}
)
.mouseleave(
function()
{
$(this).children("div.box").fadeTo(333, 0);
$(this).find("div.tint").fadeTo(333, 0);
$(this).find("div.blur div").fadeTo(333, 0);
$(this).parent().children("div.popup").css("display", "none").fadeTo(333, 0);
}
);

When the mouse enters the thumbnail, fade in the mouse box and popup completely, fade in the tint element partially (so that you can still see through it), and fade in the blur elements to 80% to cause the blur effect.

When the mouse leaves the thumbnail, fade out the mouse box, tint element, blur elements, and popup completely.

Easy! Now for the dreaded algorithm.

$("div.image-magnify:last div.thumbnail")
.mousemove(
function(e)
{
// Much like inner magnification, we will start with these same variables
// and determine the mouse position within the thumbnail.
var data = $(this).parent().data(),
offset = $(this).offset();
var ratio = {
height: data.popupHeight / data.thumbnailHeight,
width: data.popupWidth / data.thumbnailWidth
},
x = e.pageX - offset.left - data.borderWidth,
y = e.pageY - offset.top - data.borderWidth;

/*
Unlike inner magnification, we will be working with a mouse box.
We must then calculate the X and Y coordinates of the box, not the mouse.
Since the box has a width of thumbnail.width / ratio.width,
the x coordinate will be half the box's width to the left of the mouse
(while the other half of the box is to the right of the mouse).
*/
x = x - $(this).width() / ratio.width / 2;
y = y - $(this).height() / ratio.height / 2;

// If the would-be box is so far left that it's no longer on the thumbnail,
// don't put it any further left.
if (x < 0)
x = 0;

/*
If the would-be box is so far right that it's no longer on the thumbnail,
(if the position of the left of the box plus the width of the box is further than the thumbnail width)
don't put it any further right.
(set the position of the left of the box to be the width of the thumbnail minus the width of the box)
*/
if (x + $(this).width() / ratio.width > $(this).width())
x = $(this).width() - $(this).width() / ratio.width;

if (y < 0)
y = 0;
if (y > $(this).height() - $(this).height() / ratio.height)
y = $(this).height() - $(this).height() / ratio.height;

// Dealing with fractions, we better round. There's no such thing as a fraction of a pixel.
x = Math.round(x);
y = Math.round(y);

/*
Set the background position of the mouse box to match the position of the thumbnail.
Remember, it has its own background, because the thumbnail may be blurred or tinted.
Set the left and top attributes of the mouse box so that it scrolls with the mouse.
$(this).height() is subtracted from y, because div.box comes after div.blur and div.tint.
(Remember the placeholder created in the HTML?)
*/
$(this).children("div.box").css({
backgroundPosition: (x > 0 ? "-" + x + "px" : 0) + " " + (y > 0 ? '-' + y + "px" : 0),
left: (x - data.borderWidth) + "px",
top: (y - $(this).height() - data.borderWidth) + "px"
});

/*
Unlike inner magnification, the x and y values are representative of the left position in the mouse box, not the mouse itself.
Therefore when the mouse is at the far right of the image, the value of x still represents the far right minus the width of the box.
We thus do not need to substract anything when multiplying the x and y values by the ratio.
The popup's background position will never scroll the image out of view, because the x and y values is not allowed to reach the full dimensions of the thumbnail.
*/
$(this).parent().children("div.popup").css("background-position", (x > 0 ? "-" + (x * ratio.width) + "px" : 0) + " " + (y > 0 ? '-' + (y * ratio.width) + "px" : 0));
}
);

And there you have it. I find blur’s and tint’s algorithms to be much easier than inner’s, solely due to the fact that dealing with a mouse box that prevents scrolling beyond the dimensions of the thumbnail makes it easy to calculate the background of the popup.

For those wondering, it is entirely possible to use the mouse box’s algorithm in the inner magnification [without displaying the mouse box, of course]. I chose not to do so for one entirely aesthetic reason. When the box has reached the dimensions of the thumbnail, scrolling of the popup ceases. This isn’t very noticable with the mouse box, because you expect it to stop when the box’s border hits the thumbnail’s border. However, when there is no border guiding you, like with the inner magnification, it appears less fluid and more choppy. To see the difference for yourself, place your mouse in the bottom right corner of any of the magnification examples at the beginning of this tutorial. If you move it slightly to the left or up, nothing changes; the mouse box is still positioned as far to the bottom and right as it can go. However, if you do the same thing in the inner magnification example, it will scroll regardless of where your mouse is. It does not stop scrolling until your mouse is physically at the far right or far bottom of the thumbnail.

I find that to look better with an inner magnification. If you prefer using an invisible mouse box, you may simply copy-paste the algorithm from the blur and tint handler to the other.

Conclusion

If any reader (especially math majors) believes that he or she can explain the algorithms better than I did, feel absolutely free to do so in the comments. I will gladly edit them into the post itself to help future readers, giving credit where credit is due. I attempted to explain the algorithms to the best of my ability and as adequately as I believed they needed to be, but as a visual learner and memorizer, I’m aware of the difficulties of transcribing visual information.

Alas, thank you for reading!

Download the files here

Unlock the key of your success by MB6-502 and MB7-843. By using our latest MB5-294 study material, you can easily pass 70-529 exam.



Comments
  1. thanks nice tutorials Im actually trying it right now

  2. Tiabin McKirsch said:

    Awesome tutorials thank’s for sharing!

  3. Beben Koben said:

    its a rock master, good!!!
    i have add one on my blog…hihihi
    good good \m/

  4. web design said:

    Thanks the code & tutorial.

  5. This is really interesting, You are a very skilled blogger.

  6. The tutorial is stunning. I’ve followed all the steps and it worked out pretty well.

  7. Web Guru said:

    Very useful tutorial. I am going to try it. I need to magnify a image for my website background. Thanks for sharing the tutorial.

  8. Nice work thanks for the share It really works :)

  9. Matthew Smith said:

    Nice work This Made My work easy Thanks for the share i will sure try it

  10. This is really nice one and handy tutorial, thanks..

  11. That is really cool. I’m not a math major so I’ll stick with your algorithm explanations. Thanks.

  12. Great view! I recently hit on your blog and have been reading along. I thought I would leave my incipient comment. I don’€™t know what to disclose except that I have enjoyed reading. Great blog. I will keep visiting this blog often.

  13. Aric Martyn said:

    This post is great. Thank you for this post. I like this type of people who share knowledge with others.

  14. I am impressed with this site, really I am a big fan .