Hammer.jsを使ってダブルタップを実装してみた


仕事でiPad用のウェブサービス作っているのですが、仕様書を見ると『ダブルクリックで拡大』なんて項目がありました(注:ダブルクリックとはダブルタップのことです)。調べてみると、JavaScriptでダブルクリック用のイベントを実装してもiPadではダブルクリック≠ダブルタップのようで、ダブルタップを実装したい場合は独自に実装しなきゃいけないということが発覚(参考:iPhone/iPadでダブルタップをJavaScriptで実装する – to-R)。

そこで、何かいいライブラリがないだろうかと探してみたらHammer.jsというタッチ対応ライブラリを見つけたのでさっそく使ってみました(参考:スマホのマルチタッチジェスチャーを判断できるjQueryプラグイン「Hammer.js」: 小粋空間)。
Hammer.js – A javascript library for multi-touch gestures
jQueryプラグインがどこにあるか分からず少し探したので、jQueryで使う場合のjquery.hammer.jsのインストールページも下記にリンクしておきます。
EightMedia/jquery.hammer.js · GitHub

まず、headタグ内にjQuery、Hammer.js、Hammer.jsのjQueryライブラリという順番に読み込む。

参考に、JavaScript内にはliタグをダブルタップすると、そのliの要素のHTML(innerHTML)をbodyの最後に挿入するようという動作を実現するコードは以下。

ただし、これだとダブルタップした時にその部分を拡大表示してしまうという動きになってしまうという問題があります。この解決方法として、metaタグのviewportのcontent属性に”user-scalable=no”とすれば拡大しなくなります。が、これだとピンチインやピンチアウトでも拡大縮小しなくなります。やりたいことはあくまで、対象の要素にダブルタップした時に拡大しないようにしたいだけなので、下記のように書きなおすことに。

タッチがはずれた時の標準イベントをe.preventDefault()で止めています。これで、拡大しないようになりました。

以下、実行サンプル
hammer.js テスト

数値をダブルタップすると、ページ下にダブルタップした位置の数値とダブルタップした位置のドキュメント内相対座標とウィンドウ内の座標を書くようにしています。また、親要素のulタグをクリックした場合には”ul”と書くようにもしています。標準イベント(拡大)については、奇数の数値のみ($(‘li:odd’)のみ)ストップするようにしています。つまり、0の箇所をダブルタップすると拡大しますが、1のところをダブルタップしても拡大しません。

iPod touchのSafariで試してみましたが、奇数の文字をダブルタップしても拡大されませんでした。偶数の文字をダブルタップすると拡大するのですが、なぜか”ul”が表示されません(PC版Chromeでは表示されました)。左側のulのpadding部分をタップしてみると”ul”と表示されたのでulタグをタップすると”ul”と表示されるのですが・・・。

まあとにかく、Hammer.jsは便利そうです。ホールドも実装できるので、いろいろと面白いUIも実現できそうですね。ただ、指定できるオプションについてはほとんど分かっていない状態なので、活用していくにはまだ勉強が必要そうです。

JavaScriptのイテレーションメソッドまとめ(forEach,map,reduce…)


また、HTML5 テクニックバイブルで知ったこと。

ECMAScript5ではいろいろな配列操作メソッド(イテレーションメソッド)が追加されたらしく、使ってみると便利そうなので勉強用にいろいろ触ってみたのでそのメモ。

以下、HTML5 テクニックバイブルに書いてあったイテレーションメソッドの説明の引用とMDLのサイトへのリンク(ただし英語。英語が読めない自分が紹介するのもなんですが・・・)。

forEach
各要素に任意の処理を逐次実行する
参考:Array.prototype.forEach() – JavaScript | MDN
map
各要素に任意の処理を実行したり返り値で構成される配列を返す
参考:Array.prototype.map() – JavaScript | MDN
filter
条件を満たす要素のみで構成される配列を返す
参考:Array.prototype.filter() – JavaScript | MDN
some
条件を満たす要素が1つでもあるか確認する
参考:Array.prototype.some() – JavaScript | MDN
every
すべての要素が条件を満たすか確認する
参考:Array.prototype.every() – JavaScript | MDN
reduce
左から右へ処理した結果を順に受け渡して1つの値に折り畳む
参考:Array.prototype.reduce() – JavaScript | MDN
reduceRight
右から左へ処理した結果を順に受け渡して1つの値に折り畳む
参考:Array.prototype.reduceRight() – JavaScript | MDN

というわけで、上記を参考に下記のようなコードを書いてみた。

以下、実行サンプル。
JavaScriptのイテレーションメソッド

上記ページを開いたら開発者ツールを開いてみてください。コンソールタブに結果が出力されるはずです。

JavaScriptで縮小画像の作成


スマートフォンなどで撮影した写真をサーバーに転送したい場合、撮影された写真そのままではサイズが大きすぎてアップロードに時間がかかり、縮小してから送りたいといったことをしたいと思っていたら、またまたHTML5 テクニックバイブルに書いてありました。HTML5 テクニックバイブルすごい。

というわけで、上記の書籍を参考に下記のようなコードを書いてみた。

HTML

JavaScript

以下、実行サンプル。
画像を縮小して表示

やっていることは単純で、画像ファイルを選択すると、縮小用の画像サイズと同じcanvasを作成し、canvasオブジェクトのdrawImageメソッドで、画像を埋め込み、toDataURLメソッドでData URIを取得するというもの。こんな簡単にできるとは・・・。

今回は取得したData URIをimgタグで表示するようにしたサンプルを作成しましたが、これをそのままサーバーに送信したり、先日canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法 | while(isプログラマ)で紹介したやり方でData URIをバイナリ形式にしてから送信するとサーバーに縮小画像を送信することができそうです。

2014年10月21日追記:
JavaScriptで縮小画像を作成してサーバーにPOST送信する方法 | while(isプログラマ)にて試してみました。

Array.prototype.forEach.callで要素リストのイテレーション


またまた、HTML5 テクニックバイブルで知ったことのメモ。

今まで、document.querySelectorAllや開発者ツールのコンソールの$$($$はdocument.querySelectorAllと同じ動き)で取得した要素リストを巡回するのに、面倒だと思いながらも下記のように書いていました。

上記はページ内にあるaタグのhref属性内(つまり、リンク先)を一覧としてコンソールに表示するコードです。いちいち要素の長さを取得してループする回数を指定しなきゃいけなくて正直面倒だと思ってました。jQueryみたいにリストの数を意識せずにループすることができないもんかとずっと思っていたのですが、先日、下記のように書くことで上記と同じ動きをすることが発覚。

Array.prototype.forEach.callという長い構文を覚えなければいけませんが、これなら要素の数を意識せずにループすることができるようです。

ちなみに、forEachをmapにし、console.logをreturnとすると、href属性が入った配列が返される。

canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法


最近、自宅で時間ができるとHTML5 テクニックバイブルという本を読んでいるのですが、初めて知ったことがいろいろ書いてあって驚愕する日々を送っています。

今回は上記書籍を読んで知ったCanvasで描いた絵を、Base64のData URI形式ではなく、バイナリ形式にしてPOST送信する方法のメモ。ただし、IE9では使えないよう。

まずは、受け取り側のPHPを記述。普通にフォームの画像ファイルを受け取ってサーバーにファイルを保存するような記述を書いておきます。ファイルのアップロード | PHP Laboを参考にしました。

上記のPHPでは、acceptImageというプロパティに対応するファイルをtest.pngという名前で保存するようにしたPHPです。試しに、下記のようなHTMLを書いて普通にファイル選択してPOSTするとうまく動きました。

というわけでつづいて、前に作ったCanvasお絵描きツールの投稿先を、上記PHPに変更し、JavaScriptでCanvasのデータをPOST渡しできるよう記述するように投稿ボタンを押した時の処理を下記のようにしてみました。

実行サンプル:canvasを使ったお絵描き投稿システム

HTML5 テクニックバイブルのほうでは、jQueryを使わずに純粋にXMLHttpRequestオブジェクトを使ってAjax送信しているコードが載っていたのですが、自分は最近Ajaxを使う時はjQueryを使うことにしてるので、jQueryを使って書いてみました。実はjQueryを使ったAajxでは最初うまく動かなかったのですが、調べてみるとjQueryでFormDataオブジェクトをAjaxで送信する場合には、contentTypeとprocessDataをfalseにしなければいけないと分かり、そのオプションを変更することによってうまく動きました(参考:jQuery: FormData オブジェクトで送信する – Sarabande.jp)。
HTML5テクニックバイブルに載っているコードでは、そんな記述が必要ない分、もう少しシンプルな記述となっています。

それにしても、上記コードは自分が初めて知ることがたくさんあります。FormDataオブジェクトぐらいなら聞いたことはあったのですが、BlobオブジェクトとかArrayBufferオブジェクトとかUint8Arrayオブジェクトとか、何だそれ!? という思いです。簡単に説明すると、BlobオブジェクトはMIMEタイプを含んだバイナリデータを表し、ArrayBufferオブジェクトは固定長のバイナリデータを表し、Uint8Arrayは8ビットの符号なし整数を表すようです。なお、Uint8Arrayの説明には、C言語での同等タイプに『uint8_t』という記述が。そんなデータ型がC言語にあったか? と思って調べてみると、どうやら『unsinged char』と同じらしいです(参考:整数型 – Wikipedia)。

HTML5はある程度知っているつもりでいましたが、まだまだ知らないことが多くありそうです。

ところで、Blobオブジェクトの取得については、canvasオブジェクトにtoBlobメソッドが実装されていれば、そのメソッドを使うことにより、簡単に取得できるようです。ただ、このメソッドは残念ながら、Firefoxでしか実装されていないよう。html5.jpによると、2011年4月5日の草案にはすでに提案されていたようなんですが・・・(参考:W3C – 『HTML 5 differences from HTML 4』日本語訳 – HTML5.JP)。Chromeですら実装してないとなると、さすがに使うのに躊躇します。