vegas.js 19 KB

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