show.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. 'use strict';
  2. (() => {
  3. function Show(parent, files, delay) {
  4. this.index = 0;
  5. this.elapsed = 0;
  6. this.playing = true;
  7. this.list = [];
  8. this.preloaded = {};
  9. this.updated = Date.now();
  10. this.delay = !isNaN(delay) ? delay : 3000;
  11. this.parent = parent;
  12. this.parent.innerHTML = '';
  13. this.parent.appendChild(document.createElement('img'));
  14. (() => {
  15. this.parse(files);
  16. window.requestAnimationFrame(() => { this.render(); });
  17. window.addEventListener('keyup', (e) => {
  18. if (!e.keyCode) return;
  19. if (e.keyCode === 32) { this.toggle(); }
  20. else if (e.keyCode === 37) { this.prev(); }
  21. else if (e.keyCode === 39) { this.next(); }
  22. }, false);
  23. })();
  24. };
  25. Show.prototype.toggle = function() { this.playing = !this.playing; };
  26. Show.prototype.next = function() {
  27. if (++this.index >= this.list.length) this.index = 0;
  28. this.elapsed = 0;
  29. };
  30. Show.prototype.prev = function() {
  31. if (--this.index < 0) this.index = this.list.length-1;
  32. this.elapsed = 0;
  33. };
  34. Show.prototype.format = function(f, a) {
  35. f = f.replaceAll('%s', a);
  36. for (let start = f.indexOf('%'); start != -1 && f.length > start+3 && f[start+3] === 's'; start = f.indexOf('%')) {
  37. let c = String(f[start+1]);
  38. let w = parseInt(f[start+2]);
  39. let r = (String(a).length >= w) ? a : (new Array(w).join(c)).concat(a).slice(-w);
  40. f = f.replace(f.slice(start, start+4), r);
  41. }
  42. return f
  43. };
  44. Show.prototype.generate = function(idx) {
  45. let o = this.list[idx];
  46. let el;
  47. if (o.file.endsWith('.mp4') || o.file.endsWith('.webm') || o.file.endsWith('.ogg')) {
  48. el = document.createElement('video');
  49. el.muted = true;
  50. el.loop = true;
  51. } else {
  52. el = document.createElement('img');
  53. }
  54. el.src = o.file;
  55. el.addEventListener('error', () => { this.list.splice(this.list.indexOf(o), 1); }, {once: true});
  56. return el;
  57. }
  58. Show.prototype.preload = function() {
  59. let partial = Math.max(Math.trunc(.1 * this.list.length), 1);
  60. let position = partial > this.index ? this.list.length - Math.abs(partial - this.index) : this.index - partial;
  61. for (let i = 0; i <= partial*2; i++) {
  62. let l = (position+i)%this.list.length;
  63. let o = this.list[l];
  64. if (!this.preloaded.hasOwnProperty(o.file)) {
  65. this.preloaded[o.file] = this.generate(l);
  66. setTimeout(() => { delete this.preloaded[o.file]; }, 2*(o.delay ? o.delay : this.delay));
  67. }
  68. }
  69. }
  70. Show.prototype.add = function(file, delay) {
  71. let o = {file: file, delay: delay};
  72. this.list.push(o);
  73. if (o.file.endsWith('.mp4') || o.file.endsWith('.webm') || o.file.endsWith('.ogg')) {
  74. let el = document.createElement('video');
  75. el.addEventListener('canplay', () => { o.delay = (isNaN(o.delay) ? 1 : o.delay) * Math.trunc(el.duration * 1000); }, {once: true});
  76. el.src = o.file;
  77. }
  78. }
  79. Show.prototype.parse = function(files) {
  80. if (!(files instanceof Array)) return;
  81. files.map((o) => {
  82. if (typeof o === 'string') {
  83. this.add(o);
  84. } else if (o instanceof Object && !(o instanceof Array) && typeof o.file !== 'undefined' && typeof o.file === 'string') {
  85. if (o.file.indexOf('%') > -1 && typeof o.range !== 'undefined' && o.range instanceof Array) {
  86. if (o.range.length === 2 && !(isNaN(o.range[2]) && isNaN(o.range[1])) && parseInt(o.range[0]) < parseInt(o.range[1])) {
  87. for (let i = parseInt(o.range[0]); i <= parseInt(o.range[1]); i++) {
  88. this.add(this.format(o.file, i), o.delay);
  89. }
  90. } else {
  91. for (let i of o.range) {
  92. this.add(this.format(o.file, i), o.delay);
  93. }
  94. }
  95. } else {
  96. this.add(o.file, o.delay);
  97. }
  98. }
  99. });
  100. };
  101. Show.prototype.render = function() {
  102. if (!this.list.length) return;
  103. let d = Date.now();
  104. let el = this.parent.firstChild;
  105. let vid = (el.tagName == 'VIDEO') ? 'video' : 'natural';
  106. this.parent.firstChild.className = (el[vid+'Width'] / el[vid+'Height'] < this.parent.clientWidth / this.parent.clientHeight) ? 'fillheight' : 'fillwidth';
  107. let o = this.list[this.index];
  108. if (this.current != this.index) {
  109. this.current = this.index;
  110. this.preload();
  111. let el = this.preloaded[o.file];
  112. let vid = (el.tagName == 'VIDEO') ? 'video' : 'natural';
  113. if (el.tagName !== this.parent.firstChild.tagName) this.parent.replaceChild(el.cloneNode(), this.parent.firstChild);
  114. if (vid === 'video') {
  115. if (this.parent.firstChild.src !== el.src) this.parent.firstChild.src = el.src;
  116. this.parent.firstChild.currentTime = 0;
  117. this.parent.firstChild.loop = true;
  118. this.parent.firstChild.muted = true;
  119. this.parent.firstChild.play();
  120. } else {
  121. this.parent.firstChild.src = el.src;
  122. }
  123. }
  124. if (this.playing && document.hasFocus()) this.elapsed += (d - this.updated);
  125. if (this.elapsed >= (o.delay | this.delay)) this.next();
  126. s.updated = d;
  127. window.requestAnimationFrame(() => { this.render(); });
  128. }
  129. window.Show = Show;
  130. })();