501 lines
9.5 KiB
JavaScript
501 lines
9.5 KiB
JavaScript
/**
|
|
* anoSlide - Ultra lightweight responsive slider
|
|
*
|
|
* @version 1.0
|
|
* @author Angel Kostadinov
|
|
* @copyright Anowave
|
|
*/
|
|
(function ()
|
|
{
|
|
var anoSlide = function(element, options)
|
|
{
|
|
this.slides = [];
|
|
this.progress = false;
|
|
this.current = 0;
|
|
this.element = $(element);
|
|
this.options = $.extend(
|
|
{
|
|
items: 5,
|
|
speed: 1000,
|
|
auto: 0,
|
|
autoStop: true,
|
|
next: '',
|
|
prev: '',
|
|
responsiveAt: 480,
|
|
delay: 0,
|
|
lazy: false,
|
|
onConstruct: function(instance){},
|
|
onStart: function(ui){},
|
|
onEnd: function(ui){}
|
|
}, options);
|
|
|
|
/* Reference */
|
|
this.defaults =
|
|
{
|
|
items: this.options.items,
|
|
auto: 0
|
|
}
|
|
|
|
/* Preloader */
|
|
this.preloader = new anoPreload();
|
|
|
|
this.timeout = null;
|
|
};
|
|
|
|
anoSlide.prototype =
|
|
{
|
|
construct: function()
|
|
{
|
|
this.defaults.auto = this.options.auto;
|
|
|
|
this.element.css(
|
|
{
|
|
position: 'relative',
|
|
overflow: 'hidden',
|
|
display: 'block'
|
|
}).children().css(
|
|
{
|
|
position: 'absolute',
|
|
cursor: 'pointer',
|
|
overflow: 'hidden',
|
|
display: 'block'
|
|
}).each(delegate(this, function(index, slide)
|
|
{
|
|
this.slides.push(
|
|
{
|
|
element: $(slide)
|
|
})
|
|
})).find('img').css(
|
|
{
|
|
float: 'left'
|
|
})
|
|
|
|
/* Responsive */
|
|
$(window).on(
|
|
{
|
|
resize: delegate(this, this.adapt)
|
|
});
|
|
|
|
/* Activate next and prev controls */
|
|
if (this.options.next)
|
|
{
|
|
$(this.options.next).on('click', delegate(this, this.next));
|
|
}
|
|
|
|
if (this.options.prev)
|
|
{
|
|
$(this.options.prev).on('click', delegate(this, this.prev));
|
|
}
|
|
|
|
if (this.options.autoStop)
|
|
{
|
|
this.element.parent().on(
|
|
{
|
|
mouseenter: delegate(this, function(element)
|
|
{
|
|
if (this.timeout)
|
|
{
|
|
clearTimeout(this.timeout);
|
|
|
|
this.options.auto = 0;
|
|
}
|
|
}),
|
|
mouseleave: delegate(this, function(element)
|
|
{
|
|
this.options.auto = this.defaults.auto;
|
|
|
|
this.go(this.current);
|
|
})
|
|
})
|
|
}
|
|
|
|
this.options.onConstruct.apply(this,[this]);
|
|
|
|
this.adapt().go(0);
|
|
},
|
|
preload: function(index, callback)
|
|
{
|
|
var queue = [];
|
|
|
|
if (this.options.lazy)
|
|
{
|
|
for (i = index, l = index + this.options.items; i < l; i++)
|
|
{
|
|
if (this.slides[i].element.find('img[data-src]').length)
|
|
{
|
|
queue.push(
|
|
{
|
|
source: this.slides[i].element.find('img[data-src]').data('src')
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (queue.length)
|
|
{
|
|
this.preloader.reset().append(queue).preload(callback);
|
|
}
|
|
else
|
|
{
|
|
callback.apply(this,[
|
|
{
|
|
images:[]
|
|
}]);
|
|
}
|
|
},
|
|
animate: function(index, images, reverse)
|
|
{
|
|
if (!this.progress)
|
|
{
|
|
this.progress = true;
|
|
|
|
var viewport =
|
|
{
|
|
w: this.element.parent().outerWidth(),
|
|
h: this.element.parent().outerHeight()
|
|
}
|
|
|
|
/* On start callback */
|
|
this.options.onStart.apply(this,[
|
|
{
|
|
instance: this,
|
|
index: index,
|
|
slide: this.slides[index]
|
|
}]);
|
|
|
|
$.each((reverse ? this.slides.reverse() : this.slides), delegate(this, function(key, slide)
|
|
{
|
|
var offset = (reverse ? (-(index - (this.slides.length - 1 - key))) : (-(index - key))) * this.element.width()/this.options.items;
|
|
|
|
/* Check for empty slides */
|
|
if (slide.element.find('img[data-src]').length && images.length)
|
|
{
|
|
if (1 == this.options.items)
|
|
{
|
|
if (index == key)
|
|
{
|
|
var i = images.pop().image;
|
|
|
|
slide.element.find('img[data-src]').replaceWith(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var i = images.pop().image;
|
|
|
|
slide.element.find('img[data-src]').replaceWith(i);
|
|
}
|
|
}
|
|
|
|
var fn = (key == index) ? delegate(this, function()
|
|
{
|
|
this.progress = false;
|
|
|
|
this.options.onEnd.apply(this,[
|
|
{
|
|
instance: this,
|
|
index: this.current,
|
|
slide: this.slides[this.current]
|
|
}])
|
|
|
|
if (this.options.auto)
|
|
{
|
|
if (this.timeout)
|
|
{
|
|
clearTimeout(this.timeout);
|
|
}
|
|
|
|
this.timeout = setTimeout(delegate(this, this.next), this.options.auto);
|
|
}
|
|
|
|
}) : function() {};
|
|
|
|
|
|
slide.element.css(
|
|
{
|
|
width: Math.floor(this.element.outerWidth()/this.options.items) + 'px',
|
|
height: 'auto'
|
|
}).delay(key * this.options.delay).animate(
|
|
{
|
|
left: offset + 'px'
|
|
}, this.options.speed, fn);
|
|
}));
|
|
|
|
if (reverse)
|
|
{
|
|
this.slides.reverse();
|
|
}
|
|
|
|
this.element.animate(
|
|
{
|
|
height: this.slides[index].element.outerHeight()-2
|
|
});
|
|
|
|
/* Toggle controls */
|
|
var queue = this.slides.length - this.options.items - this.current;
|
|
|
|
if (!queue)
|
|
{
|
|
this.disable.next.call(this);
|
|
}
|
|
else this.enable.next.call(this);
|
|
|
|
if (index - 1 < 0)
|
|
{
|
|
this.disable.prev.call(this);
|
|
}
|
|
else
|
|
{
|
|
this.enable.prev.call(this);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
adapt: function()
|
|
{
|
|
var viewport =
|
|
{
|
|
w: this.element.parent().outerWidth(),
|
|
h: this.element.parent().outerHeight()
|
|
}
|
|
|
|
if (viewport.w < this.options.responsiveAt)
|
|
{
|
|
this.options.items = 1;
|
|
}
|
|
else
|
|
{
|
|
this.options.items = this.defaults.items;
|
|
}
|
|
|
|
$.each(this.slides, delegate(this, function(key, slide)
|
|
{
|
|
var offset = -(this.current - key) * this.element.width()/this.options.items;
|
|
|
|
slide.element.stop().css(
|
|
{
|
|
width: this.element.width()/this.options.items,
|
|
left: offset
|
|
}, this.options.speed, (key == this.slides.length - 1) ? delegate(this, function()
|
|
{
|
|
this.progress = false;
|
|
}) : function(){})
|
|
}));
|
|
|
|
if (0 !== this.slides[this.current].element.parent().height())
|
|
{
|
|
|
|
this.element.css(
|
|
{
|
|
height: this.slides[this.current].element.outerHeight()-2
|
|
})
|
|
}
|
|
|
|
return this;
|
|
},
|
|
next: function()
|
|
{
|
|
var queue = this.slides.length - this.options.items - this.current;
|
|
|
|
if (queue)
|
|
{
|
|
this.go(this.current + 1);
|
|
}
|
|
else
|
|
{
|
|
this.go(0);
|
|
}
|
|
},
|
|
prev: function()
|
|
{
|
|
this.go(this.current - 1, true);
|
|
},
|
|
stop: function()
|
|
{
|
|
if (this.timeout)
|
|
{
|
|
clearTimeout(this.timeout);
|
|
}
|
|
|
|
this.progress = false;
|
|
|
|
return this;
|
|
},
|
|
go: function(index, reverse)
|
|
{
|
|
reverse = reverse || false;
|
|
|
|
if (!this.progress)
|
|
{
|
|
if (index < 0 || index > this.slides.length - 1)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
this.current = index;
|
|
|
|
this.preload(index, delegate(this, function(ui)
|
|
{
|
|
this.animate(index, ui.images, reverse);
|
|
}));
|
|
}
|
|
}
|
|
},
|
|
enable:
|
|
{
|
|
next: function()
|
|
{
|
|
$(this.options.next).removeClass('disabled').fadeIn(300);
|
|
},
|
|
prev: function()
|
|
{
|
|
$(this.options.prev).removeClass('disabled').fadeIn(300);
|
|
}
|
|
},
|
|
disable:
|
|
{
|
|
next: function()
|
|
{
|
|
$(this.options.next).addClass('disabled').fadeOut(300);
|
|
},
|
|
prev: function()
|
|
{
|
|
$(this.options.prev).addClass('disabled').fadeOut(300);
|
|
}
|
|
}
|
|
}
|
|
|
|
var anoPreload = function()
|
|
{
|
|
this.queue = [];
|
|
this.images = [];
|
|
this.total = 0;
|
|
this.config =
|
|
{
|
|
cache: true
|
|
};
|
|
|
|
this.time =
|
|
{
|
|
start: 0,
|
|
end: 0
|
|
}
|
|
}
|
|
|
|
anoPreload.prototype =
|
|
{
|
|
onComplete: function(ui){},
|
|
reset: function()
|
|
{
|
|
this.queue = [];
|
|
this.images = [];
|
|
this.total = 0;
|
|
|
|
return this;
|
|
},
|
|
append: function(element)
|
|
{
|
|
var queue = this.queue;
|
|
|
|
$.each(element, function(index, element)
|
|
{
|
|
queue.push(element);
|
|
});
|
|
|
|
return this;
|
|
},
|
|
finish: function(event, index, image)
|
|
{
|
|
/* Decrease number of finished items */
|
|
this.total--;
|
|
|
|
$.each(this.images, function(x,i)
|
|
{
|
|
if (i.index == index)
|
|
{
|
|
i.size = {
|
|
width: image.width,
|
|
height: image.height
|
|
}
|
|
}
|
|
})
|
|
|
|
/* Check if no more items to preload */
|
|
if (0 == this.total)
|
|
{
|
|
this.time.end = new Date().getTime();
|
|
|
|
this.onComplete.apply(this,[
|
|
{
|
|
time: ((this.time.end - this.time.start)/1000).toPrecision(2),
|
|
images: this.images
|
|
}])
|
|
}
|
|
},
|
|
preload: function(callback)
|
|
{
|
|
/* Set callback function */
|
|
this.onComplete = callback || this.onComplete;
|
|
|
|
this.time.start = new Date().getTime();
|
|
|
|
/* Get queue length */
|
|
this.total = i = this.queue.length;
|
|
|
|
while(i--)
|
|
{
|
|
var image = new Image();
|
|
|
|
/* Push image */
|
|
this.images.push(
|
|
{
|
|
index: i,
|
|
image: image,
|
|
size:
|
|
{
|
|
width: 0,
|
|
height: 0
|
|
}
|
|
});
|
|
|
|
image.onload = image.onerror = image.onabort = delegate(this, this.finish, ([i,image]));
|
|
|
|
/* Set image source */
|
|
image.src = this.config.cache ? this.queue[i].source : (this.queue[i].source + '?u=' + (new Date().getTime()));
|
|
}
|
|
}
|
|
}
|
|
|
|
$.fn.anoSlide = function (options)
|
|
{
|
|
return this.each(function ()
|
|
{
|
|
if (undefined == $(this).data('anoSlide'))
|
|
{
|
|
var a = new anoSlide(this, options).construct();
|
|
$(this).data('anoSlide', a);
|
|
}
|
|
});
|
|
};
|
|
|
|
var delegate = function(target, method, args)
|
|
{
|
|
return (typeof method === "function") ? function()
|
|
{
|
|
/* Override prototype */
|
|
arguments.push = Array.prototype.push;
|
|
|
|
/* Push additional arguments */
|
|
for (var arg in args)
|
|
{
|
|
arguments.push(args[arg]);
|
|
}
|
|
return method.apply(target, arguments);
|
|
} : function()
|
|
{
|
|
return false;
|
|
};
|
|
}
|
|
})(); |