2011/08/04

SVGでアニメーションさせる

SVGでアニメーションをさせるには、SVGタグ内でアニメーションを指定します。






<svg height="200" width="400">
<polygon fill="#FFFF00" points="153,184 100.271,158.186 49.113,186.988 57.37,128.863
    14.168,89.109 72,79 96.458,25.628 123.943,77.506 182.261,84.274 141.416,126.445 " stroke-miterlimit="10" stroke="#000000">
 <animatetransform attributename="transform" begin="0s" dur="3s" from="360 50 50" repeatcount="indefinite" to="0 50 50" type="rotate">
 </animatetransform>
 <animatemotion begin="0s" dur="5s" path="M 0 100 L 400 100" repeatcount="indefinite" rotate="auto"></animatemotion>
</polygon>
</svg>


2011/08/03

データをローカルに保存する

データをローカルに保存するには、Web Storageを使います。
従来はクッキーを使って最大4KBまでは保存できましたが、HTML5はWeb Storageを使って5MB程度の保存ができるようになりました。
AmazonやTwitter等のサイトは既にWeb Storageを使ったデータの保存が活用されているそうです。

Web Storageには、sessionStorageとlocalStorageの2種類があります。

sessionStorageはその名の通り、セッション毎に使用されるストレージです。
ウィンドウやタブ毎に保存場所が異なり、sessionStorageに保存されたデータは他のウィンドウやタブからは参照することができません。また、セッション終了時(ウィンドウやタブを閉じる)には同時にsessionStorageに保存されたデータも削除されます。

localStorageはプロトコル・ドメイン・ポート番号が同じであれば全てのセッションでデータが共有され、セッションが終了してもデータは永続されます。


Web Storageのメソッド・プロパティ一覧
メソッド・プロパティ内容
length保存データの個数
key(index)指定インデックスに格納されているキー
getItem(key)指定キーに格納されている値
setItem(key, value)指定キーに指定値を格納
removeItem(key)指定キーと値の組を削除
clear()全データを削除


Web Storageにはイベントはstorageイベント1つしかありません。
以下に使用上のプロパティ値を載せますが、本項執筆時点ではまだ未実装の物が多いようです。


storageイベントで渡されるオブジェクト
プロパティ内容
keyイベント発生対象のキー
oldValueキーに対する旧いvalue
newValueキーに対する新しいvalue
urlイベントが発生したURL
storageArea変更データのストレージへの参照


以下はsessionStorageの例です。
KeyとValueに適当な値を入力して「保存」ボタンを押すとsessionStorageに保存されていく様子が分かります。また、一旦ウィンドウを閉じた後で再度開いても、データが残っていないことが分かると思います。


Key : Value:





Key : <input type="text" name="key" id="key" size="20">
  Value:<input type="text" name="value" id="value" size="20">
<input type="button" value="保存" onClick="save()">
<input type="button" value="全クリア" onClick="allClear()">
<div id="list"></div>
<script type="text/javascript">
window.onload = function() {
    list();
}
var storage = window.sessionStorage;
function save() {
    var keyValue = document.getElementById("key").value;
    var valValue = document.getElementById("value").value;

    if ( keyValue && valValue ) {
        storage.setItem(keyValue, valValue);
    }
    keyValue = "";
    valValue = "";
    list();
}
function list() {
    var list = "";
    for ( var i = 0 ; i < storage.length ; i++ ) {
        var _key = storage.key(i);
        list += "sessionStorage[" + i + "] = Key:" + _key + "  Value:" + storage.getItem(_key) + "      <a href=\"#\" onClick=\"remove(" + i + "); return false;\">削除</a><br />";
    }
    document.getElementById("list").innerHTML = list;
}
function remove(index) {
    var _key = storage.key(index);
    storage.removeItem(_key);
    list();
}
function allClear() {
    storage.clear();
    list();
}
</script>


以下はlocalStorageを使った例です。
sessionStorageをlocalStorageに変更しただけで同じです。
sessionStorageとlocalStorageは同じメソッド、プロパティが使えます。
また、localStorageはセッションが終了しても残るため、前のセッションで保存したものをページのロード時に表示させるためにonloadイベントを追加しています。




Key : Value:





Key : <input type="text" name="key" id="key" size="20">
  Value:<input type="text" name="value" id="value" size="20">
<input type="button" value="保存" onClick="save()">
<input type="button" value="全クリア" onClick="allClear()">
<div id="list"></div>
<script type="text/javascript">
window.onload = function() {
    list();
}
var storage = window.localStorage;
function save() {
    var keyValue = document.getElementById("key").value;
    var valValue = document.getElementById("value").value;

    if ( keyValue && valValue ) {
        storage.setItem(keyValue, valValue);
    }
    keyValue = "";
    valValue = "";
    list();
}
function list() {
    var list = "";
    for ( var i = 0 ; i < storage.length ; i++ ) {
        var _key = storage.key(i);
        list += "sessionStorage[" + i + "] = Key:" + _key + "  Value:" + storage.getItem(_key) + "      <a href=\"#\" onClick=\"remove(" + i + "); return false;\">削除</a><br />";
    }
    document.getElementById("list").innerHTML = list;
}
function remove(index) {
    var _key = storage.key(index);
    storage.removeItem(_key);
    list();
}
function allClear() {
    storage.clear();
    list();
}
</script>



2011/08/02

オフラインでも見られるようにする

Webサーバーにアクセスした時に必要なファイルをローカルに保存しておけば、ネットに接続していない時でも接続しているのと同じようにサービスを受けることができるようになります。
このHTML5の仕組みを「アプリケーションキャッシュ」と言います。

キャッシュするファイルを指定するためにhtml要素に「manifest=”ファイル名.manifest”」属性を付けます。
そして、Webサーバー側の設定ファイルである、.htaccessファイルに以下の1行を追加します。


AddType text/cache-manifest              manifest


これで、manifest拡張子のファイルはMIME/TYPE「text/cache-manifest」でクライアントに送信されるようになります。マニフェストファイルは「text/cache-manifest」で送信しないとクライアント側で認識されません。

manifestファイルの中身は以下の様にします。


CACHE MANIFEST
#  #はコメントです。
# 下のCACHEから、キャッシュするファイル名を指定します。
# 何もラベルが無い場合は、このCACHEを指定したのと同じになります。
CACHE
index.html

# 下のNETWORKからはキャッシュしないファイル名を指定します。
NETWORK
・・・・.cgi
http://maps.google.com/

# ここに*(アスタリスク)を置くと、全てのファイルという
# 意味になります。この場合、全てのファイルをキャッシュ
# しない設定になります。

# 下のFALLBACKから、キャッシュしていないリソースにアクセスした
# 時に表示する代替リソースを指定します。
FALLBACK
# abc.htmlがキャッシュされていない場合、error.htmlを表示します。
abc.html  error.html




上のようにマニフェストを設定すると、例えば「index.html」ファイルはクライアントのローカルにキャッシュされるようになります。

例えばここで、サーバー側でindex.htmlファイルが更新された場合、index.htmlファイルはキャッシュ指定されているのでクライアント側には旧いindex.htmlファイルが表示され続けることになります。

このような場合は、サーバー側でindex.htmlファイルの更新と同時にマニフェストファイルの更新も行えば、キャッシュ内のファイルも更新されるようになります。
マニフェストファイルの日付が更新されていると、キャッシュされたファイルも新しい物に更新されます。

また、javascriptから明示的にキャッシュを更新したい場合、「window.applicationCache.update()」を呼べば、マニフェストファイルを再ダウンロードしてキャッシュも更新してくれるようになります。


FirefoxでURL入力欄に「about:cache」と指定すれば、キャッシュ情報が表示されます。
ChromeでURL入力欄に「chrome://appcache-internals/」と指定すれば、キャッシュ情報が表示されます。

経度・緯度から住所を取得する

Google Map APIを利用して、経度、緯度情報から住所を取得するには、Geocoderオブジェクトを使用します。








<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0" />
<div id="map" style="border:1px solid #0000FF; width:400px; height:300px;"></div>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript">
var map, infoWindow;
if (!navigator.geolocation) {
    var map = document.getElementById("map");
    map.innerHTML = "このブラウザは位置情報をサポートしていません。";
} else {
    navigator.geolocation.getCurrentPosition(showMap2, showError2,
                {
                    enableHighAccuracy:true,
                    maximumAge:0,
                    timeout:10000
                });
}
function showMap2(position) {
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;

    // 取得して現在位置をGoogle Maps APIに渡す
    var mapCenter = new google.maps.LatLng(latitude, longitude);
    var mapDiv = document.getElementById("map");
    var op = {
        zoom:16,            // 表示倍率を指定
        center:mapCenter,    // 中央位置を指定
        mapTypeId:google.maps.MapTypeId.ROADMAP,
        scaleControl:true    // 倍率変更を指定
    };
    // 地図を表示
    map = new google.maps.Map(mapDiv, op);

    // マーカーを表示
    var marker = new google.maps.Marker({
        position:mapCenter,
        map:map,
        title:"現在地"});
    map.setCenter(mapCenter);

    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({'latLng': mapCenter}, getGoogleMapAddress);
    infoWindow = new google.maps.InfoWindow();
}
function showError2(error) {
    var l = document.getElementById("map");
    switch(error.code) {
        case error.UNKNOWN_ERROR:
            l.innerHTML = "UNKNOWN_ERROR:" + error.message;
            break;
        case error.PERMISSION_DENIED:
            l.innerHTML = "PERMISSION_DENIED:";
            break;
        case error.POSITION_UNAVAILABLE:
            l.innerHTML = "POSITION_UNAVAILABLE:";
            break;
        case error.TIMEOUT:
            l.innerHTML = "TIMEOUT:";
            break;
    }
}
function getGoogleMapAddress(results, status){
    var address;
    if (status == google.maps.GeocoderStatus.OK) {
        address = results[0].formatted_address;
        infoWindow.setPosition(results[0].geometry.location);
        infoWindow.setContent(address);
        infoWindow.open(map);
    } else {
    m = document.getElementById("map");
    m.innerHTML = "エラーが発生しました。" + status;
    }
}
</script>


Google Mapに現在位置を表示する

Google Mapに位置情報を表示するには、「現在位置を取得する」と同様に現在位置を取得し、その後Google Map APIに位置を指定して表示させます。








<div id="map" style="border: 1px solid #0000FF; width:400px; height:300px;"></div>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript">
if (!navigator.geolocation) {
    var map = document.getElementById("map");
    map.innerHTML = "このブラウザは位置情報をサポートしていません。";
} else {
    navigator.geolocation.getCurrentPosition(showMap, showError,
                {
                    enableHighAccuracy:true,
                    maximumAge:0,
                    timeout:10000
                });
}
function showMap(position) {
    var lat = position.coords.latitude;
    var long = position.coords.longitude;

    // 取得して現在位置をGoogle Maps APIに渡す
    var mapCenter = new google.maps.LatLng(lat, long);
    var mapDiv = document.getElementById("map");
    var options = {
        zoom:16,            // 表示倍率を指定
        center:mapCenter,    // 中央位置を指定
        mapTypeId:google.maps.MapTypeId.ROADMAP,
        scaleControl:true    // 倍率変更を指定
    };
    // 地図を表示
    var map = new google.maps.Map(mapDiv, options);

    // マーカーを表示
    var marker = new google.maps.Marker({
        position:mapCenter,
        map:map,
        title:"現在地"});
    map.setCenter(mapCenter);
}
function showError(error) {
    var l = document.getElementById("map");
    switch(error.code) {
        case error.UNKNOWN_ERROR:
            l.innerHTML = "UNKNOWN_ERROR:" + error.message;
            break;
        case error.PERMISSION_DENIED:
            l.innerHTML = "PERMISSION_DENIED:";
            break;
        case error.POSITION_UNAVAILABLE:
            l.innerHTML = "POSITION_UNAVAILABLE:";
            break;
        case error.TIMEOUT:
            l.innerHTML = "TIMEOUT:";
            break;
    }
}
</script>


定期的に位置情報を取得する

定期的に位置情報を取得するには、Geolocation APIのwatchPositionメソッドを使用します。

watchPosition(
位置情報取得成功Callback関数,
位置情報取得失敗Callback関数(省略可),
位置情報取得オプション(省略可)
);

watchPositionメソッドは、getCurrentPositionと基本的に同じですが、getCurrentPositionが1回だけ位置情報を通知するのに対し、watchPositionは位置が変更される度に通知されるというのが違います。また、戻り値に位置情報の継続取得を中止するためのIDが返却されるのも異なる点です。
各オブジェクト、プロパティ等の意味は「現在位置を取得する」を参照して下さい。








<div id="location2"></div>
<input type="button" onclick="stopWatchPosition();" value="ボタンを押すと位置情報の継続取得を中止します"></input>
<script language="javascript" type="text/javascript">
var stopID;
if (!navigator.geolocation) {
    var l = document.getElementById("location2");
    l.innerHTML = "このブラウザは位置情報をサポートしていません。";
} else {
    stopID = navigator.geolocation.watchPosition(locationGetSuccess2
        , locationGetError2,
        {
            enableHighAccuracy:true,
            maximumAge:0,
            timeout:1000
        });
}
function locationGetSuccess2(position) {
    var l = document.getElementById("location2");
    var now = new Date();
    l.innerHTML = "緯度:" + position.coords.latitude + "<br />"
            + "経度:" + position.coords.longitude + "<br />"
            + "Time:" + now.getHours() + ":" + now.getMinutes() + ":"
            +  now.getSeconds() + "<br />";
}
function locationGetError2(error) {
    var l = document.getElementById("location2");
    switch(error.code) {
        case error.UNKNOWN_ERROR:
            l.innerHTML = "UNKNOWN_ERROR:" + error.message;
            break;
        case error.PERMISSION_DENIED:
            l.innerHTML = "PERMISSION_DENIED:";
            break;
        case error.POSITION_UNAVAILABLE:
            l.innerHTML = "POSITION_UNAVAILABLE:";
            break;
        case error.TIMEOUT:
            l.innerHTML = "TIMEOUT:";
            break;
    }
}
function stopWatchPosition() {
    navigator.geolocation.clearWatch(stopID);
}
</script>


現在位置を取得する

現在位置を取得するには、Geolocation APIのgetCurrentPositionメソッドを使います。
お使いのPCやスマートフォンがGPSやWIFI等を利用した位置情報の取得に対応していれば、ブラウザで位置情報を使用することが可能になります。

navigator.geolocation.getCurrentPosition(位置情報取得成功Callback関数
, 位置情報取得失敗Callback関数(省略可)
, 位置情報取得オプション(省略可)
)

位置情報取得オプションには、以下のPositionOptionsオブジェクトを指定できます。


位置情報取得オプション指定PositionOptionsオブジェクト
プロパティ内容
enableHighAccuracy高い精度で位置情報を取得(true/false)
timeoutタイムアウト時間(ミリ秒)
maximumAge有効期間(ミリ秒)


位置情報取得成功Callback関数には、Positionオブジェクトが引数として渡され、Javascriptからそれらの情報にアクセスすることができます。


位置情報取得成功Callbackに渡されるPositionオブジェクト
プロパティ内容
latitude緯度
longitude経度
altitude高度
accuracy緯度、経度の誤差
altitudeAccuracy高度の誤差
heading方角
speed速度(m/秒)
timestampタイムスタンプ型の時刻情報


位置情報取得失敗Callback関数には、PositionErrorオブジェクトが引数として渡されます。

PositionErrorオブジェクト
プロパティ内容
codeエラーコード
messageエラーメッセージ

エラーコード
エラーコード意味
UNKNOWN_ERROR(0)不明なエラー
PERMISSION_DENIED(1)位置情報の利用が許可されていない
POSITION_UNAVAILABLE(2)位置情報が取得できない
TIMEOUT(3)位置情報取得タイムアウト


以下は、現在の位置情報を取得して表示する例です。







<div id="location"></div>
<script type="text/javascript">
if (!navigator.geolocation){
    window.alert("このブラウザはGeolocation APIを利用できません");
}else{
    navigator.geolocation.getCurrentPosition(
                    getLocationSuccessCallback,
                    getLocationErrorCallback,
            {
                enableHighAccuracy:true,
                maximumAge:0,
                timeout:10000
            }
                    );
}

function getLocationSuccessCallback(position){
    var l = document.getElementById("location")
    l.innerHTML = "緯度:" + position.coords.latitude + "<br />"
    +  "経度:" + position.coords.longitude + "<br />";
}
function getLocationErrorCallback(error){
    var message = "";
    switch(error.code)
    {
        case error.TIMEOUT:
            message = "タイムアウトが発生しました";
            break;
        case error.POSITION_UNAVAILABLE:
            message = "位置情報が利用できませんでした";
            break;
        case error.PERMISSION_DENIED:
            message = "Geolocation APIの利用権限がありません";
            break;
        case error.UNKNOWN_ERROR:
            message = "UNKNOWN_ERROR:" + error.message;
            break;
    }
    document.getElementById("location").innerHTML = message;
}
</script>


他ブラウザからドロップする

ブラウザ外の別アプリケーションからドラッグ&ドロップを受け入れるには、ドロップイベントを補足してdataTransferでドロップされた情報を読み取ります。
他アプリケーション(ブラウザ)からドロップすることができるMIME/TYPEは、「text/plain」「text/html」「text/xml」「text/uri-list」です。

下の例は、他ブラウザで適当なサイトを表示して適当な文字を選択し、そのまま四角の中にドロップすると、ドロップされた情報を表示する例です。
テキストにはHTMLタグがある可能性があるので、タグが処理されないように一旦HTMLのDiv要素に入れることでタグが処理されないようにエスケープ処理をかけています。
FireFoxで実行すると自動的にリンク読み込み処理が走ってエラーが表示されるようです。



ここにドロップして下さい。





<div id="dropSample" ondragover="onDragOver2(event)" onDrop="onDrop2(event)" style="border:1px solid #0000FF;border-radius:4px; width:300px; height:200px">
ここにドロップして下さい。
</div>
<div id="dropItemInfo"></div>
<script type="text/javascript">
var di = document.getElementById("dropItemInfo");
function onDrop2(event) {
    var text = event.dataTransfer.getData("text/plain");
    di.innerHTML = escapeHTML(text);
}
function onDragOver2(event) {
    event.preventDefault();
}
function escapeHTML(str) {
    var div = document.createElement("div");
    var text = document.createTextNode("");
    div.appendChild(text);
    text.data = str;
    return div.innerHTML;
}
</script>