読者です 読者をやめる 読者になる 読者になる

HTML5機能を色々使ったスマホ向けWebアプリを作ってみた話:後編

前回のエントリでもお知らせしたように、ドコモゼミ Webアプリラボのコンテストにアプリを出しました!面白かったら下記ページの いいねをお願いします!

かきまる

スマートフォンを使いながらも、アナログな感触をもっと楽しんで欲しい。そういう想いで考えられた、デジタルとアナログを繋ぐお絵かきWebアプリです。

.

で、今回のエントリでは 前回のHTML5機能を色々使ったスマホ向けWebアプリを作ってみた話:前編 で書き切れなかった残りを解説します。

今回は次のトピックス

  • かわいい文字で表示したい。
  • 音があると楽しい。
  • スマホとタブレット両方対応。AndroidでもiPhoneでも。

Webフォントでオリジナルフォントを使う

Androidがいけてないの一つが、フォントにあると思っています。標準フォントや入っているフォントやが機種によりバラバラ。しかも標準フォントがダサイのが多い。あまりに違うためデザイン的なページの場合、テキストで統一的に表現するのは難しく、画像にしないといけなくなります。

画像にすると色々問題があります。まず変更がめんどくさい。文言ちょっと変更する度に画像を書き出さないといけない。さらに、昨今の高精細ディスプレイ用に2種類の解像度の画像が必要。そしてモバイルの場合特に重要なのが、ページサイズが増えること。画像が多くなると必然的にページサイズが増え、3G回線などでのモバイルではロード時間が遅くなってしまいます。

じゃあ、どうすればいいのか。そういう時に、Webフォント が使えます。Webフォントとは CSS3 から導入された仕様で、これまではOSにインストール済みのフォントしか使えなかったのが、必要に応じてWeb上からフォントデータをダウンロードして表示できます。これを使用すると、機種毎にバラバラなAndroidでも、iOSでも、PCブラウザでも同じフォントで表現できるようになります。

ただ、日本語フォントのように膨大な数があるとフォントデータが増え、画像データよりむしろ重くなることがあるので注意が必要です。必要な文字だけのフォントデータのする、などの工夫が必要です。今回のかきまるでは、ひらがなのみなのでサイズは woff 形式で11kに収まりました。この程度なら、画像を複数枚用意するよりも少ないサイズでいけます。また、フォントデータをキャッシュさせれば、別の文字が増えたとしても新たなダウンロード時間は増えません。

前回のひらがなの書き順を表現するため、文字データをSVGで作っていたので、それを元にWebフォントにしました。Webフォントに使える形式は幾つあります。最近だと WOFF が標準ですが、古い形式のために ttf や svg形式のものも用意し、CSS記述でフォールバックするようにしておくとベターです。

SVGからWebフォントの作り方はこちらを参考にしました → アウトラインのSVGからフォントを生成 #かな書いてみるFontForge の出力形式に woff、svgなどを追加すると、自動でその形式で出力してくれます。

ちょっとはまったのが、フォントデータにするときはアウトラインデータの方が良い、ということです。まぁ、当然なのですが。。。アウトラインから変換することを前提としているので、stroke の lincap や linejoin などは無視されます。今回の文字データは、書き順アニメーションのために1本のパス(stroke)で書かれていたのですが、そのまま変換するとうまくいきませんでした。そこで、フォント用に別途アウトラインデータも作成しました。

音を再生する

photo by vancouverfilmschool (CC BY 2.0)

Webで音を再生するには二つの方法があります。簡単なのが HTML Audio要素を使うこと。スクリプトからも操作でき、簡単な音声の制御ができます。もう一つが Web Audio API を使用すること。こちらはもっと複雑なことが可能で、合成やフィルタリングなどを使用してWeb上でシンセサイザーのようなことも実現できます。

ただし、Web Audio API はモバイル端末ではまだまだサポートが厳しい現状です。iOS 6から一部制限付きで使えるようになりましたが、Androidでは全然だめ。ということで、スマホ向けWebアプリは 今のところ HTML Audio要素一択という状況です。

HTML Audio要素ならスマホでも盤石か、と言うとそういうわけでもありません。スマホの限られたリソース(CPUや帯域)では、音声データのダウンロードやデコードで少し時間がかかる場合があります。そこで予めデータをプリロードしておきたいところですが、これも色々と制限されています。詳しくは 過去エントリ iOS/Android で HTML5 の audio/video を任意のタイミングで再生する方法 でも書いたのですが、audio を play する前に、load しておくことで対応できます。

「かきまる」では、当初 最初のページにタッチしたタイミングで全音声データをロードしていたのですが、それだと重すぎて最初のページめくりが固まってしまいました。そこで、各ひらがの書き順ページへ遷移するタッチイベントをトリガーに、次の動物の名前の音声データをロードするようにしました。書き順を表示している裏でダウンロードされるため、次の動物にいったときには、すぐに再生されます。

この方法の欠点として、書き順を表示しきる前に次々にページをめくると音声再生が追いつかない、という問題がありますが、これはもう致し方なしとして諦めました。また、Android Chrome の ver 25 くらいまではこの方法でうまくいっていたのですが、最新版の 27にすると事前 load が効かず、play時にのみロードされるようになってしまいました。

他にも、Galaxy Nexus(Android 4.2.2)の標準ブラウザと Chrome を組み合わせると、ブラウザ終了時やスリープ時に最後に再生した音声が勝手に再生される、というとんでもないバグがあったりします。しかも通知でも再生されるため、メール着信などある度に ”おおありくい〜” とか勝手着信音となってしまったり(ーー;; 他の端末では起きなかったため、対処は見送り。。

スマホ/タブレット/Retina対応。Androidでも。

一通り動くようになってから、最後に スマホ(縦横)と、タブレット(縦横)でもちゃんと見えるよう、CSS メディアクエリを使って調整しました。Android でもきちんと表示されるよう対応させるのに苦労しました。

SVGを画像として使う。

画像をスマホ/タブレット用に最適化すると、それぞれのRetina用も考えると、スマホ or タブ x 2解像度 = 4パターン必要になります。さらにそれの縦横まで対応すると、1つの画像に8パターンも必要になってしまいます。(実際には縦横は同じ画像を縮小で表示することが多いでしょうが)

今回は画像に SVGを使ったため、1パターンさえあればよいので非常に楽でした。Android 2.x は SVGに対応していないのですが、ここはもうばっさり切りました。Android 4以上なら問題なし!と言いたいところですが、まだ落とし穴があります。

SVGをWebページに埋め込みで使う場合、幾つか方法があります。

  • OBJECTタグで使う
  • IMGタグで使う
  • インラインSVGにする

古いブラウザなどの場合、IMGに使えなかったりするので、OBJECTタグを使用する必要がありますが、スマホの標準ブラウザ(Chrome)ならIMGで問題ありません。OBJECTタグにする問題点は、外部ドキュメントになるため、UIイベントがそちらに取られてしまうことがあります。また、インラインSVGにすると元ページにSVG要素を埋め込まないといけないため、画像の差し替えが煩雑です。なので、単に画像として使う場合は IMG要素として使うのがオススメです。(この節のSVGと書いてある画像も実はIMGタグでsvgを指定しています)

IMGタグでSVGを使う場合、画像サイズの指定はIMGタグで行います。width/heightの属性か、CSSの属性で指定できます。通常、widthとheightを指定すると、元画像のアスペクト比を保持したままスケールします。しかし、一部のAndroid標準ブラウザでは、アスペクト比が保持されず引きのばされてしまいます。iOS と Android標準ブラウザの表示違いは下の図のようになります。

Android標準ブラウザ

そこで svg ファイルの viewBox を見直しました。svgタグには、どの範囲を表示するか指定する viewBox という属性があります。このviewBox のアスペクト比と実際に表示させる画像のアスペクト比を一致させることで、この問題に対応することができます。

アイコン

一部のアイコンにも、SVGフォントを使いました。IcoMoon などを使えば簡単に導入できて便利です。ただし、Androidでは要注意です。

通常、Webフォントをアイコンに使う場合、特定の文字コードをアイコンに割り当てます。Unicodeでは、外字エリアとして私用領域という名称で U+E000〜U+F8FF、U+000F0000〜U+000FFFFDなどが割り当てられているので、これを使うことが多い。前述の IcoMoon はこの外字エリアを使っていて、U+E001 から埋めていきます。

ところが、一部Andoroid(Softbank端末など)は、この外字エリアに独自のアイコンを定義してたりします。→ 携帯電話の絵文字

U+E001 は ソフトバンクの絵文字ともろに被っています。今回は時間が無かった & ドコモ向けなので、残念ながら対応は見送りましたが、IcoMoon などを使う場合、コードには十分気をつけてください。

メディアクエリ

SVG画像のスケールは上記の対応で可能になりました。次は、メディアクエリを使い、幅に応じて画像や文字の大きさを変更します。メディアクエリで端末サイズを判定するには主に二つの方法があります。

  1. min-width, max-width で判定する。
  2. min-device-width, max-device-width で判定する。

1つ目の方法は、windowの幅で判定するのに対して、2つめは、端末の幅で判定します。PCブラウザから見るとどちらも同じように動きますが、スマートデバイスでみると大きく異なります。特に回転した場合に違いが出ます。デバイスの向きを横に回転したとき、1の方法で横幅の変更を検知し、レイアウトを変更できます。しかし、2つめの方法では端末自体の幅は固定のため、回転しても変更されません。そのため、向きを判定するためには orientation:portrait / orientation:landscape を組み合わせて使用します。 なお、この orientation は厳密には向きを見ているのでは無く、縦長か横長なのかを見ているだけです。なので、PCブラウザで縦長/横長を変更しても反映されます。

どっちの判定を使えば良いか、というと前者をオススメします。というのも、これまた一部のAndroid標準ブラウザでは、device-width を正しく認識しません。例えば、Galaxy Nexus, S2, S3 なんかでは、window.innerWidth は 360px なのに、メディアクエリの device-width は 320px として認識されます。細かい調整をする場合、正しく認識されないのは致命的です。また、device-widthを使用する場合は orientation も同時に指定しないと意味が無いため、記述も長くなり、お勧めしません。

まとめ

Webアプリでも表現が幅がだいぶ広がってきました。今後、WebRTC や WebGLとか使えるようになったらもっと凄いものができるようになると思います。まだ Android Chromeでもデフォルトはオフ、Safariは全然うごかない(今日発表の iOS7 で対応がでるかも!?)

現状では「かきまる」のようなシンプルなものでも、意外と苦労しています。スマホ向けWebアプリで、なめらかなUIや、複数端末への対応は骨が折れます。特にAndroidは本当にツライのでGoogleさんマジでなんとかしてください(切実