vegas.js 23 KB

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