1. 程式人生 > >Making an Image Magnifier in jQuery

Making an Image Magnifier in jQuery

Making an Image Magnifier in jQuery

Preface ?

This tutorial exists as an archive of the original publication, written in 2010 using jQuery 1.4.2. It supports Internet Explorer 6 and still functions the same to this day in modern browsers, as evidenced by the live demo. Some of the coding examples put forth in the coming sections may not seem optimal, and you’re right. They do not make use of such technologies as flex boxes or alpha-opacity. These simply did not exist at the time. Beta browser features were also ignored in favor of cross-browser compatibility. As a result, this tutorial is backwards compatible by more than a decade. While I would no longer recommend a lot of these practices, there is no need to reinvent the wheel. The product in this tutorial is in working order and a rewrite would achieve nothing more than sacrificing backwards compatibility.

Introduction ?

This script takes an image and its thumbnail as input, then lets the user magnify an area of that thumbnail as output. As the webpage author, you may condense an otherwise large image into a small space. In this demo, four 1600x1280 images are scaled down to 640x512 and 320x256.

Note that this does not magically transform a low-resolution image into a high-resolution image. It requires both high-resolution and low-resolution versions. 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 ?

The first thing we’ll want to do is set up a function that allows us to 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 (blur: boolean), whether or not it tints (tint: color), whether or not it magnifies inside the thumbnail (inner: boolean), the low-resolution image (filepath: string) and its dimensions (width: number and height: number), and the high-resolution image (filepath: string) and its dimensions (width: number and height: number). Considering there are three boxes involved in the magnification process (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!

Giving each box an object, we left with the effect, the low-resolution image, the high resolution image, and the style information of the boxes. Four variables.

An example of these variables being sent to the function:

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 box border, 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.

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.

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.

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

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

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

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.

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

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.

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 then becomes 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.

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 algorithms. 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.

Inner Magnification Elements ?

The most efficient setup for inner magnification is:

While div.image-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:

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.

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:

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 blurred images as well. If the blur were placed above the tint, the tint color would not be noticeable, since the blur effect is just four untinted images. The cursor box is placed inside the thumbnail, 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.

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.

The box’s height and width need to be proportionate 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 will 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 redundantly 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.

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.

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.

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 horizontal position of the thumbnail 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 are 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 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. -(a-b) 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 subtract 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).

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.

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 you liked this article, feel free to give it a clap or two. It’s quick, it’s easy, and it’s free! If you have any questions or relevant great advice, please leave them in the comments below.