Art
I'd like to thank my friend Nolan Tredway for letting me use some of his work for this demo. The graphics in this scene come from a piece that depicts the nursery rhyme 'Humpty Dumpty' and its roots in the story of King Richard III of England and the Battle of Bosworth Field.
HTML/CSS
The html and css is pretty simple. Each layer of the scene is a png image with transparency. A relatively positioned div wraps the list of layer images from back to front in the scene. Each <img /> tag has a rel value corresponding to the z value read in by the plugin.
JavaScript
Once the images a lined up on top of each other, a JavaScript plugin tracks the mouse cursor and adjusts the position of the layers accordingly.
To do the plugin must:
- keep track of mouse position
- keep track of window size
- update layer position on interval
Keep track of mouse position and window size
With jQuery tracking the mouse position is trivial.
var methods = {As is tracking the mouse position.
init : function(options){
...
//listen for mouse move
$(document).mousemove(methods.onMouseMove);
...
},
onMouseMove : function(event){
mouseX = event.clientX;
mouseY = event.clientY;
}
}
...
var mouseX, mouseY;
var methods = {
init : function(options){
...
//get and keep window size
$window.resize(methods.onWindowResize).resize();
...
},
onWindowResize : function(event){onWindowResize : function(event){
wHeight = $window.height();
wWidth = $window.width();
}
}
...
var wHeight, wWidth;
}
Updating layer position
The meat of the plugin is responsible for shifting each layer based on its z index and mouse position.
var methods = {There were a couple of steps I took to increase the performance of the position calculation. In the init function the $.each() function is used to loop through frames for ease of implementation, but the calculatePosition function needs to run a bit faster so I used a for loop. Secondly, I decided to calculate the new position of the frames on an interval, rather than on mouse move. Mouse move occurs too often to perform any significant calculation and translation, so a interval and the jQuery animate plugin was utilized.
var settings = {
magnitudeX: 100, //control baseline of x movement
magnitudeY: 50, //control baseline of y movement
paralaxFactor : 10 //sets baseline zindex for paralax movement
}, $frames, originalPosition; //list of frames and original position
var methods = {
init : function(options) {
if(options){
$.extend(settings, options);
}
settings = $.extend(settings, options);
...
$t = $(this);
$frames = $t.children(".frame");
$frames.each(function(){
//store paralax index as float
$.data(this, "paralaxLayer", parseFloat($(this).attr('rel')));
});
originalPosition = $frames.position();
...
//reposition on interval rather than mouse move
setInterval(methods.calculatePosition, 100);
...
},
calculatePosition : function(){
//calculate baseline offset for this mouse position
var offsetX = (settings.magnitudeX)*(mouseX/wWidth);
- (settings.magnitudeX/2),
offsetY = (settings.magnitudeY)*(mouseY/wHeight);
- (settings.magnitudeY/2),
frame, paralaxLayer, paralaxFactorX, paralaxFactorY;
//go through and reposition each frame
for(i=0; i<$frames.length; i++){
$frame = $($frames[i]);
paralaxLayer = $.data($frames[i], "paralaxLayer")/
settings.paralaxFactor;
paralaxFactorX = -paralaxLayer*offsetX,
paralaxFactorY = -paralaxLayer*offsetY;
//use animate for smooth transition
$frame.clearQueue().animate({
top: originalPosition.top+paralaxFactorY,
left: originalPosition.left+paralaxFactorX,
}, 300);
}
}
}
Possible Improvements
- load layer data into object or array instead of using $.data plugin
- integrate text shadow into plugin