vegas.js 20 KB

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