jquery.serialScroll.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*!
  2. * jQuery.serialScroll
  3. * Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
  4. * Licensed under MIT.
  5. * @projectDescription Animated scrolling of series with jQuery
  6. * @author Ariel Flesler
  7. * @version 1.3.0
  8. * https://github.com/flesler/jquery.serialScroll
  9. */
  10. ;(function($) {
  11. var NAMESPACE = '.serialScroll';
  12. var $serialScroll = $.serialScroll = function(settings) {
  13. return $(window).serialScroll(settings);
  14. };
  15. // Many of these defaults, belong to jQuery.ScrollTo, check it's demo for an example of each option.
  16. // @link {http://demos.flesler.com/jquery/scrollTo/ ScrollTo's Demo}
  17. $serialScroll.defaults = {// the defaults are public and can be overriden.
  18. duration:1000, // how long to animate.
  19. axis:'x', // which of top and left should be scrolled
  20. event:'click', // on which event to react.
  21. start:0, // first element (zero-based index)
  22. step: 1, // how many elements to scroll on each action
  23. lock:true,// ignore events if already animating
  24. cycle:true, // cycle endlessly (constant velocity)
  25. constant:true // use contant speed ?
  26. /*
  27. navigation:null,// if specified, it's a selector to a collection of items to navigate the container
  28. target:window, // if specified, it's a selector to the element to be scrolled.
  29. interval:0, // it's the number of milliseconds to automatically go to the next
  30. lazy:false,// go find the elements each time (allows AJAX or JS content, or reordering)
  31. stop:false, // stop any previous animations to avoid queueing
  32. force:false,// force the scroll to the first element on start ?
  33. jump: false,// if true, when the event is triggered on an element, the pane scrolls to it
  34. items:null, // selector to the items (relative to the matched elements)
  35. prev:null, // selector to the 'prev' button
  36. next:null, // selector to the 'next' button
  37. onBefore: function() {}, // function called before scrolling, if it returns false, the event is ignored
  38. exclude:0 // exclude the last x elements, so we cannot scroll past the end
  39. */
  40. };
  41. $.fn.serialScroll = function(options) {
  42. return this.each(function() {
  43. var
  44. settings = $.extend({}, $serialScroll.defaults, options),
  45. // this one is just to get shorter code when compressed
  46. event = settings.event,
  47. // ditto
  48. step = settings.step,
  49. // ditto
  50. lazy = settings.lazy,
  51. // if a target is specified, then everything's relative to 'this'.
  52. context = settings.target ? this : document,
  53. // the element to be scrolled (will carry all the events)
  54. $pane = $(settings.target || this, context),
  55. // will be reused, save it into a variable
  56. pane = $pane[0],
  57. // will hold a lazy list of elements
  58. items = settings.items,
  59. // index of the currently selected item
  60. active = settings.start,
  61. // boolean, do automatic scrolling or not
  62. auto = settings.interval,
  63. // save it now to make the code shorter
  64. nav = settings.navigation,
  65. // holds the interval id
  66. timer;
  67. // Incompatible with $().animate()
  68. delete settings.step;
  69. delete settings.start;
  70. // If no match, just ignore
  71. if (!pane) return;
  72. // if not lazy, save the items now
  73. if (!lazy) {
  74. items = getItems();
  75. }
  76. // generate an initial call
  77. if (settings.force || auto) {
  78. jump({}, active);
  79. }
  80. // Button binding, optional
  81. $(settings.prev||[], context).bind(event, -step, move);
  82. $(settings.next||[], context).bind(event, step, move);
  83. // Custom events bound to the container
  84. if (!pane._bound_) {
  85. $pane
  86. // You can trigger with just 'prev'
  87. .bind('prev'+NAMESPACE, -step, move)
  88. // f.e: $(container).trigger('next');
  89. .bind('next'+NAMESPACE, step, move)
  90. // f.e: $(container).trigger('goto', 4);
  91. .bind('goto'+NAMESPACE, jump);
  92. }
  93. if (auto) {
  94. $pane
  95. .bind('start'+NAMESPACE, function(e) {
  96. if (!auto) {
  97. clear();
  98. auto = true;
  99. next();
  100. }
  101. })
  102. .bind('stop'+NAMESPACE, function() {
  103. clear();
  104. auto = false;
  105. });
  106. }
  107. // Let serialScroll know that the index changed externally
  108. $pane.bind('notify'+NAMESPACE, function(e, elem) {
  109. var i = index(elem);
  110. if (i > -1) {
  111. active = i;
  112. }
  113. });
  114. // Avoid many bindings
  115. pane._bound_ = true;
  116. // Can't use jump if using lazy items and a non-bubbling event
  117. if (settings.jump) {
  118. (lazy ? $pane : getItems()).bind(event, function(e) {
  119. jump(e, index(e.target));
  120. });
  121. }
  122. if (nav) {
  123. nav = $(nav, context).bind(event, function(e) {
  124. e.data = Math.round(getItems().length / nav.length) * nav.index(this);
  125. jump(e, this);
  126. });
  127. }
  128. function move(e) {
  129. e.data += active;
  130. jump(e, this);
  131. }
  132. function jump(e, pos) {
  133. if (!$.isNumeric(pos)) {
  134. pos = e.data;
  135. }
  136. var n,
  137. // Is a real event triggering ?
  138. real = e.type,
  139. // Handle a possible exclude
  140. $items = settings.exclude ? getItems().slice(0,-settings.exclude) : getItems(),
  141. limit = $items.length - 1,
  142. elem = $items[pos],
  143. duration = settings.duration;
  144. if (real) e.preventDefault();
  145. if (auto) {
  146. // clear any possible automatic scrolling.
  147. clear();
  148. timer = setTimeout(next, settings.interval);
  149. }
  150. // exceeded the limits
  151. if (!elem) {
  152. n = pos < 0 ? 0 : limit;
  153. // we exceeded for the first time
  154. if (active !== n) {
  155. pos = n;
  156. // this is a bad case
  157. } else if (!settings.cycle) {
  158. return;
  159. // invert, go to the other side
  160. } else {
  161. pos = limit - n;
  162. }
  163. elem = $items[pos];
  164. }
  165. // no animations while busy
  166. if (!elem || settings.lock && $pane.is(':animated') ||
  167. real && settings.onBefore &&
  168. // Allow implementors to cancel scrolling
  169. settings.onBefore(e, elem, $pane, getItems(), pos) === false) return;
  170. if (settings.stop) {
  171. // remove all running animations
  172. $pane.stop(true);
  173. }
  174. if (settings.constant) {
  175. // keep constant velocity
  176. duration = Math.abs(duration/step * (active - pos));
  177. }
  178. $pane.scrollTo(elem, duration, settings);
  179. // in case serialScroll was called on this elemement more than once.
  180. trigger('notify', pos);
  181. }
  182. function next() {
  183. trigger('next');
  184. }
  185. function clear() {
  186. clearTimeout(timer);
  187. }
  188. function getItems() {
  189. return $(items, pane);
  190. }
  191. // I'll use the namespace to avoid conflicts
  192. function trigger(event) {
  193. $pane.trigger(
  194. event+NAMESPACE,
  195. [].slice.call(arguments,1)
  196. );
  197. }
  198. function index(elem) {
  199. // Already a number
  200. if ($.isNumeric(elem)) {
  201. return elem;
  202. }
  203. var $items = getItems(), i;
  204. // See if it matches or one of its ancestors
  205. while((i = $items.index(elem)) === -1 && elem !== pane) {
  206. elem = elem.parentNode;
  207. }
  208. return i;
  209. }
  210. });
  211. };
  212. })(jQuery);