Difference between revisions of "Team:Exeter/jquery.coverflow"
(Created page with "/*jslint devel: true, bitwise: true, regexp: true, browser: true, confusion: true, unparam: true, eqeq: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 *...") |
(No difference)
|
Revision as of 15:57, 12 September 2015
/*jslint devel: true, bitwise: true, regexp: true, browser: true, confusion: true, unparam: true, eqeq: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */ /*globals jQuery */
/*!
* Coverflow * * Copyright (c) 2013-2015 Martijn W. van der Lee * Licensed under the MIT. */
/* Lightweight and flexible coverflow effect using CSS3 transforms.
* For modern browsers with some amount of graceful degradation. * Optional support for jQuery.interpolate() plugin. * Optional support for .reflect() plugins. * * Requires jQuery 1.7+ and jQueryUI 1.9+. * Recommended jQuery 1.8+ and jQueryUI 1.9+. */
- (function($, undefined) {
"use strict";
var sign = function(number) { return number < 0 ? -1 : 1; }, scl = function(number, fromMin, fromMax, toMin, toMax) { return ((number - fromMin) * (toMax - toMin) / (fromMax - fromMin)) + toMin; }, wheelEvents = ('onwheel' in document) ? 'wheel' : 'mousewheel', // FF getWheel = function(event) { if ('deltaY' in event.originalEvent) { return 0 - event.originalEvent.deltaY; } else if ('wheelDelta' in event.originalEvent) { return event.originalEvent.wheelDelta; // IE } };
$.widget("vanderlee.coverflow", { options: { animateComplete: undefined, animateStep: undefined, density: 1, duration: 'normal', easing: undefined, enableKeyboard: true, enableClick: true, enableWheel: true, index: 0, innerAngle: -75, innerCss: undefined, innerOffset: 100 / 3, innerScale: 0.75, outerAngle: -30, outerCss: undefined, outerScale: 0.25, selectedCss: undefined, visible: 'density', // 'density', 'all', NNN (exact) width: undefined,
change: undefined, // Whenever index is changed confirm: undefined, // Whenever clicking on the current item select: undefined // Whenever index is set (also on init) },
_window_handler_resize: null, _window_handler_keydown: null,
_create: function() { var that = this, covers = that._getCovers(), images = covers.filter('img').add('img', covers).filter(function() { return !(this.complete || this.height > 0); }), maxHeight = covers.height(), height;
// Internal event prefix that.widgetEventPrefix = 'vanderlee-coverflow';
that.hovering = false; that.pagesize = 1; that.currentIndex = that.options.index;
// Fix height that.element.height(maxHeight); images.load(function() { height = that._getCovers().height(); if (height > maxHeight) { maxHeight = height; that.element.height(maxHeight); } });
// Hide all covers and set position to absolute covers.hide();
// Enable click-jump that.element.on('mousedown tap', '> *', function(event) { if (that.options.enableClick) { var index = that._getCovers().index(this); if (index === that.currentIndex) { that._callback('confirm', event); } else { that._setIndex(index, true); } } });
// Mousewheel that.element.on(wheelEvents, function(event) { if (that.options.enableWheel) { var delta = getWheel(event) > 0 ? 1 : -1;
event.preventDefault(); that._setIndex(that.options.index - delta, true); } });
// Swipe if ($.isFunction(that.element.swipe)) { that.element.swipe({ swipe: function(event, direction, distance, duration, fingerCount) { var count = Math.round((direction === 'left' ? 1 : -1) * 1.25 * that.pagesize * distance / that.element.width()); that._setIndex(that.options.index + count, true); } }); }
// Keyboard that.element.hover( function() { that.hovering = true; } , function() { that.hovering = false; } );
// Refresh on resize that._window_handler_resize = function() { that.refresh(); }; $(window).on('resize', that._window_handler_resize);
that._window_handler_keydown = function(event) { if (that.options.enableKeyboard && that.hovering) { switch (event.which) { case 36: // home event.preventDefault(); that._setIndex(0, true); break;
case 35: // end event.preventDefault(); that._setIndex(that._getCovers().length - 1, true); break;
case 38: // up case 37: // left event.preventDefault(); that._setIndex(that.options.index - 1, true); break;
case 40: // down case 39: // right event.preventDefault(); that._setIndex(that.options.index + 1, true); break;
case 33: // page up (towards home) event.preventDefault(); that._setIndex(that.options.index - that.pagesize, true); break;
case 34: // page down (towards end) event.preventDefault(); that._setIndex(that.options.index + that.pagesize, true); break; } } }; $(window).on('keydown', that._window_handler_keydown);
// Initialize that._setIndex(that.options.index, false, true);
return that; },
/**
* Destroy this object
* @returns {undefined}
*/
_destroy: function() {
$(window).off('resize', this._window_handler_resize);
$(window).off('keydown', this._window_handler_keydown);
this.element.height();
},
/** * Returns the currently selected cover * @returns {jQuery} jQuery object */ cover: function() { return $(this._getCovers()[this.options.index]); },
/** * * @returns {unresolved} */ _getCovers: function() { return $('> *', this.element); },
_setIndex: function(index, animate, initial) { var that = this, covers = that._getCovers();
index = Math.max(0, Math.min(index, covers.length - 1));
if (index !== that.options.index) { // Fix reflections covers.css('position', 'absolute'); this._frame(that.options.index);
if (animate === true || that.options.duration === 0) { that.options.index = Math.round(index);
var duration = typeof that.options.duration === "number" ? that.options.duration : jQuery.fx.speeds[that.options.duration] || jQuery.fx.speeds._default;
this.refresh(duration, that.options.index); } else { that.options.index = Math.round(index); that.refresh(0); } } else if (initial === true) { that.refresh(); that._callback('select'); } },
_callback: function(callback, event) { this._trigger(callback, event, [this._getCovers().get(this.currentIndex), this.currentIndex]); },
index: function(index) { if (index === undefined) { return this.options.index; }
while (index < 0) { index += this._getCovers().length; }
this._setIndex(index, true); },
_frame: function(frame) { frame = frame.toFixed(6);
var that = this, covers = that._getCovers(), count = covers.length, parentWidth = that.element.innerWidth(), coverWidth = that.options.width || covers.first().outerWidth(), visible = that.options.visible === 'density' ? Math.round(parentWidth * that.options.density / coverWidth) : $.isNumeric(that.options.visible) ? that.options.visible : count, parentLeft = that.element.position().left - ((1 - that.options.outerScale) * coverWidth * 0.5), space = (parentWidth - (that.options.outerScale * coverWidth)) * 0.5;
that.pagesize = visible;
covers.removeClass('current').each(function(index, cover) { var $cover = $(cover), position = index - frame, offset = Math.min(Math.max(-1., position / visible), 1), isMiddle = position == 0, zIndex = count - Math.abs(Math.round(position)), isVisible = Math.abs(position) <= visible, sin = Math.sin(offset * Math.PI * 0.5), cos = Math.cos(offset * Math.PI * 0.5), left = sign(sin) * scl(Math.abs(sin), 0, 1, that.options.innerOffset * that.options.density, space), scale = isVisible ? scl(Math.abs(cos), 1, 0, that.options.innerScale, that.options.outerScale) : 0, angle = sign(sin) * scl(Math.abs(sin), 0, 1, that.options.innerAngle, that.options.outerAngle), css = isMiddle ? that.options.selectedCss || {} : ( $.interpolate && that.options.outerCss && !$.isEmptyObject(that.options.outerCss) ? ( isVisible ? $.interpolate(that.options.innerCss || {}, that.options.outerCss, Math.abs(sin)) : that.options.outerCss ) : {} ), transform;
// bad behaviour for being in the middle if (Math.abs(position) < 1) { angle = 0 - (0 - angle) * Math.abs(position); scale = 1 - (1 - scale) * Math.abs(position); left = 0 - (0 - left) * Math.abs(position); }
//@todo Test CSS for middle behaviour (or does $.interpolate handle it?)
transform = 'scale(' + scale + ',' + scale + ') perspective(' + (parentWidth * 0.5) + 'px) rotateY(' + angle + 'deg)';
$cover[isMiddle ? 'addClass' : 'removeClass']('current'); $cover[isVisible ? 'show' : 'hide']();
$cover.css($.extend(css, { 'left': parentLeft + space + left, 'z-index': zIndex, '-webkit-transform': transform, '-ms-transform': transform, 'transform': transform }));
// Optional callback that._trigger('animateStep', null, [cover, offset, isVisible, isMiddle, sin, cos]);
if (frame == that.options.index) { // Optional callback that._trigger('animateComplete', null, [cover, offset, isVisible, isMiddle, sin, cos]); } }); },
refresh: function(duration, index) { var that = this, previous = that.currentIndex, covers = that._getCovers(), covercount = covers.length, triggered = false;
covers.css('position', 'absolute'); that.element.stop().animate({ '__coverflow_frame': index || that.options.index }, { 'easing': that.options.easing, 'duration': duration || 0, 'step': function(now, fx) { that._frame(now);
that.currentIndex = Math.max(0, Math.min(Math.round(now), covercount - 1)); if (previous !== that.currentIndex) { previous = that.currentIndex; that._callback('change'); if (that.currentIndex === that.options.index) { triggered = true; } } }, 'complete': function() { that.currentIndex = that.options.index; if (!triggered) { that._callback('change'); } that._callback('select'); } }); } }); }(jQuery));