RSS

Perspective with jParallax

Perspective with jParallax

Perspective with jParallax

What happens when you give nerds the space to get creative? Friday at Affinity Bridge Head Quarters went a little something like this...

Ariane: Wow Alberto, our site header and footer are moving! But you hate Flash, how did you do that!??

Alberto: Well, actually it's not flash at all! It is a very neat script named jParallax that I had been wanting to experiment with for animating the illustrations on the site.

Ariane: Can you tell us a little bit more about this script, how does it work, how is the movement controlled, the overlapping, etc?

Alberto: I sure can. According to wikipedia "Parallax is an apparent displacement or difference in the apparent position of an object viewed along two different lines of sight". What jParallax does is use the jQuery library to implement this effect.

As far as translating it into HTML, CSS, and javascript, first thing is to built your HTML structure, I'm going to take a snippet of our header as example:

<div id="header-parallax" class="parallax">
  <div class="layer layer-0">
    <img src="/<?php print path_to_theme(); ?>/images/jparallax/header/moon.png" />
    <img src="/<?php print path_to_theme(); ?>/images/jparallax/header/moon_phases/moon_phase_<?php print moon_phase(); ?>.png" />
  </div>
  <div class="layer layer-1">
    <img src="/<?php print path_to_theme(); ?>/images/jparallax/header/east.png" />
  </div>
  <div class="layer layer-2">
    <img src="/<?php print path_to_theme(); ?>/images/jparallax/header/lions-peaks.png" />
  </div>
  
  ...

  <div class="layer layer-8">
    <img src="/<?php print path_to_theme(); ?>/images/jparallax/header/stanley-park.png" />
  </div>
  <div class="layer layer-9">
    <img src="/<?php print path_to_theme(); ?>/images/jparallax/header/vancouver.png" />
  </div>
</div>

Again, it's just snippet so you can have an idea of what is happening.

Next you need to work on your CSS; here the main parts control of the overflow for the image area, and the position of every single image inside its layer.

.parallax {
  overflow: hidden;
  padding: 0;
  position: relative;
  width: 1000px;
}

#header-parallax,
#header-parallax .layer {
  height: 130px;
}

/* header layers */
#header-parallax .layer-0 { width: 1010px; }

#header-parallax .layer-1 { width: 1074px; }

#header-parallax .layer-2 { width: 1111px; }

...

#header-parallax .layer-8 { width: 1333px; }

#header-parallax .layer-9 { width: 1370px; }

The last part is to apply jParallax to your image area and in our case we need it to pass additional parameters, for eg. in our scenario we didn't need movement in the y axis.

('#header-parallax').jparallax({yparallax: false});

Ariane: So, how does that translate more simply?

Alberto: Of course. The layers are moved according to the movement of the cursor, the action starts as soon you position your cursor inside the image area, when the cursor reachess one of the container's edges all the layers do the same; the bigger the layer is, the faster it will have to move.

In our animation we are using an image area or "viewport" of 1000px and as you can see the city of Vancouver and Stanley Park placed in the foreground move faster than the Lions Peak or the moon which are way in the back. This is because the layers for the moon and Lions Peak are 1010px and 1074px respectively compared with the 1370px of Vancouver and the 1333px of Stanley Park.

Demo jParallax

A picture is worth a thousand words; here the red frame is our viewport and all those green frames represent the different layers that we are using (there are only 5 green frames but we are actually using 10 layers).

Ariane: Ah, I think I see how that all came together... but wait a minute. I noticed you also added the windmill on the mountains in the footer, but it rotates like a windmill, is this also jParallax?

Alberto: Firstly I blame Shawn for throwing an additional challenge my way :). The first animation that I created was for the footer, when I finished it and presented it to the team everybody was very excited, but Shawn also added "...it would be nice if that wind mill could move".

At first I told him that for that we would need Flash, then I gave it a second thought and I said to myself "... what the heck, I can do that without Flash!", so I decided to try an animated gif to see how it will behave. And wouldn't you know, it worked!

We liked the results and we applied that to the water as well, then I was the one who got excited and said it would be neat if we could follow the phase of the moon accurately.

Ariane: What?! The moon is animated too?

Alberto: Oh yeah, but I'm not the author of that part. Hey Zoë would you mind to tell us how did you applied your magic here?

Zoë: I'd be delighted to. And at great length!

So where Alberto left off, we were all admiring the geographic realism of the jParallax artwork, when some wag made what I'm pretty sure was a joke about the moon not being in the correct phase. At this point, having realized that any wish anyone idly voiced would swiftly become digital reality, Mack rounded in on me and cackled with glee, "Hey Zoë, make the phase of the moon update dynamically!"

Now, we could have depended on any one of many astronomical sites for very precise current lunar phase information, but I didn't wanna, for two reasons: 1) depending on data from an external server we don't control is icky, 2) that would rob me of a chance to do math.

I found an algorithm that would calculate the phase of the moon based on the current date, translated it into PHP, and added it as a function in template.php, moon_phase(), which returns an integer from 0 to 29. The integer represents the "age" of the moon in days, with 0 meaning "new moon", 15 "full moon", 23 "waning half moon", and 29 "almost new moon again with just a tiny sliver of moon still illuminated". So on a day when there is a waxing gibbous moon leering through the clouds above us, this function will return approximately 11. You get the idea.

Next, I generated a slew of images to represent the moon phases. Alberto wanted the shadow on the face of the moon to be an overlay on the original full moon image, so that in the future, should we wish to turn the moons in the header and footer a violent shade of green, we could swap out the moon image without having to produce a whole new set of phase images. I acquiesced, grumbling, since this approach, while practical, involved me having to figure out how to make a PNG with transparency. (Turned out this is totally easy. In Photoshop, make an image with transparent background; cut out areas and/or turn down the layer transparency; File -> Save for Web; choose the PNG-24 preset.)

Squinting mightily at a moon phase calendar, I made a Photoshop document with 29 layers, each holding a little shadow image, and created an action set to step through and export them one by one as PNGs. Then another action to resize and recolor them all, since the moon is smaller in the header than the footer. Now, perhaps if I was really hungry for nerd cred I could have done away with all the Photoshop fumbling and generated the shadow images with nothin' but math and pi and some command-line ImageMagick foo, but you know what? No. I bet you anything it would have taken way longer that way to churn out 29 shadows that would each look aesthetically pleasing superimposed over moon.png. Hurumph.

I then added this line here to the HTML:

<img src="/<?php print path_to_theme(); ?>/images/jparallax/header/moon_phases/moon_phase_<?php print moon_phase(); ?>.png" />

Alberto's CSS structure was already so tight that the shadow positioned itself perfectly over the face of the moon, with no tweaking required.  He's cool like that.

And then, I had to obsess and obsess over how to make the shadow look prettier. Photorealism? A gradient, with more and more blur toward the half-moon? Eventually I settled on a flat gray shadow with a crisp edge.

Which you can't see AT ALL right now, because we had to go and launch the whole sexy jparallax during a full moon! Bah!!

Ariane: WOW. That's pretty nerdy. And by nerdy, I mean AWESOME. :D