[CSS3|jQuery|Webkit]translate3d等のtransformで設定した現在値を取得したい

2011
09.26

皆さんご無沙汰しております、Web屋の中の人です。

手術やらリハビリやら、少々複雑なシステム開発案件等でお勉強の方を長期に渡り休止しておりましたが久々にやってみようと言う事でインターフェイスの調整やバグ取り、むしろソースの整理なんかをしているとタイトルの状況になりました。

iPhoneiPad 上でアニメーションしたい時は、jQuery.animate は原則使い物になりませんので CSS3transform を利用してグリグリします。

 

ちなみに今回はタイトルに記載している通り「webkit」ブラウザのみ対象としていますのでご注意ください。
※Safari、Chromeなどなど。

 

ありがちな例を挙げると、js でスライダーなんかを作っている時に「この要素の x 座標は今ナンボ程動いてますか」と言う状況です。
jQuery を使っているなら簡単ですよね、

1
alert($('#hoge').css("left"));

これでパパッと解っちゃう訳で、非常に便利です。

所が CSS3transform で設定する時は、

1
2
3
#hoge {
    -webkit-transform: translate3d(237px, 5px, 0px);
}

こんな感じなので、x 座標ってどうやって書けば取得出来るの?となった訳です。

1
alert($('#hoge').css("-webkit-transform"));

取りあえずこう書いてみると、下記のように出力されました。

matrix(1, 0, 0, 1, 237, 5)

うーん、4番目が x で5番目が y なんだろうな…じゃあ z はどこに出るんだろう。

 

ブラウザ限定の強み、と言う事で素直に WebKitCSSMatrix で分解する事にした。仕様書通り使うと

1
2
3
var obj = document.getElementById('hoge');
var m = new WebKitCSSMatrix(obj.style.webkitTransform);
alert(m.e);

こんな感じになりますが、折角 jQuery を使っているので…

1
2
var m = new WebKitCSSMatrix($('#hoge').css('-webkit-transform'));
alert(m.e);

こんな感じでも取得可能です、結構乱暴に使えますね。
取得出来る値はコチラの仕様書に網羅されています、かなり色々設定出来るだけに複雑ですね…単純な物は a〜f って覚えておくのが良さそうです。

 

 

それではまた。

 

 

[CSS3]transition-durationの時間を無視して途中で終わってしまう時のTips

2011
06.29

皆さんご無沙汰しております、どっぷりと新製品の開発に浸かっております。

そこそこ経験を積んでからのチャレンジだったので、それなりに順調なのですが…やっぱりハマるとこはハマりますね。

 

その中でもハマると厄介な部分の Tips を紹介したいと思います。

 

内容は件の通り、再現は確実に取れる訳ではないのですが javascript から要素の transition プロパティを設定して、すぐに値を変更させようとすると再現しやすいようです。

具体的には下記のような感じ。

 

1
2
3
4
5
6
7
8
9
// 検証環境は safari です
var hoge = function() {
    $('#foo').css('-webkit-transition', 'all 1000ms ease-in');
    $('#foo').css('-webkit-transform', 'rotate(360deg)');
}

$(function() {
    hoge();
});

 

こんな具合で実行すると、要素 #foo が1秒かけてぐるりと1回転するハズが少し傾いた直後に一瞬で「360度回転した」事になりアニメーションの最終結果まで飛ばされてしまう。

と言う現象が発生します。

ちなみに Line : 3〜4 をチェインさせても発生する時はしますし、はたまたこれで普通に動いちゃったりもするので何だか良く解りません。

先に fix してしまったので、それ以降発生せず原因が不明なままなんですよね。発生しないに越した事は無いけど。

 

取りあえず css の元ファイルの方でデフォルト状態を色々変更しつつ検証していたのですが、同じプロパティを持つ3つの要素があって、2つの要素は行けるけど、1つだけ駄目だったと言うケースもあったりなんかして云々。

結局 transition の設定を値変更処理が追い抜いてしまっているんじゃないかと言う推測に至りました。

 

と言う訳で困った時の setTimeout です、本当はコールバックを駆使するのが一番確実だと思うのですが、コールバックイベントとして用意されているのは webkitTransitionEnd と言うのしか見つけられませんでした。

このイベントは「値の変化が終了したら発生」するのでこの場合は約に経ちませんので、ここは setTimeout に頼る事にしました。

同期的な部分は上記のイベントでコールバックを蹴って取れば良いと思う、終わり良ければなんとやら。事後処理で手を打つのも悪く無い選択だと思います。

では fix 版のコードを書いてみます。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
// 検証環境は safari です
var timer;
var hoge = function() {
    $('#foo').css('-webkit-transition', 'all 1000ms ease-in');
   
    timer = setTimeout(function() {
        $('#foo').css('-webkit-transform', 'rotate(360deg)');
    }, 0);
}

$(function() {
    hoge();
});

 

取りあえずこれで動いています、今の所は。

setTimeout の第2引数は最初100でやってたのですが、0でも問題無いようです。

すると遅延による処理追い越しとか単純な事じゃないのかもしれませんね、今回のように無名関数で実行する等のプロセスを分ける事に意味があるのかもしれない。

 

全部推測だけど

 

ActionScriptJavaScript を今までガッツリやってる方達からすると基本だろ!と言われそうですが、これ系のスクリプトが得意と言うのは「いかに同期を取らせるのが上手いか」なんだろうなと思いました。

 

それではまた。

 

[iPhone|iPad]iOSにおけるhtml5::manifestの削除について Vol.2

2011
06.02

さて皆さん、早速ですが manifest は結局どう言う仕様なのよ?と言う事を私なりに解釈した解説を実機スクリーンショットを交えて紹介して行こうと思います。

 

最初は公式シミュレータでやっていたのですが、Mac の HDD を参照してしまうので容量 1TB とかあってキャッシュして数値を動かすのが非常に困難だったので実機にしました。実機は 16.9GB みたいな感じで 100MB 単位で動かせるので都合が良いです。

 

まずはこの manifest とやらはどれぐらいのキャッシュを許可出来るんだろうかと言う所から、仕様書によるとデフォルトは 5M だけど拡張可能との事。

検証方法は 3.2MB のm4v ファイル(何でも良いんだけどね)を複製しておいて、それのパスを追加しては更新の繰り返しと言う作業です。

適当な要素に #console 的な id を書いて、イベント拾って出力しながら経過を見守ります。

 

まず最初に警告が出たのはキャッシュが 10MB を超えた時でした、

10MBを超えた時に即座にアラートが出現

なんだか良く解らないのですが、「増やす」を選択しても「キャンセル」を選択しても unknown error が出力されてキャッシュが止まってしまいました。

コンソールにログを非同期に出力

取りあえず例外が投げられたらキャッシュをやり直す処理を追記して、さらにパスを増やして行くと…

25MBでも確認した

今度は 25MB を超えた時点でアラートが発生した、同じくエラーが発生したようで再びキャッシュをし直す処理が走りました。

取りあえずもうチョット容量欲しいかなと思うので、更にパスを追記。

50MBも確認した

おお、50MB 行けたね!凄い凄い、普通のサイトならデフォルト 5M で余裕だけど WebApp だと結構リソース多いからね。取りあえずこれぐらい有れば十分かな?

ここから先は試していませんが、50MB は確実に行ける事が分かりました。

 

しかしこれ…10-25-50 の度にいちいち聞かれるんだろうか、アプリに合わせて任意に指定出来ればスマートなのになぁ。

 

さて、ココからが本題。HDD どうなった?な部分ですが、48MB キャッシュした時点で下記のように変化がありました。

befor

after

使用可能の項目の値が 12.9GB から 12.8GB に減少しているのが確認出来ます、まぁローカルにキャッシュしたのだから当然ですね。

で、ここで放置してしまうとユーザーはこの消せないキャッシュの積み重ねでいつか苦しむ事になります。

なんとかこの値を 12.9GB に戻す事が今回のミッションと言う事です。

 

まず manifest から先ほどから追記しまくった m4v のパスを全て消しました、これでそのページにはキャッシュしなくても良いファイルに変わる筈です。更新を完了させてから容量を見ても…変わって無い。

Safari をタスクから切って OS を再起動しても駄目でした、一体どこに保存されているのやら。

通常キャッシュとは別物だと思っていたのですが、設定から Safari のキャッシュをクリアして、Safari を再起動してみたら…

キャッシュを消去

容量復活!

見事容量が復活しました!

これで何とかユーザーを救えそうです、キャッシュクリアの操作をさせるのは難しい事ですが manifest からパスを外してあげるアンインストール的な操作方法を用意してあげるのが良いのでは無いでしょうか。

キャッシュ自体はほっとけば何かの拍子消えるモノですし、多分。(もしかして消えない?)若しくはアプリサイトにその旨を記載しておくとかでも体裁は保てるのではないかと思います。

 

まとめ

  • manifest 自体は削除出来ない(今の所)
  • manifest からパスを外したファイルは通常キャッシュ領域に移動される、又は同じキャッシュ領域だが manifest に書かれていると削除されない仕様のどちらかである
  • manifest からパスを切ったファイルは Safari のキャッシュクリアで消せる
  • manifest 対応サイト乱立によるユーザー資源の浪費を食い止める操作を提供してあげないと大変な事になる
  • Apple が設定画面から消せるような機能を追加してくれるのに期待する

と言った感じですかね、細々したファイルなら然程気にしなくても良さそうですがやはり消せないゴミファイルが溜まって行くのは気持ちが良いモノではありません。

大きなキャッシュを残したいのであれば、削除出来る機能を提供してあげるのがマナーとなりそうですね。

今回は中々興味深い結果となりました、今後の仕様変更に注目して行きたいですね。

 

それではまた。

 

1 | 2

 

[iPhone|iPad]iOSにおけるhtml5::manifestの削除について Vol.1

2011
06.02

暑くなったり肌寒くなったりで体調を崩しそうな大阪ですが、皆様の周りは如何な具合でしょう?

 

さて、今回は結構重要な話しです。

 

今後隆盛を迎えるであろう WebApp はもちろんの事、これから先は PC ブラウザで閲覧する html5 コンテンツも快適性を追求して当たり前のように使われる事になると思われる manifest の仕様についてのレポートを作りました。

実は私は色々開発はしているものの、オフラインで動かすための manifest 指定と言うモノに少々気持ち悪さを覚えています。

理由は単純に「良く解らない」からです、これが凄く気持ち悪い。

 

manifest とは、非常に強力で有用な技術です。簡単に言えば超強力なキャッシュ機能と言うとしっくり来ますね。manifest ファイルにそのページで使われるリソースのパスを書いておくと、ローカルにそれらを保存して Web にアクセスする事無くそれらを利用する事が出来るようになります。

その性能はと言うと、大げさでも無くネイティブアプリに迫る高速読み込みが行え、おまけにオフラインでも何の問題も無いと言う高性能っぷり。

 

初めて知った時は「これはもう使うっきゃ無いよね!」と思いました、だって凄いでしょう?

所が使うに当たって当然出てくる疑問があります、それは「これってどうやって消すの?」と言うシンプルな疑問。

導入方法は数多くのサイトで語られていますが、manifest を消す方法、特に iOS では未だに確立されていません。唯一消す方法は復元を実行すると言う超力技だけと言う現状。

 

本当に消えない、と言うか消せないんです。後述しますが、私も実験中に順番を誤り個人的に所持している iPhone から二度と更新出来ない URL を作ってしまいました。

※これを回避する為の準備も紹介します、絶対にこれは守らないと泣きを見ます。

PC の方は結構なんでもありなので消せる見たいですが、我々が目を向けなければならないのは一般ユーザーの方達です。

 

例えば、こんなケースが想定出来ます。

  • 色々なサイトで manifest を使った高速ブラウジングが次々と実装される
  • ユーザーは色んなサイトを閲覧し続ける
  • なんだか iPhone/iPad の HDD 容量が凄い事になってる
  • 消せない助けて

 

如何でしょう、何とも無惨な事になると思いませんか?ここで彼等に「復元しろ」とか「初期出荷状態に戻せ」なんて言える筈ありません。

この問題の指摘は Apple 社にもメールしておこうと思います、設定の Safari から manifest を個別削除出来る機能をつけないとえらい事になるんじゃないのか?と。

 

さて、取りあえず今の所 manifest 自体は「消す事が出来ない」仕様のようなので、せめて「容量を解放する方法はあるのか?」と言う方向で検証してみましょう。

 

まず manifest を触る際に、絶対に準備しなければならない事があります。

それは Javascript による manifest のチェック機能です、これを必ず最初に作る所から始めましょう。

これが無いと、そのページを開いた瞬間「消す事が出来ないキャッシュが生成されて、以後そのキャッシュしか見ない」と言う状況が発生します。

そうです、永久にそのデバイスからはその URL の情報を更新出来なくなります。先ほど少し触れましたが、私は個人所有の実機でこれをやらかしました :(

 

Javascript ソース

1
2
3
4
5
6
7
8
9
10
var appCache = window.applicationCache;
var checkUpdate = function() {
    if(navigator.onLine) appCache.update();
    else alert('Please update for online.');
}

appCache.addEventListener('updateready', function() {
    appCache.swapCache();
    location.reload();
}, false);

html ソース

1
<input onclick="checkUpdate()" type="button" value="update" />

 

簡単に要所だけ書くとこんな感じのソースになります、ここでは挙動を調べる事に集中するので詳しい設置方法や、細かいイベントの返り値なんかは、凄く丁寧に解説して下さっているブログがあるのでそちらを参照してください。

ちなみにこの appCache.update() はロード完了後に自動的に一回だけ呼ばれますが、記事を読んでると絶対じゃないそうなので念のため適当なボタンからアップデートをチェックする関数を実装しています。

 

そして manifest ファイルを準備して html 要素に manifest=”ファイル名” を追記すれば晴れて「何度でも更新出来る manifest 付きページ」の完成です。やったね!

 

次回は実機のスクリーンショットを交えて、HDD の容量変化を見ながら実際に manifest キャッシュを行ったモノを紹介します。

 

それではまた。

 

1 | 2

 

[IE6|IE7|IE8]cloneNodeで複製したエレメントのidをjQueryから参照出来ない時のTips

2011
05.23

皆さんこんにちわ、なんだか冷えますね。

 

さて、皆大好き IE ブラザーズのお茶目な仕様で少々ハマりましたので仕様解説と解決策の TIPS です。

実は基本的な事かもしれませんが、仕様を理解しておくのに越した事は無いでしょう。

リッチコンテンツ制作時にちょこちょこ問題になる可能性が高いので、便利な関数も作ってみました。改良して使ってもらえればよろしいかと思います。

 

出くわした状況

メニューをスライドしたい時に数がギリギリなので、一番端の要素を予め逆側に複製しておいてスライドした後に複製したエレメントを消去しようとした。

 

症状

IE だけスライドした後に複製元となったエレメントが動き出して、思い通りの動きをしなかった。

 

元ソース

■html

1
2
3
4
5
<ul id="listBox">
    <li id="list1">hoge</li>
    <li id="list2">moge</li>
    <li id="list3">foo</li>
</ul>

■javascript

1
2
3
4
5
6
7
var content = document.getElementById('listBox');
var clone = document.getElementById('list1').cloneNode(true);
clone.id = 'temp';
clone.style.position = 'absolute';
clone.style.left = '600px';
content.appendChild(clone);
$('#temp').animate({left: 400});

 

cloneNode して、id を書き換えて親要素に appendChild して動かそうとした訳です。

IE 以外のブラウザはこれで問題無く動きます、IE 以外は。

IE だけは #temp を呼ぶと複製元である #list1 を参照して、そちらを動かそうとします。ちなみにリスナを仕掛けたり、setTimeout でぐるぐる回してフラグを監視して、存在確認をした後に命令しても一切を無視して複製元を動かします。
更に jQuery から attr を使って id を変えても駄目です、凄い頑固ちゃんです。

 

早速原因を追求していくと、どうにも困った事になっていました。

 

IE は同一プロセス内で cloneNode(複製)されたエレメントは、別プロセスから呼ばれない限り複製元の id を参照する。

 

え、なんで!?と言う仕様ですよね、cloneNode(複製)したプロセスとは別にボタンを用意して、適当に命令を書いた関数を呼び出すと思い通りの動きをします。

もしかしたらちょっと違うのかもしれませんが、大体こんな感じの挙動になる筈です。

 

 

非常に気持ち悪いです。

 

 

ともあれ fix していきましょう、要は cloneNode(複製)したエレメントの挙動がおかしいのならば新規でエレメントを生成して、複製元のエレメントもしくは複製したばかりのエレメントから欲しい情報を新規エレメントに移してから appnedChild すれば良いのです。

 

改良版ソース

■javascript

1
2
3
4
5
6
7
8
9
10
11
12
13
var content = document.getElementById('listBox');
var clone = document.getElementById('list1').cloneNode(true);
var elm = document.createElement('li');
content.appendChild(getCloneNode(clone, elm, 'temp'));
$('#temp').animate({left: 400});

function getCloneNode(clone, elm, id) {
    elm.id = id;
    elm.style.position = 'absolute';
    elm.style.left = '600px';
    elm.innerHTML = colne.innerHTML;
    return elm;
}

 

 

こうやると意図した動きになるはずです、なんだか回りくどいけど。

これは複製したエレメントから新規エレメントに要素を移していますが、多分複製せずに直接突っ込んでも上手く行きそうですね。別処理との兼ね合いで content を外で作ってますが、全部関数内でやっちゃった方が綺麗にかけそうですね。

 

そこはお好みで。

 

なんか悔しかったから複製してやっただけですし。

 

それではまた。