vegas.js 19 KB

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