CSSチームが運営するHTML5・CSS3情報サイト

HTML5 GOGO

ボーズ・オンラインストア

Mckee

今回は、前回目標としながら志半ばに断念した機能を実装して、パワーアップして帰ってきました。

概要

今回はそのサンプルを題材に、
進化したVideoCanvasを連携させた描画の紹介と、
Indexed Database APIを使用する箇所で、効率的になった処理を紹介します。

※対応ブラウザ:Chrome 最新
※Firefoxはお絵かきアプリに使用している offsetX, offsetYプロパティが未実装。
※動作にはCPUパワーが少々必要になります。
※サンプルコードは紹介用に編集してありますので、
動作しているコードと異なる部分があります。

サンプルアプリケーションについて

動画を再生しながら、その上に落書きできます。色を変えながら落書きしましょう。
さっき落書きした模様は、あのシーンで描いたアレもコレも動画と一緒に再生されます。
動画を再生しながら加工しているような感じが楽しげです。

HTMLサンプル

前回からの変更点

処理

1.Indexeddb仕様変更

トランザクション開始時にオブジェクトストア名を指定しなくては行けなくなったようです。

var store = $.indexeddb.db.transaction("video", $.indexeddb.IDBTransaction.READ_WRITE).objectStore("video");

2.Indexeddb コールバック形式の処理あたり

IndexeddbではDB接続の確立やトランザクションの開始などIndexeddbを使った処理の度に、「成功」「失敗」かで処理を切り替えます。そのため事前にそれぞれのコールバックに処理を登録しておく必要があります。
Indexeddbを使った関数を呼び出す際に、処理成功時に実行したい関数を渡しておいて、コールバックで実行させるという方法が良さそうでした。

例)データを取得したら、そのデータを使って再生処理を実行。

呼び出し側
$.indexeddb.getDataMulti($.canvas.paly);
関数側
"getDataMulti" : function(action) {
	var store = $.indexeddb.db.transaction("video").objectStore("video");
	var req = store.openCursor();
	var storedData = [];
	
	req.onsuccess = function(evt) {
		var cursor = evt.target.result;
		
		if(cursor) {
			storedData.push(cursor.value);
		} else {
			if(action != 'undefined') {
				action(storedData);
			}
			
			$.adjustHeight();
			return false;
		}
		cursor.continue();
	}
	return false;
},

機能

1.再生中に落書きできる

前回は、Canvasが1枚しかなく、100ミリ秒ごとに動画をCanvasに描画しているので、
落書き部分が、動画のフレームに上書きされてしまい、再生中に絵を描く事ができませんでした。

今回は、動画を描画する Canvas と、落書きする Canvas を重ねることで、
動画は再生しつつ、落書きは落書き用のレイヤーで処理することで、
落書きを保持しつつ、落書きだけを消去することが可能になりました。

<canvas id="videoCanvas" width="313" height="236"></canvas>
<canvas id="layer1" width="313" height="236"></canvas>

2.動画と落書きを保存できる

100ミリ秒ごとに「動画と落書きを合成した画像」を配列に蓄え、
[保存]ボタンが押されると、そのデータをIndexeddbに保存します。
動画と落書きを合成別々に保存する方法も考えたのですが、
動画の再生と落書きの描画を、同期させるのが不安定な感じがしたのでやめました。
[落書き]と[画像合成]と[画像の蓄積]は連なって処理されるので、
再生する時に動画と落書きのズレは発生しません。

$.video = {
	"ms" : 0,
	"startTimer" : function() {
		
		var interval = 100;
		timer = setTimeout(function() {
			$.video.ms += interval;
			
			/**
			 * @param Object<br />
			 * HTMLImageElement, HTMLCanvasElement, HTMLVideoElement
			 * @param Number 描画イメージ矩形のx座標
			 * @param Number 描画イメージ矩形のY座標
			 * @param Number イメージを描画する幅(初期値はイメージ本来の幅)
			 * @param Number イメージを描画する高さ(初期値はイメージ本来の高さ)
			 */
			$.canvas.ctx.drawImage(video, 0, 0, 313, 236);
			$.canvas.ctx.globalCompositeOperation = 'source-over';
			$.canvas.ctx.drawImage($.layer1.cnvs, 0, 0, 313, 236);
			
			var data = $.canvas.cnvs.toDataURL();
			
			var indexData = {
				"ms" : $.video.ms,
				"video" : data
			};
			
			dataStrage.push(indexData);
			
			$.video.startTimer();
		}, interval);
	}
};

3.落書きした模様を再生できる

Indexeddbに保存した画像を全て取得し、
パラパラマンガの要領で、100ミリ秒毎に Canvas に描画していきます。

$.canvas = {
	"cnvs" : '',
	"ctx" : '',
	"data" : {},
	"pause" : true,
	"paly" : function(dataArr) {
		var store = dataArr;
		var interval = 100;
		var screen = $('#screen')[0];
		screenCtx = screen.getContext('2d');
		$('#product  .loading').hide();
		timer1 = setTimeout(function() {
			if(store.length != 0) {
				var data = store.shift();
				var img = $('<img>');
				img.attr('src', data.video);
				$(img).load(function() {
					screenCtx.drawImage(img[0], 0, 0, 313, 236);
				});
				setTimeout(arguments.callee, interval);
			} else {
				$.canvas.pause = true;
				clearTimeout(timer1);
			}
		}, interval);
		
	}
};

4.細かい変更点

  • カラーパレットの見た目
    現在選択中の色が少し大きく表示されます。

img要素のsrc属性は、JSの処理とは非同期に読み込まれる

保存した動画と落書きを再生する時に、再生される時とされない時がありました。
何が問題かと色々調べていると、Canvas に画像を描画する際の処理に問題がありました。
画像をCanvasの drawImage()を使って描画させているのですが、
その画像が読み込まれる前にdrawImage()が実行されているというのが原因でした。
そこで画像が読み込まれてから Canvas に描画するよう処理を修正しました。
JSで処理する対象要素が準備できていなくて、エラーになることはよくありますが、
今回は、エラーが何も表示されずに処理が停止するので困りました。

Indexeddb から取得した画像を、Canvas に描画する処理

var data = store.shift();
var img = $('<img>');
img.attr('src', data.video);
$(img).load(function() {	//←画像が読み込まれてから Canvas に描画する
	screenCtx.drawImage(img[0], 0, 0, 313, 236);
});

動画と落書きの再生を同期させるためにしたことと、その弊害

2つの再生を同期させるために「動画と落書きを合成した画像」を保存しています。
そのために、保存したものを再生するときには、動画と落書きを分離させることができません。
別々のレイヤーに動画と落書きをそれぞれ描画させるというのを生かして、
保存後も別々に表示や再編集を行えるように、別々に保存できたらもっと良かったと思います。

Subscribe:
pagetop