iPhone, Android(desire) ブラウザのhtml5対応状況
手元にあるスマートフォンのhtml5対応状況を、http://html5test.com/ で確認したのでメモ。
- iphone4 (4.0.2): Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0_2 like Mac OS X; ja-jp) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A400 Safari/6531.22.7
- iphone3G (3.1.2): Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; ja-jp) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16
- android2.1: Mozilla/5.0 (Linux; U; Android 2.1-update1; ja-jp; HTCX06HT Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17
- (HTC Desire X06HT, ファームウェアバージョン: 2.1-update1, ブラウザバージョン: WebKit3.1)
(※) ユーザエージェントは、「javascript:alert(navigator.userAgent)」で表示されたもの
iPhone4 | iPhone3G | Android2.1 | ||
---|---|---|---|---|
Parsing rules | <!DOCTYPE html> triggers standards mode | YES | YES | YES |
HTML5 tokenizer | NO | NO | NO | |
HTML5 tree building | NO | NO | NO | |
SVG in text/html | NO | NO | NO | |
MathML in text/html | NO | NO | NO | |
Canvas | canvas element | YES | YES | YES |
2D context | YES | YES | YES | |
Text | YES | YES | YES | |
Video | video element | YES | YES | YES |
Subtitle support | NO | NO | NO | |
Poster image support | YES | YES | YES | |
MPEG-4 support | YES | YES | NO | |
H.264 support | YES | YES | NO | |
Ogg Theora support | NO | NO | NO | |
WebM support | NO | NO | NO | |
Audio | audio element | YES | YES | YES |
PCM audio support | YES | YES | NO | |
MP3 support | YES | YES | NO | |
AAC support | YES | YES | NO | |
Ogg Vorbis support | NO | NO | NO | |
WebM support | NO | NO | NO | |
Local devices | device element | NO | NO | NO |
Elements | Embedding custom non-visible data | NO | NO | NO |
Section elements | 6 out of 7 | NO | NO | |
Grouping content elements | NO | NO | NO | |
Text-level semantic elements | NO | NO | NO | |
hidden attribute | NO | NO | NO | |
Scroll into view | YES | YES | YES | |
contenteditable attribute | YES | YES | YES | |
Forms | input element types | YES | 5 out of 13 | 6 out of 13 |
input element attributes | 8 out of 10 | 1 out of 10 | 5 out of 10 | |
Other form elements | NO | NO | 1 out of 5 | |
Form validation | YES | NO | NO | |
User interaction | Drag and drop | YES | NO | YES |
Undo history | NO | NO | NO | |
Session history | YES | NO | NO | |
Text selection | YES | YES | YES | |
Microdata | Microdata | NO | NO | NO |
Web applications | Application Cache | YES | YES | YES |
Custom scheme handlers | YES | NO | NO | |
Custom content handlers | YES | NO | NO | |
Geolocation | Geolocation | YES | YES | YES |
WebGL | 3D context | NO | NO | NO |
Communication | Cross-document messaging | YES | YES | YES |
WebSocket | NO | NO | NO | |
Server-Sent Events | YES | NO | NO | |
Files | FileReaderAPI | NO | NO | NO |
Storage | Session storage | YES | YES | YES |
Local storage | YES | YES | YES | |
IndexedDB | NO | NO | NO | |
Web SQL Database | YES | YES | YES | |
Workers | Web Workers | NO | NO | YES |
WebSocket。。。
iPhone Safariだとaudio/videoタグのautoplay, autobuffer属性が効かない
Resources - Safari - Apple Developer の
User Control of Downloads Over Cellular Networks」の部分
In Safari on iPhone OS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, autobuffering and autoplay are disabled. No data is loaded until the user initiates it. This means the JavaScript play() and load() methods are also inactive until the user initiates playback, unless the play() method is triggered by user action. In other words, a user-initiated Play button works, but an onLoad play event does not.
「携帯が従量課金とかの可能性があるから、autoplayとautobufferは無効にしてるよ。
ユーザが意図して再生を開始するまで、データはロードされないよ。
それは、ユーザのアクションをトリガーにして再生を開始するまでは、JSのplay()とかload()も機能しないってことを意味するよ。
つまり、ユーザによって開始されたplay()は動作するけど、onloadとかのイベントだと動作しないよ。」
って感じでしょうか。
たしかに、実際以下のようなhtmlを書いても、再生されなかった。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>html5 audio test on iPhone</title> </head> <body> <audio src="music.mp3" autoplay></audio> </body> </html>
でも、下のようなhtmlは再生されてはいけないはずなのだが再生された。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>html5</title> <script type="text/javascript"><!-- function onload_play() { var aud = document.getElementById('aud'); aud.load(); aud.play(); } //--></script> </head> <body onload="onload_play();"> <audio id="aud" src="music.mp3"></audio> </body> </html>
これがバグなのかどうかはわからない。
が、いずれにせよ、サイレントモードを無視して再生されるので、
音を鳴らすのは微妙。
なんかいい方法はないのか。。。
html5 の File API を使って、ローカルのテキストファイルを読み込んでみる
とっても簡単です(Firefox 3.6 で動作を確認)。
html
fileのアップロードフォームにonchangeを仕込んでおきます。
あとは、読み込んだファイルの内容を表示するtextareaを用意。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <input type="file" onchange="read(this)" /> <hr /> <textarea id="text" cols="80" rows="20" wrap="off"></textarea> </body> </html>
javascript
function read (ele) { if (!ele.files.length) return; var file = ele.files[0]; if (!/^text\//.test(file.type)) return; // text/plain, text/html, ... var fr = new FileReader(); fr.onload = function () { document.getElementById('text').value = fr.result; // 読み込み結果をtextareaに }; fr.readAsText(file); // ファイルをテキストとして読み込む }
readAsTextの2つ目の引数で文字コードを指定できます*1が、
指定しなくても上手いこと推定してくれるっぽいです。
(UTF-8, SJIS, EUC は普通に読み込めました。)
file フォームに multiple をセットしておけば、
複数ファイルの中身をマージした結果とかも簡単に出せちゃいそうですね
html5の File API を使って、アップロード無しで画像プレビュー
画像をプレビューするために、サーバへアップロードする必要がなくなります*1。
html
fileのアップロードフォームにonchangeを仕込んでおきます。
あとは、プレビュー表示用の要素だけ準備。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <form> <input type="file" name="file" onchange="preview(this)" /> </form> <hr /> <b>preview:</b><br /> <div id="preview_field"></div> </body> </html>
javascript
function preview(ele) { if (!ele.files.length) return; // ファイル未選択 var file = ele.files[0]; if (!/^image\/(png|jpeg|gif)$/.test(file.type)) return; // typeプロパティでMIMEタイプを参照 var img = document.createElement('img'); var fr = new FileReader(); fr.onload = function() { img.src = fr.result; // 読み込んだ画像データをsrcにセット document.getElementById('preview_field').appendChild(img); } fr.readAsDataURL(file); // 画像読み込み // 画像名・MIMEタイプ・ファイルサイズ document.getElementById('preview_field').innerHTML = 'file name: ' + file.name + '<br />' + 'file type: ' + file.type + '<br />' + 'file size: ' + file.size + '<br />'; }
スバラシイ。
html5で複数ファイルアップロード&保存
とても簡単。
html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <form action="save.cgi" method="POST" enctype="multipart/form-data"> <input type="file" name="files" multiple /> <input type="submit" value="submit" /> </form> </body> </html>
input type="file" で multiple属性を追加するだけ。
canvasに描いた絵(画像)をサーバに保存
canvasに描かれたデータを取得するには、canvas.toDataURL() を使います。
toDataURL()で得られるのは、base64エンコードされた画像情報なので、
それをサーバに送って、base64デコードして保存という流れです。
toDataURL()して、そのデータをPOSTする部分のjavascript (ajax部分が面倒なので、prototype.jsを使用)
function saveImage() { var imgdata = $('canvas_id').toDataURL(); // デフォルトだとpng, 引数でjpegとかも可能 imgdata = imgdata.replace('data:image/png;base64,', ''); // 頭のいらない部分を落とす new Ajax.Request(<画像保存CGIのURL>, { parameters: 'img=' + imgdata, // 画像データを送信 onComplete: function(res) { // callback 別になくてもよいが。 if (res.responseText != 'ok') alert('error'); } }); }
サーバ側(画像保存CGI)
#!/usr/bin/perl use strict; use warnings; use CGI; use MIME::Base64; my $q = CGI->new; my $img = $q->param('img'); # base64エンコードされた画像データを取得 my $res_msg = 'error'; if ($img) { open(my $fh, '>', 'image.png') or die $!; # 保存するファイル名でopen print $fh MIME::Base64::decode($img); # base64デコード close($fh); $res_msg = 'ok'; } print "Content-type:text/plain\n\n"; print $res_msg; exit;
これで、image.png にcanvasの画像が保存されます。
origin-clean flag
canvasにはセキュリティの面から、origin-cleanフラグというものが存在しているらしいです。
このフラグが false になっている場合、toDataURL()は「security error」となって、失敗します。
これに関する原文は、以下です。
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#security-with-canvas-elements
canvasは、最初はorigin-cleanフラグがtrueにセットされていますが、
いくつか特定のアクションをするとorigin-cleanフラグがfalseになります。
例えば、以前の記事 でやった画像の読み込み(drawImage)とかで、
その画像が canvasを表示しているページと同じドメインの画像なら toDataURL()できますが、
別ドメインの画像をcanvasに描画してしまうと、origin-cleanフラグがfalseになって、toDataURL()できなくなります。
なので、外部の画像を読み込ませて、お絵かきさせて、サーバで保存とかしたい場合は、
一度サーバ側で画像をダウンロードして、それをcanvasに描画する必要がありそうです。
canvasで簡単なお絵かきをしてみたメモ
タイトルのまんまです。
Firefox/Chromeあたりで動きます。
html
canvasを用意しとくだけです。
canvas内の座標を取るときに、layerX/Y で取りたいので、styleにposition:relativeを当てておきます。
(positionが当たってないと、layerX/Yは pageX/Y の値と等しくなるらしいので。
positionを当てたくないなら、pageX/Yからcanvasのオフセットを引いたりすることになるんでしょうか。)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <canvas id="c" style="position:relative; border:3px solid #999" width="600" height="480"></canvas> </body> </html>
javascript
var cEle, cCtx; var drawing = false; // ドラッグ中フラグ window.addEventListener('load', function() { // window onload cEle = document.getElementById('c'); cCtx = cEle.getContext('2d'); // canvasの設定 cCtx.lineJoin = 'round'; // 角を丸く cCtx.lineCap = 'round'; // 線の終端を丸く cCtx.lineWidth = 5; // 線の幅 cCtx.strokeStyle = '#00F'; // 線の色 // event cEle.addEventListener('mousedown', start, false); // canvas上のmousedownイベントでstart()を呼び出す cEle.addEventListener('mousemove', move, false); // canvas上のmousemoveイベントでmove()を呼び出す window.addEventListener('mouseup', stop, false); // window上のmouseupイベントでstop()を呼び出す }, false); function start(event) { cCtx.beginPath(); // サブパスリセット cCtx.moveTo(event.layerX, event.layerY); // 初期座標を指定 drawing = true; // ドラッグ中フラグを立てる move(event); // ドラッグせずにすぐmouseupした場合に、点を書くため } function move(event) { if (!drawing) return; cCtx.lineTo(event.layerX, event.layerY); // 直前の座標と現在の座標を直線で繋ぐ cCtx.stroke(); // canvasに描画 } function stop(event) { if (!drawing) return; cCtx.closePath(); // サブパスを閉じる drawing = false; // ドラッグ中フラグを落とす }
stop()をcanvasでなくwindowに登録しているのは、
canvas上でmousedownし、ドラッグしたままcanvas外まで出てからmouseupした際にもちゃんと動作するためです。
クリックした場合、Firefoxでは点が描画されますが、Chromeでは描画されません。
また、FirefoxでもlineCapがroundでないと点は描画されません。
おまけ
ネット上の画像をcanvasに描画できる機能を追加してみます。
html
画像のURLを入力するフォームを追加
<form onsubmit="loadImage(this.image_url.value); return false;"> Image URL: <input type="text" size="80" name="image_url" /> <input type="submit" value="load" /> </form>
javascript
function loadImage(imgUrl) { var img = new Image(); img.src = imgUrl + '?' + (new Date/1); // 一応、キャッシュ対策とかしとく img.onload = function() { cCtx.drawImage(img, 0, 0); // 画像が読み込めたらcanvasに描画 } img.onerror = function() { alert('image load error'); } }
リサイズとか何もせずにただ描画しているので、はみ出した部分は描画されません。
Chromeは無理ですが、Firefoxなら [右クリック] --> [名前を付けて画像を保存] で
canvasの画像をローカルに保存できます。
(※) なお、紹介したコードはわかりやすく書くために、グローバル変数とか気にせず使ってますが、
本当ならクラスとか作って、その中に閉じ込める方がベターです。
各メソッドやプロパティなどの詳細は、Canvasリファレンス - HTML5.JP を参照。