Javascript 関数を繰り返して呼び出す
callBack 関数が true を返す間、定期的に関数を呼び出す
class Repeater { #cnt = null; #timerId = null; #loop = function () { if (this.#cnt && this.cbFunc (--this.#cnt) && this.#cnt) this.#timerId = setTimeout (this.#loop.bind (this), this.interval); else this.stop (); } //_______ constructor (cbFunc, count, interval) { this.cbFunc = cbFunc; this.count = this.#cnt = count; this.interval = interval; } start (force = false) { this.stop (); if (force) this.#cnt = this.count; this.#loop (); return this; } stop (force = false) { if (this.#timerId) this.#timerId = clearTimeout (this.#timerId) || null; if (force) this.#cnt = 0; return this; } //_______ static create (cbFunc, count = 1, interval = 1000) { if ('function' !== typeof cbFunc) throw new Error ('関数ではありません'); let obj = new this (cbFunc, count, interval); return obj.start (); } }
callBack 関数が true を返す間、定期的に関数を呼び出す。その2
勉強のため promise を使ってみた。
class AsyncRepeater { #cnt = null; #timerId = null; #loop = function () { if (this.#cnt && this.cbFunc (--this.#cnt) && this.#cnt) //マイナスに対し ―∞ へ this.#timerId = setTimeout (this.#loop.bind (this), this.interval); else this.end (); }; //_______ constructor (cbFunc, count, interval, resolve, reject) { this.cbFunc = cbFunc; this.count = this.#cnt = count; this.interval = interval; this.resolve = resolve; this.reject = reject; } start (force = false) { this.stop (); if (force) this.#cnt = this.count; this.#loop (); return this; } restart () { this.#loop (); return this; } stop () {//あくまでも中断 if (this.#timerId) this.#timerId = clearTimeout (this.#timerId) || null; return this; } end (force = false) { this.stop (); if (force && this.reject)//強制中断 this.reject (); else if (this.resolve) this.resolve (); return this; } //_______ static create (cbFunc, count = 1, interval = 1000, async = true) { if ('function' !== typeof cbFunc) throw new Error (); let obj, resolve, reject; let p = async ? new Promise ((a, b)=> {resolve = a; reject = b}) : null; obj = new this (cbFunc, count, interval, resolve, reject); Object.assign (obj, {promise: p}); obj.start (); return obj; } }
応用して、画面をフェードイン・フェードアウトで切り替えられるチェンジャーを作ってみた
css のアニメーションが楽だったような感じ。
LI 要素に data-change_time を設け、以下のようにセットする
構文は、[フェードインまでの時間, 表示し続ける時間, フェードアウトまでの時間, 空白の時間] とする
停止・再開機能を付けようと思ったが中止。
フリーソフトの exeUSB なるものを見つけた。
USBメモリを装着すると、自動で指定のアプリを起動できる
そこで Chrome --kiosk のモードで起動すれば、電子看板のように使える
html
<ul id="SAMPLE"> <li data-change_time="1000,1500,1000,1000"> <li ...> ...
Script
DisplayChanger.create (SAMPLE);
class DisplayChanger { #skip = function (n = 1) { // - !? for (let i = 0, I = n % this.children.length; i < I; i++) this.parent.appendChild (this.current); }; #getStateTimes (e = this.current) { let state = /^(?:\s*([+-]?\d*)(?:,\s*([+-]?\d*)(?:,\s*([+-]?\d*)(?:,\s*([+-]?\d*)?)?)?)?)?(?:,.*)?$/.exec (e.dataset.change_time); if (null == state) console.error (e, e.dataset.change_time); return state.slice (1).map ((a, i, _, val = parseInt (a))=> isNaN (val) ? this.default[i]: val); }; #next = function () { this.#skip (); this.#loop (); }; #loop = async function () { let e = this.current; let [t0, t1, t2, t3] = this.#getStateTimes (e); let i = this.interval; await (AsyncRepeater.create (this.#fadeIn (e, t0, i), -1, i)).promise; await this.#keep (t1); await (AsyncRepeater.create (this.#fadeOut (e, t2, i), -1, i)).promise; await this.#keep (t3); this.#next (); }; #keep = function (a) { return new Promise (c=> setInterval (c, a)); } #fadeIn = function (e, time, interval) { let max = Math.round (time / interval); let step = 1 / max; let opacity = 0; return function () { opacity += step; e.style.opacity = (1 < opacity) ? 1: opacity; return opacity < 1; }; } #fadeOut = function (e, time, interval) { let max = Math.round (time / interval); let step = 1 / max; let opacity = 1; return function () { opacity -= step; e.style.opacity = (opacity < 0) ? 0: opacity; return 0 < opacity; }; } //_____ constructor (a, b, c, d) { this.parent = a; this.begin = b; this.interval = c; this.default = d;//[fadeInTime, displayTime, fadeOutTime, blankTime] this.#skip (b); this.isInOpration = false; //作動中か? } start () { this.isInOpration = true; this.#loop (); } //_____ get current () { return this.parent.firstElementChild; } get children () { return [...this.parent.children]; } //_____ static create (parent, begin = 0, interval = 20, fadeInTime = 1000, displayTime = 5000, fadeOutTime = 500, blankTime = 300) { if (1 > arguments.length) throw new Error ('引数がありません'); [...parent.children].forEach (e=> e.style.opacity = 0); let tm = parent.dataset; interval = Math.max (1, parseFloat (tm.interval || interval)); fadeInTime = Math.max (1, parseFloat (tm.fadeInTime || fadeInTime)); displayTime= Math.max (1, parseFloat (tm.displayTime || displayTime)); fadeOutTime= Math.max (1, parseFloat (tm.fadeOutTime || fadeOutTime)); blankTime = Math.max (1, parseFloat (tm.blankTime || blankTime)); let obj = new this (parent, begin, interval, [fadeInTime, displayTime, fadeOutTime, blankTime]); obj.start (); return obj; } }