Browse Source

attempt to condense render loop logic

fix bug in render cycle per-animation-frame preventing animations

fix bug in recursive format function to properly parse all replacements

adjust preload behavior to be part of render:

1. fixes long-term reload
2. reduces preload overhead to +-10% of the list
Casey DeLorme 2 years ago
parent
commit
92e1199928
1 changed files with 64 additions and 40 deletions
  1. 64 40
      show.js

+ 64 - 40
show.js

@@ -5,13 +5,14 @@
 		this.elapsed = 0;
 		this.playing = true;
 		this.list = [];
+		this.preloaded = {};
 		this.updated = Date.now();
 		this.delay = !isNaN(delay) ? delay : 3000;
 		this.parent = parent;
 		this.parent.innerHTML = '';
-		this.parent.appendChild(document.createElement('span'));
-		(async () => {
-			await this.parse(files);
+		this.parent.appendChild(document.createElement('img'));
+		(() => {
+			this.parse(files);
 			window.requestAnimationFrame(() => { this.render(); });
 			window.addEventListener('keyup', (e) => {
 				if (!e.keyCode) return;
@@ -36,51 +37,71 @@
 
 	Show.prototype.format = function(f, a) {
 		f = f.replaceAll('%s', a);
-		for (let start = f.indexOf('%'); f.length > start+3 && f[start+3] === 's'; ) {
+		for (let start = f.indexOf('%'); start != -1 && f.length > start+3 && f[start+3] === 's'; start = f.indexOf('%')) {
 			let c = String(f[start+1]);
 			let w = parseInt(f[start+2]);
 			let r = (String(a).length >= w) ? a : (new Array(w).join(c)).concat(a).slice(-w);
 			f = f.replace(f.slice(start, start+4), r);
 		}
-		return f;
+		return f
 	};
 
-	Show.prototype.preload = async function(file, delay) {
-		const o = { p: file };
-		if (o.p.endsWith('.mp4') || o.p.endsWith('.webm') || o.p.endsWith('.ogg')) {
-			o.e = document.createElement('video');
-			o.e.addEventListener('error', () => { this.list.splice(this.list.indexOf(o), 1); }, {once: true});
-			o.e.addEventListener('canplay', () => { o.d = (isNaN(delay) ? 1 : delay) * o.e.duration * 1000; }, {once: true});
-			o.e.muted = true;
-			o.e.loop = true;
-			o.e.src = o.p;
+	Show.prototype.generate = function(idx) {
+		let o = this.list[idx];
+		let el;
+		if (o.file.endsWith('.mp4') || o.file.endsWith('.webm') || o.file.endsWith('.ogg')) {
+			el = document.createElement('video');
+			el.muted = true;
+			el.loop = true;
 		} else {
-			o.e = document.createElement('img');
-			o.e.addEventListener('error', () => { this.list.splice(this.list.indexOf(o), 1); }, {once: true});
-			o.d = isNaN(delay) ? this.delay : delay;
-			o.e.src = o.p;
+			el = document.createElement('img');
 		}
+		el.src = o.file;
+		el.addEventListener('error', () => { this.list.splice(this.list.indexOf(o), 1); }, {once: true});
+		return el;
+	}
+
+	Show.prototype.preload = function() {
+		let partial = Math.max(Math.trunc(.1 * this.list.length), 1);
+		let position = partial > this.index ? this.list.length - Math.abs(partial - this.index) : this.index - partial;
+		for (let i = 0; i <= partial*2; i++) {
+			let l = (position+i)%this.list.length;
+			let o = this.list[l];
+			if (!this.preloaded.hasOwnProperty(o.file)) {
+				this.preloaded[o.file] = this.generate(l);
+				setTimeout(() => { delete this.preloaded[o.file]; }, 2*(o.delay ? o.delay : this.delay));
+			}
+		}
+	}
+
+	Show.prototype.add = function(file, delay) {
+		let o = {file: file, delay: delay};
 		this.list.push(o);
+		if (o.file.endsWith('.mp4') || o.file.endsWith('.webm') || o.file.endsWith('.ogg')) {
+			let el = document.createElement('video');
+			el.addEventListener('canplay', () => { o.delay = (isNaN(o.delay) ? 1 : o.delay) * Math.trunc(el.duration * 1000); }, {once: true});
+			el.src = o.file;
+		}
 	}
 
-	Show.prototype.parse = async function(files) {
+	Show.prototype.parse = function(files) {
 		if (!(files instanceof Array)) return;
 		files.map((o) => {
 			if (typeof o === 'string') {
-				this.preload(o);
+				this.add(o);
 			} else if (o instanceof Object && !(o instanceof Array) && typeof o.file !== 'undefined' && typeof o.file === 'string') {
 				if (o.file.indexOf('%') > -1 && typeof o.range !== 'undefined' && o.range instanceof Array) {
 					if (o.range.length === 2 && !(isNaN(o.range[2]) && isNaN(o.range[1])) && parseInt(o.range[0]) < parseInt(o.range[1])) {
 						for (let i = parseInt(o.range[0]); i <= parseInt(o.range[1]); i++) {
-							this.preload(this.format(o.file, i), o.delay);
+							this.add(this.format(o.file, i), o.delay);
 						}
 					} else {
 						for (let i of o.range) {
-							this.preload(this.format(o.file, i), o.delay);
+							this.add(this.format(o.file, i), o.delay);
 						}
 					}
 				} else {
-					this.preload(o.file, o.delay);
+					this.add(o.file, o.delay);
 				}
 			}
 		});
@@ -89,25 +110,28 @@
 	Show.prototype.render = function() {
 		if (!this.list.length) return;
 		let d = Date.now();
+		let el = this.parent.firstChild;
+		let vid = (el.tagName == 'VIDEO') ? 'video' : 'natural';
+		this.parent.firstChild.className = (el[vid+'Width'] / el[vid+'Height'] < this.parent.clientWidth / this.parent.clientHeight) ? 'fillheight' : 'fillwidth';
 		let o = this.list[this.index];
-		let vid = (o.e.tagName == 'VIDEO') ? 'video' : 'natural';
-		if (o.e.src === this.parent.firstChild.src) o.e.className = this.parent.firstChild.className = (o.e[vid+'Width'] / o.e[vid+'Height'] < this.parent.clientWidth / this.parent.clientHeight) ? 'fillheight' : 'fillwidth';
-		if (this.parent.firstChild !== o.e && !o.loading) {
-			o.loading = true;
-			let parent = this.parent;
-			o.e.addEventListener(vid === 'video' ? 'canplay' : 'load', () => {
-				if (vid === 'video') {
-					parent.replaceChild(o.e, parent.firstChild);
-				} else if (vid === 'natural' && parent.firstChild.tagName === 'VIDEO') {
-					parent.replaceChild(document.createElement('img'), parent.firstChild);
-				}
-				if (parent.firstChild.tagName === 'IMG') parent.firstChild.src = o.p;
-				if (vid === 'video') o.e.play();
-				delete o.loading
-			}, {once: true});
-			let tmp = o.e.src; o.e.src = ''; o.e.src = tmp;
+		if (this.current != this.index) {
+			this.current = this.index;
+			this.preload();
+			let el = this.preloaded[o.file];
+			let vid = (el.tagName == 'VIDEO') ? 'video' : 'natural';
+			if (el.tagName !== this.parent.firstChild.tagName) this.parent.replaceChild(el.cloneNode(), this.parent.firstChild);
+			if (vid === 'video') {
+				if (this.parent.firstChild.src !== el.src) this.parent.firstChild.src = el.src;
+				this.parent.firstChild.currentTime = 0;
+				this.parent.firstChild.loop = true;
+				this.parent.firstChild.muted = true;
+				this.parent.firstChild.play();
+			} else {
+				this.parent.firstChild.src = el.src;
+			}
 		}
-		if (this.playing && document.hasFocus() && (this.elapsed += (d - this.updated)) && this.elapsed >= o.d) this.next();
+		if (this.playing && document.hasFocus()) this.elapsed += (d - this.updated);
+		if (this.elapsed >= (o.delay | this.delay)) this.next();
 		s.updated = d;
 		window.requestAnimationFrame(() => { this.render(); });
 	}