vegas.js 20 KB

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