Moving Photo Magnifier into Drupal

A while back I made a demo of an image magnifier on a stand-alone web page. Now I'm going to implement the same functionality in a Drupal page. All Drupal needs to provide is a simple content type with a title, image, and description. The rest will be done by jquery.

First, since the magnifier depends on having 2 images, one 400 pixels wide and , for magnification, one 1600 pixels wide, in Drupal go to Administer > Site Building > Image Cache and create two new presets, "pic400" which scales our upload to 400 pixels wide, and "pic1600" which scales the upload to 1600 pixels wide.

Next, go to Administer > Content Management > Content Types and create a new content type, which I'll call "Magnifier". Enter "Magnifier" in the Title field and make the field that has "Body" in it blank - that way the content type will not have a Body field. (We will add a Description field of our own instead - this is considered a Drupal "best practice" since you will have more control over a field you create.) For this content type all you need to enter is the required "title" and "type" fields. The defaults will do for everything else. (Under Workflow Settings I unchecked "Promoted to Front Page" since I don't want these pages listed on the front page, since my front page is for my list of articles. Later I will create a View - which I'll have to write a separate article about - to list my "Magnifier" pages.)

Once the "Magnifier" content type is created, in the list of content types, click on "Manage Fields" in the "Magnifier" row. (This requires the CCK module in Drupal 6. In Drupal 7 content creation is part of the core.) Now, create two fields "Image" and "Description". For the Image field choose file type "File" and type of "Image". For the Description field, choose file type "Text" and type of "Text Area (multiple rows)". At the top of the Manage Fields page for "Magnifier" click on the Display Fields tab. Here set the Label for both fields to hidden. For the teaser display I chose for the image a display of "pic180 linked to node" - this is an Image Cache preset I created earlier which I use for all teasers. You can create a preset like this if you want an image in your teaser or you can just choose "hidden". For the full node display, choose "pic400 image".

Now we are ready to create a new page in our new content type. To do so, go to Create Content and choose the Magnifier content type. I've got my image of Renior's "Le Moulin de Galette" so I give it the title "Le Mouline de Galette" and upload my 1600 pixel wide image and type in a short description. Voila!

Now we will put jquery to work. First we have to take a look at the source code to see what Drupal has served up. You can do this with Firebug or by choosing "View Source" in whatever browser you are using. We do this so we can tell jquery which selectors to target.

I downloaded a custom jquery-ui install from jquery-ui.com. It contains the "draggable" component and the jquery-ui core (version 1.7.3). I named it jquery-ui.js and put it in my theme folder in /sites/all/themes/.

(IMPORTANT: I discovered working on this demo that the draggable component requires JQuery version 1.3.2. Drupal 6.16 comes with JQuery version 1.2.6, so I had to replace Drupal's jquery file in the /misc folder. This may or may not have unforeseen side effects.)

For this project, instead of putting the code in a field on the page, I want to put it in a block which will appear on all the pages of the Magnifier content type. So I go the Administer > Site Building > Blocks and create a new block, being sure to set the Input Type of the Block body to "As is."

So that this code only loads for the Magnifier content type, in the Page Specific Visibility Setting, I checked "Show if the following PHP code returns TRUE (PHP-mode, experts only)" and put this code in the text box.



<?php
  if(arg(0) == 'node')
  {
      $nid = arg(1);
      $node = node_load(array('nid' => $nid));
      if ($node->type == 'magnifier')
      {
         return TRUE;
      }
  }
  return FALSE;
?>

This returns "true" if the content type is "magnifier." (In Drupal 7, this is not needed. You can simply click the checkbox next to the content type.)

Let's take a look at the code that goes in the code block:


<style>

div#magnifier {
  z-index: 99;
  margin: 0;
  padding: 0;
  display: block;
  position: absolute;
  top: 2px;
  left: 2px;
  width: 99px;
  height: 76px;
  min-width: 99px;
  min-height: 76px;
  border: 1px solid #f00;
  outline: 1px solid #ff0;
  background: none; /* the fallback if no rgba */
  background: rgba(200, 200, 200, 0.3);
}

div.xyz { position: relative; float: left; }

div#box2 {
  width: 400px;
  height: 298px;
  float: left;
  border: 1px solid #333;
  padding: 0;
  margin: 0 12px;
  overflow: hidden;
  position: relative;
}

div#box2 img {
  position: relative;
  top: 0;
  left: 0
}

div.field-field-desc5 { position: relative; clear: both; padding-top: 10px; }

<style>

So that's our styling. It's very similar to what's in the earlier demo. It just styles the little red-bordered magnifying box and styles the box that holds the 1600 pixel wide image. Note the "overflow: hidden;".

Now we put the jquery to work. First we load our custom ui code that adds the draggable functionality.

Then when the document is ready, we append the magnifier div to the div that contains the 400 pixel wide image. (The classes of the divs depends on the name you gave the image field you created in your content type.)

Then we add the class "xyz" to the div that contains the image and the style floats it so we can float the div we are going to add next to it.

The next two lines modify the content area of the page, hiding the sidebar that on most pages shows ads, and widening the content area so it will hold the 2 images side by side.

Next comes the "draggable" part from http://jqueryui.com/demos/draggable/.

In the first line, we select the element with the id of "magnifier" and apply to it the jQuery ui function "draggable." Line two specifies that the magnifier stays contained within its parent. In the drag function, we get the top and left positions of the ui parameter and multiply them by -4. (We use 4 since our magnification is 4x, and it's negative so the large image will move left as the magnifier is moved right.) In the last two lines, the position of the image in the div with the id of box2 is set by modifying the css properties top and left.

Finally, the code puts the path of the image into the variable thepic, then it changes the path (from pic400 to pic1600), and then it loads the large image.



<script src="/home/sites/all/themes/netnik/jquery-ui.js"></script>

<script>

$(document).ready(function() {


  $('div.field-field-img5 div.field-item').append('<div id="magnifier"></div>');

  $('div.field-field-img5 div.field-item').addClass('xyz');

  $('div#sidebar-left').css('display', 'none');
  $('div#main-holder').css('width', '900px');


  $('#magnifier').draggable({
    containment: 'parent',
    drag: function(event, ui) { 
      var xtop = (ui.position.top);
      var xleft = (ui.position.left);
      $('#box2 img').css('left', ((xleft * -4) + 'px'));
      $('#box2 img').css('top', ((xtop * -4) + 'px'));
    }
  });


  var thepic = $('div.field-field-img5 div.field-items img').attr('src');

  thepic = thepic.replace(/pic400/, "pic1600");

  $('div.field-field-img5 div.field-items').append
('<div id="box2"><img src=' + thepic + ' /></div>');

});

</script>


Whew, that turned out to be a little more complex, and especially more complex to explain, than I had foreseen, but now I've got a magnifier content type so I can add as many images as I want. Next I am going to use Views to add a list of links to the pictures.

In the meantime, you can see a couple magnified photos here and here.