どうでもいいプログラム研究所

とある編集者によるIT、Web、ソフトウェア、プログラミングに関する雑記と覚え書き

Twitterで誕生日に飛ぶ風船を再現するGoogle Chromeの拡張機能を作ってみた

f:id:tdyu5021:20210127000848p:plain

Twitterでは、プロフィールに誕生日を設定していると、その人の誕生日にホーム画面に風船が飛びます。あの仕掛けが個人的に好きなので、これをどのWebページでも再現するGoogle Chrome拡張機能を作ってみました。備忘録も兼ねてまとめてみます。

「毎日が誕生日」なGoogle Chrome拡張はこちら

このChrome拡張機能を使うとどんな感じになるのか。まずは、私が投稿した以下のツイート内の動画をご覧ください。

見ての通り、Webページを表示する度にTwitterの風船みたいなやつが飛んでくるというプログラムです。最初は楽しいけど、1分で飽きますね、これ…

まあ誕生日気分を味わいたい方にはぴったりなプログラムです。ちなみに本物のTwitterだと風船をタップすると割れるという仕掛けまでついているようですが、面倒なのでそこまではやりませんでした。

実際のJavaScriptのコード

というわけで最初にソースコードを載せます。Google Chrome拡張機能はHTML、CSSJavaScriptで作ることができ、しかもChromeストアに公開せず、自分のブラウザだけに適用するくらいならわずかな設定でできます。作り方は他のサイトみればすぐにわかりますので省略します(気が向いたらこのブログに書きます)

今回の拡張機能は既存の画面をいじるだけでなので使用するのはJavaScriptファイル(と風船の画像ファイル)だけです。その実際のコードが以下です。※jQueryで記述しているのでjQueryファイルを別途読み込んでいる前提です。

$(function(){
	//風船5種類
	img1 =chrome.extension.getURL('b1.png');
	img2 =chrome.extension.getURL('b2.png');
	img3 =chrome.extension.getURL('b3.png');
	img4 =chrome.extension.getURL('b4.png');
	img5 =chrome.extension.getURL('b5.png');
	imgArr = [img1,img2,img3,img4,img5];
	$('body').append('<div id="added"></div>');

	//画面の高さと幅を取得
	wh = $(window).height();
	ww = $(window).width();

	//風船オブジェクトの配列
	bl=[];
	for(var i = 0; i<30; i++){
		var startX=Math.floor(Math.random()*(ww/20))*20;
		var startY = wh+(Math.floor(Math.random()*10)*80);
		var ratio =1 / (Math.floor(Math.random()*5)+4);//Yが進む距離に対してXが進む距離の比率
		direction = 1-Math.round(Math.random())*2;
		bl[i]= new balloon(i,startX,startY,ratio,direction);
		bl[i].appear();
	}
});

function balloon(i,x,y,r,d){
	this.i=i;
	this.x=x;
	this.y=y;
	var dist1 =-8;
	var dist2 = Math.abs(dist1)*r*d;
	$('#added').append('<img id=image'+this.i+'>');

	//風船を出現させる
	this.appear = function(){
	  var target = $("#image"+this.i);
	  target.css({
	    position:'absolute',
	    top:this.y,
	    left:this.x,
	    zIndex:9999,
	    width:'80px'
	  });
	  var rnd = Math.floor(Math.random()*imgArr.length);
	  target.attr("src", imgArr[rnd]);
	  var xx = this.x;
	  var yy = this.y;

	  //風船を動かす
	  var si = setInterval(function(){
	    yy+=dist1;
	    xx+=dist2;
	   	target.css({
	      top:yy,
	      left:xx
	   	});
	  	if(yy<-200) clearInterval(si);
	  },20);
	};
}

作成のポイント

60行足らずの簡単なコードですが、ポイントをかいつまんで見てみます

①風船の画像を画面上に配置する

通常、HTML、CSS、JavaSciptなどで画像を読み込む場合は、参照先のディレクトリにもよりますが、もし相対パスなら

img1 ='img/b1.png'

上記のように書きますが、Chrome拡張機能を作る際には参照の仕方が異なります。

Chrome拡張機能を有効化するには必要なファイルをフォルダにまとめてアップロードするのですが、JavaScriptファイルと使用する画像ファイルが同じ階層にある場合、

img1 =chrome.extension.getURL('b1.png');

こんな感じで画像を参照させます。

次に、風船の画像ファイルを画面に配置するには、

(1)新しく<div>要素を作り、
(2)その中に風船の数だけ<img>要素を作成する

という感じで既存のHTMLの中に組み込む必要があります。

(1)の部分は上記のコードの中でこの部分です。appendメソッドでbodyタグ内の最後にまず<div>要素を付け足します。

$('body').append('<div id="added"></div>');

(2)に関しては、風船を格納する<img>要素を作ると同時に、画像を画面上で好きな位置に動かさなければならないので、 cssでpositionをabsoluteに設定します。最前面にくるようにzIndexプロパティも適当に大きな値にしておきました。

以下の部分で、付け足した<div>要素の中にappendメソッドで<img>要素を設定し、

$('#added').append('<img id=image'+this.i+'>');

以下の部分でスタイルを設定しています。

var target = $("#image"+this.i);
target.css({
   position:'absolute',
   top:this.y,
   left:this.x,
   zIndex:9999,
   width:'80px'
});

 風船オブジェクトとそのインスタンスをたくさん作る

今回は風船を大量に出現させ、またそれぞれが別の角度で別の場所で動いていくので、まず風船オブジェクトを定義し、そのインスタンスを生成して各風船(インスタンス)の位置やら進む角度や方向などを与えていくやり方がよさそうです。

この考え方を今回のコードで実行する際、骨子だけ作るとまず以下のような感じになるかと思います。 

for(var i = 0; i<30; i++){
  bl[i]= new balloon();
  bl[i].appear();
}

function balloon(){
	this.appear = function(){
		var si = setInterval(function(){
			$("#image"+[変数]).css({
				top:xxxxx,//天地の位置を動かす
				left:xxxx//左右の位置を動かす
			});
		},20);
	};
}

まずballonnという風船自体の関数オブジェクトを作り、その中に各風船を動かすappearというメソッド作ります。今回は適当に30個の風船を作ることにしたので、ループの上限を30にしてforループで回し、

bl[i]= new balloon();
bl[i].appear();

で各風船インスタンスを作成して動かします。

インスタンスを生成したときに風船オブジェクトに渡す情報(=new balloon();の引数)は以下です。

  1. 風船の通し番号
  2. 風船の左右の初期位置
  3. 風船の上下の初期位置
  4. 風船が進む上方向距離と左右方向距離の比率
  5. 風船の進む方向(左か右か)

 このうち4番目については、風船を動かすループの中で縦方向に動く距離に対して横方向に動く距離を比率をランダムに設定することで、風船が飛ぶ角度をバラバラにしており、その情報を渡すものです。もっといいやり方がないかなと思いつつ書いています。

だいたい要点はそんなところです。

この記事を書いた理由

 JavaScriptはたまにしか書かないので、結構関数オブジェクトとインスタンスの生成の書き方を忘れるんですよね。このプログラムも大した時間がかけてませんが、過去の自分のファイルを見ないと書き方わかりませんでした。

というかこの書き方で合ってるのか謎だし。別のプログラムでprototypeとかも使ってるけど、完全に雰囲気で書いている…(そしてその使い方はもう忘れた)