I wanted to get some feedback on this JS I am working on. It works wonderfully on my desktop, but on my and my friends' laptops (especially Safari) it is very slow. I need some pointers on improving the efficiency so it works nicely across browsers/computers.
// If jQuery was not loaded, offload it.
if (!typeof jQuery) {
document.write(unescape("%3Cscript src='/resources/jquery-2.1.0.min.js' type='text/javascript'%3E%3C/script%3E"));
}
/*
* Called when the DOM has been loaded.
* Create all parallaxElements with their respective properties, pool them
* in an array and begin the parallax animations.
*/
$(document).ready(function () {
var kami = new ParallaxElement("#kami", 1, 1, $("#parallaxcanvas").width(), $("#parallaxcanvas").height(), 1);
var mainrock = new ParallaxElement("#mainrock", .25, .25, 75, 25, 1);
var bgrock1 = new ParallaxElement("#bgrock1", .12, .12, 75, 75, 1);
var bgrock2 = new ParallaxElement("#bgrock2", .06, .06, 75, 75, 1);
var bgrock3 = new ParallaxElement("#bgrock3", .03, .03, 75, 75, 1);
var parallaxElements = [ kami, mainrock, bgrock1, bgrock2, bgrock3];
Parallax("#parallaxcanvas", parallaxElements);
});
/*
* jstr - The css identifier of the parallax element(s).
* xStep - The amount to move, every update, along the x.
* yStep - The amount to move, every update, along the y.
* xRange - The range this element is allowed to move along the x.
* yRange - The range this element is allowed to move along the y.
* update - How often to update this element's position in milliseconds.
*/
function ParallaxElement(jstr, xStep, yStep, xRange, yRange, update) {
var init_pos = $(jstr).position();
var followPos = { left: init_pos.left, top: init_pos.top };
var moveInterval;
var minX = init_pos.left - xRange;
var maxX = init_pos.left + xRange;
var minY = init_pos.top - yRange;
var maxY = init_pos.top + yRange;
/*
* Gets called every update milliseconds.
* Adjusts this parallaxelement to follow the followPos.
*/
this.BeginFollow = function() {
moveInterval = setInterval(function() {
var curPos = $(jstr).position();
if (curPos.left < followPos.left && (xStep >= 0 ? curPos.left < maxX : curPos.left > minX)) {
curPos.left += xStep;
}
if (curPos.left > followPos.left && (xStep >= 0 ? curPos.left > minX : curPos.left < maxX)) {
curPos.left -= xStep;
}
if (curPos.top < followPos.top && (yStep >= 0 ? curPos.top < maxY : curPos.top > minY)) {
curPos.top += yStep;
}
if (curPos.top > followPos.top && (yStep >= 0 ? curPos.top > minY : curPos.top < maxY)) {
curPos.top -= yStep;
}
$(jstr).css({ "left": curPos.left, "top": curPos.top });
}, update);
};
/*
* Changes the followPos to a copy of the provided top and left coordinates.
*/
this.UpdateFollow = function(left, top) {
// Adjust left and top origin to be the lower middle of the element.
followPos.left = left - ($(jstr).width() / 2);
followPos.top = top - ($(jstr).height() / 1.5);
}
/*
* Stops this element from following the followPos, resetting such
* position and the element's position to it's original location.
*/
this.EndFollow = function() {
// Reset the position of all elements.
clearInterval(moveInterval);
$(jstr).css({ "left": init_pos.left, "top": init_pos.top });
followPos = { left: init_pos.left, top: init_pos.top };
};
}
/*
* Driver function for the parallax effect.
* Listens to the mouseenter, mousemove, mouseleave events of the provided
* parallax canvas element and relays event information to the
* provided parallaxElements.
* Also performs all image source swapping for animations.
*/
function Parallax(canvas, parallaxElements) {
// Hook mouse events.
$(canvas).mouseenter(initParallax);
$(canvas).mousemove(deltParallax);
$(canvas).mouseleave(resetParallax);
// timeout for when the user stops moving the mouse for 500ms.
var stillTimeout;
// Canvas argument.
var canvasOffset = $(canvas).offset();
// The last x and y coordinates used.
var x, y;
/*
* Initializes the parallax animations by making all parallaxElements
* begin following, and inits the stillTimeout which changes the
* image source after 500 milliseconds, when there is no mouse activity.
*/
function initParallax(event) {
for (i = 0; i < parallaxElements.length; i++) {
parallaxElements[i].BeginFollow();
}
// Swap to the regular skin when the user has not moved the mouse.
stillTimeout = setTimeout(function() {
if ($("#kami img").attr("src") != "media/kami-static.png") {
$("#kami img").attr("src", "media/kami-static.png");
}
}, 500);
}
/*
* Function called when the user moves the mouse around the
* parallax canvas element. Updates the position to follow for
* each of the parallaxElements.
* Also swaps the image source depending on the direction of mouse
* movement for animations. Resets the stillTimeout since the user
* has moved the mouse.
*/
function deltParallax(event) {
// Calculate the canvas' offset.
x1 = event.pageX - canvasOffset.left;
y1 = event.pageY - canvasOffset.top;
// Perform img src swapping animations.
var srcAttr = $("#kami img").attr("src");
if (x1 < x && srcAttr != "media/kami-left.png") {
$("#kami img").attr("src", "media/kami-left.png");
}
if (x1 > x && srcAttr != "media/kami-right.png") {
$("#kami img").attr("src", "media/kami-right.png");
}
// Update parallaxElement follow coordinates.
for (var i = 0; i < parallaxElements.length; i++) {
parallaxElements[i].UpdateFollow(x, y);
}
// Reset the still timer.
clearTimeout(stillTimeout);
stillTimeout = setTimeout(function() {
if ($("#kami img").attr("src") != "media/kami-static.png") {
$("#kami img").attr("src", "media/kami-static.png");
}
}, 500);
// Last coordinates used.
x = x1;
y = y1;
}
/*
* Called when the user's mouse leaves the parallaxCanvas element.
* Resets the position of all parallaxElements to their original position,
* and clears the stillTimeout.
*/
function resetParallax(event) {
for (var i = 0; i < parallaxElements.length; i++) {
parallaxElements[i].EndFollow();
}
clearTimeout(stillTimeout);
}
}
The first iteration I had would move these images however much the user would move the mouse, and this was even snappier than what I have now, except again, on laptops, and most especially, Safari. I changed it to "follow" in the hopes that it would perform better.
It's also on Github if anyone would prefer to review through there.