vegas.js 17 KB

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