faviconの動的書き換え

 
Web Page
Booskanium's Tips.

Twitterのfaviconはつぶやきがあった場合など赤い点で知らせてくれます。
その方法と、さらにfaviconをパラパラ漫画的にアニメーションさせる事も可能にしたモジュールを試作しました。

faviconを動的に入れ替る

favicon置換するhtmlコードは下記のとおり

<html lang="ja">
<head>
<meta charset="utf-8">
<title>favicon変更</title>
<link rel="shortcut icon" href="favicon.ico" id="favicon">
<script>
function chngFavi(){
  var favi = document.getElementById("favicon");
  favi.href = "favicon2.ico";
}
</script>
</head>
<body>
  <input type="button" value="favicon切り換え" onclick="chngFavi();">
</body>
</html>
							

favicon入れ替えモジュール

faviconの入れ替えと、ファビコンでパラパラ漫画が出来るモジュールを作ってみました。
作成したモジュールのコードは下記のとおりです。

'use strict';
/*
*	Animate the favicon
*	Animation favicon can be changed
*	Confirmed to work with Firefox and Chrome
*
*	Lisence: MIT
*	Author: booskanium
*
*/
export default class Petit_favicon {
	constructor(cnf) {
		this.default = {		//Various settings for turning the favicon
			faviId: "favicon",		//ID name of the element specified by the favicon.
			faviHref: "",			//Default value of href attribute of element specified by favicon.
			favis: [				//List the png image files to be animated. The base64 format is recommended.
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUXFxf9/f17uF6KAAAAPUlEQVR4nGP4DwQM+Il/DAz/GT4zMN5n+HCAnZ/hQ/0/nIQ8w9////dDtH34D+TiU8zP8LGBsR9kfD1OywEU9GGHRxCX/AAAAABJRU5ErkJggg=="},	
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUJCQn////yzuPuAAAAJUlEQVR4nGP4DwQMBAl7IPFHHoX4J49D7H89TNs/LOpwa8NqOQAy6m86rGA4sAAAAABJRU5ErkJggg=="},	
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUhISH9/f1SulBhAAAAOUlEQVR4nGP4DwQMBAgGhnqGPwyM54EsZn6g2D98RAPjfpCO/wwfDjADifr/eIiPDUCT/4GMx2U5ALW9ZVjTXZbRAAAAAElFTkSuQmCC"},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUjIyP9/f3Pu2qBAAAAOUlEQVR4nGP4DwQM+Il/DAz/Gf4wMM4Hstj4gWL/cBLyQJWM+yE6/jcwyeNVzA9UBzT0DwNDPU7LAQBPYYZTwADdAAAAAElFTkSuQmCC"},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUXFxf///+kBIlKAAAAM0lEQVR4nGP4DwQM+Imf///bMzz4/0eeAPEBRPxmYKxn+MfAANTbwCQPNOAPYeKfPE7LAUDJaMn96+KXAAAAAElFTkSuQmCC"},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEULCwv7+/sLx3DPAAAAOUlEQVR4nGP4DwQMBAgGhnqGHwcY7Bl+1AO5P+xxEH9Asv8bgIqBxH2QXnZ8xN92kMn5IMX2OC0HAPYuZ18AwFUvAAAAAElFTkSuQmCC"},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUKCgr7+/tFR+2/AAAAPklEQVR4nGP4DwQMBAgGhnqGnw1A4kc9kPsBTmDjAhX/Z/jTwLAfJMYPJP7xo7D+IYv9ZGA4z/CPgcEep+UAfe9j/4lhW7gAAAAASUVORK5CYII="},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUICAj+/v7TkBV+AAAAMElEQVR4nGP4DwQM+Il/DAz/GT42MPYzfKj/x4+b+PH/vzxc234gARQjivhvj9NyAKTLaRhZinoaAAAAAElFTkSuQmCC"},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUKCgr6+voqmYZfAAAARUlEQVR4nGP4DwQM+Il/DAz/GT43MM9n+PD/nzzDh/p//HAChfv/vzxQJRD9ZGDcD5fFoeP/f36GzwyM9xn+MDDU47QcALkVX5KjF4UGAAAAAElFTkSuQmCC"},
				{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA3NCSVQICAjb4U/gAAAABlBMVEUICAj7+/vYRtdfAAAAPUlEQVR4nGP4DwQMBAgGhnqGjw2M/Qwf6v/zA4l/2Ikf///LgxQDdTQw7AfqBYrhJkBG/WNgOA/SYY/TcgDvoGFjT+QL7gAAAABJRU5ErkJggg=="}
			],
			fps: 2				//Note that if you make the FPS too large, frames will be dropped.
		}
		this.config = JSON.parse(JSON.stringify(this.default));		//Dafaultをconfigへdeep copy
		this.intervId = null;		//Favicon turning interval ID 
		this.ix = 0;				//Favicon turning index 
		if (cnf) {			//Set your own settings
			this.change(cnf);
		}
		if (!!document.getElementById(this.config.faviId)) {
			this.default.faviHref = document.getElementById(this.config.faviId).href;
			this.config.faviHref = document.getElementById(this.config.faviId).href;
		}
	}
	change(cnf) {	//Change the animation favicon
		if (this.intervalId) {
			window.clearInterval(this.intervalId);
		}	
		if (cnf) {
			if ('faviID' in cnf) this.config.faviId = cnf.favId;
			if ('faviHref' in cnf) this.config.faviHref = cnf.favHref;
			if ('favis' in cnf) this.config.favis = cnf.favis;
			if ('fps' in cnf) this.config.fps = cnf.fps;
		} else {
			this.config = JSON.parse(JSON.stringify(this.default));		//Deep copy Default to config
		}
	}

	start() {	//Start Favicon animation 
		if (this.intervalId) {
			window.clearInterval(this.intervalId);
		}
		let fpsRate = 1000 / this.config.fps;
		this.ix = 0;
		document.getElementById(this.config.faviId).href = this.config.favis[this.ix].href;
		if (this.config.favis.length > 1) {
			this.intervalId = setInterval(()=>{
				if (this.ix >= this.config.favis.length - 1) {
					this.ix = 0;
				} else {
					this.ix++;
				}
				document.getElementById(this.config.faviId).href = this.config.favis[this.ix].href;
			}, fpsRate);
		}
	}

	stop() {	//Stop the Favicon animation and replace it with the default Favicon
		if (this.intervalId) {
			window.clearInterval(this.intervalId);
		}
		document.getElementById(this.config.faviId).href = this.config.faviHref;
	}
}

簡単に説明すると
・モジュールなのでimportする
・newはDOMが有効になった(DOMContentLoaded)後に
・configの設定はnewする時、またはchangeメソッドで
・configでのfavicon指定はコード中のコメントを参照
・faviconイメージはbase64でconfigに配列で設定する
 ※配列が1つだと単純なファビコン入れ替え
 ※faviconのURL指定も可だがDDoSもどきなので要注意
 ※faviconイメージはpng推奨
 ※faviconでsvg利用はSfariが開国したてで時期尚早
・ぱらぱらfpsが速すぎるとコマ落ちするので程々に
・メソッドは
 change configの入れ替え
 start faviconのアニメーション開始
 stop faviconを既定値に戻す

動くファビコンを利用する場合の注意事項です。
・base64で抱え込まないと自爆DDoSかも
・スマホブラウザでは無意味(無駄に裏で機能している)
・faviconを派手に動かしすぎると多分嫌われる
ということで、ほどほどに!!

上記のモジュールを当ページに組み込んだ「index.js」

import Petit_favicon from "../js/modules/__petit_fav.js";
const favi = new Petit_favicon();

document.getElementById("stop").addEventListener("click", () => {
  favi.stop();
});
document.getElementById("start").addEventListener("click", () => {
  favi.change({favis: [
        {href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAySURBVEhL7c0hAgAgDIBA9P9/nsVOWuMKkQPDpvu7poFqoBqoBqqBaqAaqAaqgWog4AGhxgE/VKJr9AAAAABJRU5ErkJggg=="}, 
        {href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAxSURBVEhL7c2hAQAACITA1/131mIn2bhCpGbyqq9vHCAHyAFygBwgB8gBcoAcIAcgWbZaAj6Rffc+AAAAAElFTkSuQmCC"}, 
        {href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABKSURBVEhLtcexDQAwEAMh77/0p7+aSDTs9lfP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VyPbQ8yw/wuEOdxyQAAAABJRU5ErkJggg=="}  
      ],fps:1});
  favi.start();
});
document.getElementById("bule").addEventListener("click", () => {
  favi.change({favis:[{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAySURBVEhL7c0hAgAgDIBA9P9/nsVOWuMKkQPDpvu7poFqoBqoBqqBaqAaqAaqgWog4AGhxgE/VKJr9AAAAABJRU5ErkJggg=="}]});
  favi.start();
});
document.getElementById("yellow").addEventListener("click", () => {
  favi.change({favis:[{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAxSURBVEhL7c2hAQAACITA1/131mIn2bhCpGbyqq9vHCAHyAFygBwgB8gBcoAcIAcgWbZaAj6Rffc+AAAAAElFTkSuQmCC"}]});
  favi.start();
});
document.getElementById("red").addEventListener("click", () => {
  favi.change({favis:[{href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABKSURBVEhLtcexDQAwEAMh77/0p7+aSDTs9lfP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VzP9VyPbQ8yw/wuEOdxyQAAAABJRU5ErkJggg=="}]});
  favi.start();
});
document.getElementById("demo").addEventListener("click", () => {
  favi.change();
  favi.start();
});