vegas.js 16 KB

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