vegas.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. /* global jQuery, Zepto */
  2. (function ($) {
  3. 'use strict';
  4. var defaults = {
  5. slide: 0,
  6. delay: 5000,
  7. preload: false,
  8. preloadImage: false,
  9. preloadVideo: false,
  10. timer: true,
  11. overlay: false,
  12. autoplay: true,
  13. shuffle: false,
  14. cover: true,
  15. color: null,
  16. align: 'center',
  17. valign: 'center',
  18. transition: 'fade',
  19. transitionDuration: 1000,
  20. transitionRegister: [],
  21. animation: null,
  22. animationDuration: 'auto',
  23. animationRegister: [],
  24. init: function () {},
  25. play: function () {},
  26. pause: function () {},
  27. walk: function () {},
  28. slides: [
  29. // {
  30. // src: null,
  31. // color: null,
  32. // delay: null,
  33. // align: null,
  34. // valign: null,
  35. // transition: null,
  36. // transitionDuration: null,
  37. // animation: null,
  38. // animationDuration: null,
  39. // cover: true,
  40. // videos: []
  41. // }
  42. // ...
  43. ]
  44. };
  45. var videoCache = {};
  46. var Vegas = function (elmt, options) {
  47. this.elmt = elmt;
  48. this.settings = $.extend({}, defaults, $.vegas.defaults, options);
  49. this.slide = this.settings.slide;
  50. this.total = this.settings.slides.length;
  51. this.noshow = this.total < 2;
  52. this.paused = !this.settings.autoplay || this.noshow;
  53. this.$elmt = $(elmt);
  54. this.$timer = null;
  55. this.$overlay = null;
  56. this.$slide = null;
  57. this.timeout = null;
  58. this.transitions = [
  59. 'fade', 'fade2',
  60. 'blur', 'blur2',
  61. 'flash', 'flash2',
  62. 'negative', 'negative2',
  63. 'burn', 'burn2',
  64. 'slideLeft', 'slideLeft2',
  65. 'slideRight', 'slideRight2',
  66. 'slideUp', 'slideUp2',
  67. 'slideDown', 'slideDown2',
  68. 'zoomIn', 'zoomIn2',
  69. 'zoomOut', 'zoomOut2',
  70. 'swirlLeft', 'swirlLeft2',
  71. 'swirlRight', 'swirlRight2'
  72. ];
  73. this.animations = [
  74. 'kenburns',
  75. 'kenburnsLeft', 'kenburnsRight',
  76. 'kenburnsUp', 'kenburnsUpLeft', 'kenburnsUpRight',
  77. 'kenburnsDown', 'kenburnsDownLeft', 'kenburnsDownRight'
  78. ];
  79. if (this.settings.transitionRegister instanceof Array === false) {
  80. this.settings.transitionRegister = [ this.settings.transitionRegister ];
  81. }
  82. if (this.settings.animationRegister instanceof Array === false) {
  83. this.settings.animationRegister = [ this.settings.animationRegister ];
  84. }
  85. this.transitions = this.transitions.concat(this.settings.transitionRegister);
  86. this.animations = this.animations.concat(this.settings.animationRegister);
  87. this.support = {
  88. objectFit: 'objectFit' in document.body.style,
  89. transition: 'transition' in document.body.style || 'WebkitTransition' in document.body.style,
  90. video: $.vegas.isVideoCompatible()
  91. };
  92. if (this.settings.shuffle === true) {
  93. this.shuffle();
  94. }
  95. this._init();
  96. };
  97. Vegas.prototype = {
  98. _init: function () {
  99. var $wrapper,
  100. $overlay,
  101. $timer,
  102. isBody = this.elmt.tagName === 'BODY',
  103. timer = this.settings.timer,
  104. overlay = this.settings.overlay,
  105. self = this;
  106. // Preloading
  107. this._preload();
  108. // Wrapper with content
  109. if (!isBody) {
  110. this.$elmt.css('height', this.$elmt.css('height'));
  111. $wrapper = $('<div class="vegas-wrapper">')
  112. .css('overflow', this.$elmt.css('overflow'))
  113. .css('padding', this.$elmt.css('padding'));
  114. // Some browsers don't compute padding shorthand
  115. if (!this.$elmt.css('padding')) {
  116. $wrapper
  117. .css('padding-top', this.$elmt.css('padding-top'))
  118. .css('padding-bottom', this.$elmt.css('padding-bottom'))
  119. .css('padding-left', this.$elmt.css('padding-left'))
  120. .css('padding-right', this.$elmt.css('padding-right'));
  121. }
  122. this.$elmt.clone(true).children().appendTo($wrapper);
  123. this.elmt.innerHTML = '';
  124. }
  125. // Timer
  126. if (timer && this.support.transition) {
  127. $timer = $('<div class="vegas-timer"><div class="vegas-timer-progress">');
  128. this.$timer = $timer;
  129. this.$elmt.prepend($timer);
  130. }
  131. // Overlay
  132. if (overlay) {
  133. $overlay = $('<div class="vegas-overlay">');
  134. if (typeof overlay === 'string') {
  135. $overlay.css('background-image', 'url(' + overlay + ')');
  136. }
  137. this.$overlay = $overlay;
  138. this.$elmt.prepend($overlay);
  139. }
  140. // Container
  141. this.$elmt.addClass('vegas-container');
  142. if (!isBody) {
  143. this.$elmt.append($wrapper);
  144. }
  145. setTimeout(function () {
  146. self.trigger('init');
  147. self._goto(self.slide);
  148. if (self.settings.autoplay) {
  149. self.trigger('play');
  150. }
  151. }, 1);
  152. },
  153. _preload: function () {
  154. var video, img, i;
  155. for (i = 0; i < this.settings.slides.length; i++) {
  156. if (this.settings.preload || this.settings.preloadImages) {
  157. if (this.settings.slides[i].src) {
  158. img = new Image();
  159. img.src = this.settings.slides[i].src;
  160. }
  161. }
  162. if (this.settings.preload || this.settings.preloadVideos) {
  163. if (this.support.video && this.settings.slides[i].video) {
  164. video = this._video(this.settings.slides[i].video);
  165. }
  166. }
  167. }
  168. },
  169. _random: function (array) {
  170. return array[Math.floor(Math.random() * (array.length - 1))];
  171. },
  172. _slideShow: function () {
  173. var self = this;
  174. if (this.total > 1 && !this.paused && !this.noshow) {
  175. this.timeout = setTimeout(function () {
  176. self.next();
  177. }, this._options('delay'));
  178. }
  179. },
  180. _timer: function (state) {
  181. var self = this;
  182. clearTimeout(this.timeout);
  183. if (!this.$timer) {
  184. return;
  185. }
  186. this.$timer
  187. .removeClass('vegas-timer-running')
  188. .find('div')
  189. .css('transition-duration', '0ms');
  190. if (this.paused || this.noshow) {
  191. return;
  192. }
  193. if (state) {
  194. setTimeout(function () {
  195. self.$timer
  196. .addClass('vegas-timer-running')
  197. .find('div')
  198. .css('transition-duration', self._options('delay') - 100 + 'ms');
  199. }, 100);
  200. }
  201. },
  202. _video: function (srcs) {
  203. var video,
  204. source,
  205. cacheKey = srcs.toString();
  206. if (videoCache[cacheKey]) {
  207. return videoCache[cacheKey];
  208. }
  209. if (srcs instanceof Array === false) {
  210. srcs = [ srcs ];
  211. }
  212. video = document.createElement('video');
  213. video.muted = true;
  214. video.preload = true;
  215. srcs.forEach(function (src) {
  216. source = document.createElement('source');
  217. source.src = src;
  218. video.appendChild(source);
  219. });
  220. videoCache[cacheKey] = video;
  221. return video;
  222. },
  223. _options: function (key, i) {
  224. if (i === undefined) {
  225. i = this.slide;
  226. }
  227. if (this.settings.slides[i][key] !== undefined) {
  228. return this.settings.slides[i][key];
  229. }
  230. return this.settings[key];
  231. },
  232. _goto: function (nb) {
  233. if (typeof this.settings.slides[nb] === 'undefined') {
  234. nb = 0;
  235. }
  236. this.slide = nb;
  237. var $slide,
  238. $inner,
  239. $video,
  240. $slides = this.$elmt.children('.vegas-slide'),
  241. src = this.settings.slides[nb].src,
  242. videos = this.settings.slides[nb].video,
  243. delay = this._options('delay'),
  244. align = this._options('align'),
  245. valign = this._options('valign'),
  246. color = this._options('color') || this.$elmt.css('background-color'),
  247. cover = this._options('cover') ? 'cover' : 'contain',
  248. self = this,
  249. total = $slides.length,
  250. video,
  251. img;
  252. var transition = this._options('transition'),
  253. transitionDuration = this._options('transitionDuration'),
  254. animation = this._options('animation' ),
  255. animationDuration = this._options('animationDuration');
  256. if (transition === 'random' || transition instanceof Array) {
  257. if (transition instanceof Array) {
  258. transition = this._random(transition);
  259. } else {
  260. transition = this._random(this.transitions);
  261. }
  262. }
  263. if (animation === 'random' || animation instanceof Array) {
  264. if (animation instanceof Array) {
  265. animation = this._random(animation);
  266. } else {
  267. animation = this._random(this.animations);
  268. }
  269. }
  270. if (transitionDuration === 'auto' || transitionDuration > delay) {
  271. transitionDuration = delay;
  272. }
  273. if (animationDuration === 'auto') {
  274. animationDuration = delay;
  275. }
  276. $slide = $('<div class="vegas-slide"></div>');
  277. if (this.support.transition && transition) {
  278. $slide.addClass('vegas-transition-' + transition);
  279. }
  280. // Video ?
  281. if (this.support.video && videos) {
  282. video = this._video(videos);
  283. $video = $(video)
  284. .addClass('vegas-video')
  285. .css('background-color', color);
  286. if (this.support.objectFit) {
  287. $video
  288. .css('object-position', align + ' ' + valign)
  289. .css('object-fit', cover)
  290. .css('width', '100%')
  291. .css('height', '100%');
  292. } else if (cover === 'contain') {
  293. $video
  294. .css('width', '100%')
  295. .css('height', '100%');
  296. }
  297. $slide.append($video);
  298. // Image ?
  299. } else {
  300. img = new Image();
  301. $inner = $('<div class="vegas-slide-inner"></div>')
  302. .css('background-image', 'url(' + src + ')')
  303. .css('background-color', color)
  304. .css('background-position', align + ' ' + valign)
  305. .css('background-size', cover);
  306. if (this.support.transition && animation) {
  307. $inner
  308. .addClass('vegas-animation-' + animation)
  309. .css('animation-duration', animationDuration + 'ms');
  310. }
  311. $slide.append($inner);
  312. }
  313. if (!this.support.transition) {
  314. $slide.css('display', 'none');
  315. }
  316. if (total) {
  317. $slides.eq(total - 1).after($slide);
  318. } else {
  319. this.$elmt.prepend($slide);
  320. }
  321. // $slides
  322. // .css('transition', 'all 0ms')
  323. // .each(function () {
  324. // this.className = 'vegas-slide';
  325. // if (this.tagName === 'VIDEO') {
  326. // this.className += ' vegas-video';
  327. // }
  328. // if (transition) {
  329. // this.className += ' vegas-transition-' + transition;
  330. // this.className += ' vegas-transition-' + transition + '-in';
  331. // }
  332. // }
  333. // );
  334. self._timer(false);
  335. function go () {
  336. self._timer(true);
  337. setTimeout(function () {
  338. if (transition) {
  339. if (self.support.transition) {
  340. $slides
  341. .css('transition', 'all ' + transitionDuration + 'ms')
  342. .addClass('vegas-transition-' + transition + '-out');
  343. $slide
  344. .css('transition', 'all ' + transitionDuration + 'ms')
  345. .addClass('vegas-transition-' + transition + '-in');
  346. } else {
  347. $slide.fadeIn(transitionDuration);
  348. }
  349. }
  350. for (var i = 0; i < $slides.length - 4; i++) {
  351. $slides.eq(i).remove();
  352. }
  353. self.trigger('walk');
  354. self._slideShow();
  355. }, 100);
  356. }
  357. if (video) {
  358. if (video.readyState === 4) {
  359. video.currentTime = 0;
  360. video.play();
  361. go();
  362. } else {
  363. video.oncanplay = function () {
  364. video.play();
  365. if (!video._started) {
  366. video._started = true;
  367. go();
  368. }
  369. };
  370. }
  371. } else {
  372. img.src = src;
  373. img.onload = go;
  374. }
  375. },
  376. shuffle: function () {
  377. var temp,
  378. rand;
  379. for (var i = this.total - 1; i > 0; i--) {
  380. rand = Math.floor(Math.random() * (i + 1));
  381. temp = this.settings.slides[i];
  382. this.settings.slides[i] = this.settings.slides[rand];
  383. this.settings.slides[rand] = temp;
  384. }
  385. },
  386. play: function () {
  387. if (this.paused) {
  388. this.paused = false;
  389. this.next();
  390. this.trigger('play');
  391. }
  392. },
  393. pause: function () {
  394. this._timer(false);
  395. this.paused = true;
  396. this.trigger('pause');
  397. },
  398. toggle: function () {
  399. if (this.paused) {
  400. this.play();
  401. } else {
  402. this.pause();
  403. }
  404. },
  405. playing: function () {
  406. return !this.paused && !this.noshow;
  407. },
  408. current: function (advanced) {
  409. if (advanced) {
  410. return {
  411. slide: this.slide,
  412. data: this.settings.slides[this.slide]
  413. };
  414. }
  415. return this.slide;
  416. },
  417. jump: function (nb) {
  418. if (nb < 0 || nb > this.total - 1 || nb === this.slide) {
  419. return;
  420. }
  421. this.slide = nb;
  422. this._goto(this.slide);
  423. },
  424. next: function () {
  425. this.slide++;
  426. if (this.slide >= this.total) {
  427. this.slide = 0;
  428. }
  429. this._goto(this.slide);
  430. },
  431. previous: function () {
  432. this.slide--;
  433. if (this.slide < 0) {
  434. this.slide = this.total - 1;
  435. }
  436. this._goto(this.slide);
  437. },
  438. trigger: function (fn) {
  439. var params = [];
  440. if (fn === 'init') {
  441. params = [ this.settings ];
  442. } else {
  443. params = [
  444. this.slide,
  445. this.settings.slides[this.slide]
  446. ];
  447. }
  448. this.$elmt.trigger('vegas' + fn, params);
  449. if (typeof this.settings[fn] === 'function') {
  450. this.settings[fn].apply(this.$elmt, params);
  451. }
  452. },
  453. options: function (key, value) {
  454. var oldSlides = this.settings.slides.slice();
  455. if (typeof key === 'object') {
  456. this.settings = $.extend({}, defaults, $.vegas.defaults, key);
  457. } else if (typeof key === 'string') {
  458. if (value === undefined) {
  459. return this.settings[key];
  460. }
  461. this.settings[key] = value;
  462. } else {
  463. return this.settings;
  464. }
  465. // In case slides have changed
  466. if (this.settings.slides !== oldSlides) {
  467. this.total = this.settings.slides.length;
  468. this.noshow = this.total < 2;
  469. this._preload();
  470. }
  471. }
  472. };
  473. $.fn.vegas = function(options) {
  474. var args = arguments,
  475. error = false,
  476. returns;
  477. if (options === undefined || typeof options === 'object') {
  478. return this.each(function () {
  479. if (!this._vegas) {
  480. this._vegas = new Vegas(this, options);
  481. }
  482. });
  483. } else if (typeof options === 'string') {
  484. this.each(function () {
  485. var instance = this._vegas;
  486. if (!instance) {
  487. throw new Error('No Vegas applied to this element.');
  488. }
  489. if (typeof instance[options] === 'function' && options[0] !== '_') {
  490. returns = instance[options].apply(instance, [].slice.call(args, 1));
  491. } else {
  492. error = true;
  493. }
  494. });
  495. if (error) {
  496. throw new Error('No method "' + options + '" in Vegas.');
  497. }
  498. return returns !== undefined ? returns : this;
  499. }
  500. };
  501. $.vegas = {};
  502. $.vegas.defaults = defaults;
  503. $.vegas.isVideoCompatible = function () {
  504. return !/(Android|webOS|Phone|iPad|iPod|BlackBerry|Windows Phone)/i.test(navigator.userAgent);
  505. };
  506. })(window.jQuery || window.Zepto);