uu.node.add(source, context, position) のpositionに数値を指定可能に
uu.node.add(source, context, position) の position に数値を指定すると、ノードの挿入位置を指定可能になりました。従来通り文字列("first", "prev", "./first", "./last", "next", "last")も指定可能です。
詳しくはこちら http://code.google.com/p/uupaa-js/wiki/uu_node_add
<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8" /> <title>uu.node.add(NodeArray)</title> <style> .div1 { background-color: #111; color: white; } .div2 { background-color: #333; color: white; } .div3 { background-color: #555; color: white; } #contextNode { background-color: blue; } </style> <script src="../../src/uupaa.js"></script> <script> uu.ready(function(uu, doc) { var nodeArray = [uu.div({ "class": "div1" }, "div1"), uu.div({ "class": "div2" }, "div2"), uu.div({ "class": "div3" }, "div3")]; var ctx = uu.id("contextNode"); var pos = [ "first", "prev", "./first", "./last", "next", "last" ]; // uu.add(nodeArray, ctx, pos[3]); uu.add(nodeArray, ctx, 4); }); </script> </head><body> <div id="parentNode"> <div id="firstSibling"> first(0) </div> <div id="prevSibling"> prev(1) </div> <ul id="contextNode">(2) <li id="firstChild"> ./first(.0) </li> <li>(.1)</li> <li id="lastChild"> ./last(.2) </li> </ul> <div id="nextSibling"> next(3) </div> <div id="lastSibling"> last(4) </div> </div> </body></html>
速度が求められる部分に Function#bind を導入するのはまだやめましょう
Function#bind は、ECMAScript-262 5th で導入された Prototype.js 由来のメソッドです。
uupaa.js では Function#bind の互換実装も提供していますが、現在はコメントアウトした状態でリリースしています
Function#bind と 自分でbind相当の処理を書いた場合のベンチマークを http://jsdo.it/uupaa/FunctionBind に載せました(引数を束縛する使い方についてのベンチマークです)。
Browser | ratio | Function#bind | 自分でbind | loops |
Chrome 9 dev | 335% | 171 | 51 | *100 |
Firefox 4 beta 9 | 93% | 298 | 320 | *100 |
Firefox 3.6 | ---- | ---- | 764 | *100 |
IE 8 | 824% | 1590 | 193 | *10 |
IE 9pp7 | 84% | 112 | 134 | *100 |
Opera 11 | 1897% | 1802 | 95 | *100 |
-------------------------------- | ||||
HTC Desire HD (Android 2.2) | 1844% | 166 | 9 | *1 |
IS06 (Android 2.2.1) | 816% | 204 | 25 | *1 |
iPhone 3GS (iOS 4.2.1) | 992% | 258 | 26 | *1 |
Android Dev Phone 1 (Android 1.6) | 1685% | 1415 | 84 | *1 |
HTC Desire HD - 1GHz, 768MB
IS06 - 1GHz, 400MB
iPhone 3GS - 600MHz, 256MB
Dev Phone 1 - 528MHz(384MHz), 192MB
Android は一見フリーズしたような状態になりますが、暫くすると画面が更新されます。
ベンチマークスクリプトがAndroidの場合にloop回数を考慮していなかったため修正し、再測定後のスコアに差し替えました。
Debug friendly な ON/OFF できるロールオーバー機能の実装
SubmitボタンのonClickイベントの流れを追いたいのに、Submitボタンに仕掛けられたロールオーバー機能(mouseover等)にデバッガの制御を奪われて、しかもその先がMinifyされたライブラリで、イラッとしたことはありませんか?
デバッグ中はロールオーバーを一括でOFFにして、あとで戻せればいいじゃない? と思い、そのような機能を作ってみました。
jsdo.it で試せます http://jsdo.it/uupaa/DebugFriendlyRollover
<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8" /> <title></title> <style> .rollover { outline: 5px solid green; } </style> <script src="src/uupaa.js"></script> <script> uu.ready(function(uu, doc) { uu.add([ uu.click(uu.button("rollover target button1"), fire), uu.click(uu.button("rollover target button2"), fire), uu.click(uu.button("rollover target button3"), fire), uu.click(uu.button("rollover target button4"), fire) ]); uu.rollover(uu.query("button"), "rollover"); }); function fire(evt) { alert("onclick"); } function handleClick(evt, off) { if (off) { uu.rollover.disable(); alert('ロールオーバーをOFFにしました') } else { uu.rollover.enable(); alert('ロールオーバーをONにしました') } } (function(uu) { // build struct uu.rollover = uu.mix(uurollover, { enable: uurolloverrebind.bind(null, true), // Function#bind(null, true) disable: uurolloverrebind.bind(null, false) // Function#bind(null, false) }); uu.unrollover = unrollover; // uu.rollover - bind rollover handler function uurollover(node, // @param NodeArray/Node: roll) { // @param ClassNameString/CallbackFunction: var db = uurollover._, ary = node.nodeType ? [node] : node, // toArray i = 0, iz = ary.length; for (; i < iz; ++i) { uu.event.hover(ary[i], roll, "rollover"); // hint("rollover") db.node.push(ary[i]); db.roll.push(roll); db.disable.push(false); } } // uu.unrollover - unbind rollover handler function unrollover(node) { // @param NodeArray/Node(= void): var db = uurollover._, ary = node.nodeType ? [node] : node, // toArray i = 0, iz = ary.length, pos; for (; i < iz; ++i) { pos = db.node.indexOf(ary[i]); if (pos >= 0) { uu.event.unhover(ary[i]); db.node.splice(pos, 1); // remove db.roll.splice(pos, 1); db.disable.splice(pos, 1); } } } // inner - uu.rollover.rebind function uurolloverrebind(enable) { // @param Boolean: true -> hover, false -> unhover var db = uurollover._, ary = db.node, i = 0, iz = ary.length, j, jz, fn = enable ? uu.event.hover : uu.event.unhover; for (; i < iz; ++i) { if (enable === db.disable[i]) { fn(ary[i], db.roll[i], "rollover"); db.disable[i] = !enable; } } } // db uurollover._ = { node: [], // Node: roll: [], // ClassNameString/CallbackFunction: disable: [] // Boolean: }; })(uu); </script> </head><body> <p>debug friendly な ON/OFF できるロールオーバー機能の実装</p> <input type="button" value="rollover off()" onclick="handleClick(event, true)" /> <input type="button" value="rollover on()" onclick="handleClick(event, false)" /> <hr /> </body></html>
rollover がデバッグのジャマになるときは、コンソールに
uu.rollover.disable()
とタイプしてください。
uu.rollover を使って仕掛けた mouseover ハンドラなどが一括で外れます。
uupaa.js 0.8 上では uu.rollover → uu.event.rollover となる予定です
。
地獄のJavaScript (Symbolic JavaScript)
jsdo.it に投稿してみた http://jsdo.it/uupaa/hellsjs
誰かうまいこと、a〜zをつくりだしてほしいんだよ。
uu.ready("window", function(uu, doc) { uu.mix(window, uu); uu.config.log.rollup = 1000; // logロールアップ回避 log('-1 = @', ~[] ); log('0 = @', +[] ); // or -[] log('1 = @', -~[] ); // or +!+[] log('2 = @', -~[] << -~[] ); // 1<<1 log('3 = @', -~[] + -~[] + -~[] ); // 1+1+1 log('4 = @', -~[] << -~[] + -~[] ); // 1<<1+1 log('5 = @', (-~[] << -~[] + -~[]) + -~[] ); // (1<<1+1)+1 log('6 = @', (-~[] + -~[] + -~[]) << -~[] ); // (1+1+1)<<1 log('7 = @', ((-~[] + -~[]) << (-~[] + -~[])) - -~[] ); // (1+1)<<(1+1)-1 log('8 = @', ((-~[] + -~[]) << (-~[] + -~[])) ); // (1+1)<<(1+1) log('9 = @', ((-~[] + -~[]) << (-~[] + -~[])) + -~[] ); // (1+1)<<(1+1)+1 log('-2147483648 = @', ~[] << ~[] ); log('2147483647 = @', ~(~[] << ~[]) ); log('true = @', !![] ); // or !+[] log('false = @', ![] ); // or !!+[] log('"t" = @', (!![]+"")[ +[] ] ); log('"r" = @', (!![]+"")[ -~[] ] ); log('"u" = @', (!![]+"")[ (-~[]) + (-~[]) ] ); log('"e" = @', (!![]+"")[ (-~[]) + (-~[]) + (-~[]) ] ); log('"f" = @', (![]+"")[ +[] ] ); log('"a" = @', (![]+"")[ -~[] ] ); log('"l" = @', (![]+"")[ (-~[]) + (-~[]) ] ); log('"s" = @', (![]+"")[ (-~[]) + (-~[]) + (-~[]) ] ); log('"e" = @', (![]+"")[ (-~[]) + (-~[]) + (-~[]) + (-~[]) ] ); });
jsのオレオレ演算子
jsのオレオレ演算子といえば、
- Boolean値に変換する !!arg
- 0又は1に変換する +!!arg
- 小数点を切り落とす arg|0 や arg >> 0
などがありますが、若干分かりづらいので、初心者の方にはおすすめできません。
http://jsdo.it/uupaa/9YFT で実行できます。
uu.ready(function(uu, doc) { var arg = 1.23; uu.log(" !!arg = @", !!arg); uu.log("+!!arg = @", +!!arg); uu.log(" arg|0 = @", arg|0); uu.log("arg>>0 = @", arg>>0); });
uu.ready("href:url/dispatcher", callback, ...) impl
条件成立でコールバックする関数を登録しておく機能 uu.ready() の条件に、ページのURLが一致する場合にコールバックする機能を追加しました。
このようにして利用します。同じものを http://jsdo.it/uupaa/2011-01-19 にあげてあります。
// jsdo.it のコードは <iframe> 内で動作するため、 // DOMContentLoaded の発火タイミングが <html> 内に設置した場合と // 異なります。ご注意ください。 <!DOCTYPE html><html lang="ja"><head><meta charset="utf-8" /> <script src="src/uupaa.js"></script> <script> uu.ready(function() { alert("DOMContentLoaded - this location.href = " + location.href); }); uu.ready("href:/http://jsdo.it/nguser", function(uu, doc) { // この関数は指定されたURLにマッチしないためコールバックされません alert("NG - http://jsdo.it/nguser..."); }, /* 条件を省略 */ function() { // この関数は指定されたURLにマッチしないためコールバックされません // uu.ready(callback, callback, ...)のように関数を連続して記述すると、 // 直前の条件の評価に基づいて関数を連続でコールバックします alert("NG - http://jsdo.it/nguser..."); }, "href:uupaa/2011-01-19", function(uu, doc) { // 最初にコールバックされます。DOMContentLoaded前に呼ばれる可能性があります alert("OK1 - uupaa/2011-01-19..."); }, "href:uupaa/2011-01-19", function(uu, doc) { // 2番目にコールバックされます。DOMContentLoaded前に呼ばれる可能性があります alert("OK2 - uupaa/2011-01-19..."); }, /* 条件を省略 */ function() { // 条件が省略されているため、直前の条件の評価に基づき、 // この関数は3番目にコールバックされます alert("OK3 - uupaa/2011-01-19..."); }, "dom", function(uu, doc) { // DOMContentLoaded のタイミングでコールバックされます。 alert("DOMReady(DOMContentLoaded)"); }, "window", function(uu, doc) { // window.onload のタイミングでコールバックされます。 alert("WindowReady(window.onload)"); }, "storage", function(uu, storage) { // WebStorage(または互換機能)が利用可能になったタイミングでコールバックされます。 alert("StorageReady"); }, "audio", function(uu, doc) { // <audio>(または互換機能)が利用可能になったタイミングでコールバックされます。 alert("AudioReady"); }, "canvas", function(uu) { // <canvas>(または互換機能)が利用可能になったタイミングでコールバックされます。 alert("CanvasReady"); }, "svg", function(uu) { // <svg>(または互換機能)が利用可能になったタイミングでコールバックされます。 alert("SVGReady"); }); </script></head><body></body></html>
クリスマスツリーを iPhone でも見れるようにしたよ
ジェバンニ仕事で、http://koebu.com/event/xmas/2010/ を iPhone / iPad に対応させました*1。
目からビーム出して頑張ってる最中のツイートまとめ
iOS では、ユーザアクションを伴わない、audio.play() やaudio.load() は機能しない。さらにpreloadとautoplay属性も無効化されている http://bit.ly/fmLf5y in Device-Specific Considerations
http://twitter.com/uupaa/status/15433471463784449
なんかね、PCブラウザのように、audio.play() 呼んでも audio.load() 呼んでも動かなくて、色々ググッたんだよね。
iOS でBGMは、そのままでは自動再生できないので、ユーザに画面をタッチしてもらう必要がある
http://twitter.com/uupaa/status/15433728247468035
この時点では、window.addEventListener("touchstart", function() {...}, false) でイベントかすめとって、その隙に色々やったらどうなんだろう? とかモヤモヤしてた
つまりiOSでは、なんとしても画面にタッチしてもらって、そのタッチイベントハンドラ内で、audio.load()を含めゴニョゴニョまとめてやってしまって、ある程度までオーディオデータの読み込みが進んだら(canplayイベントが発生したら)、play()で再生すればできるのかな?
http://twitter.com/uupaa/status/15434696645156864
いやいやいやいやいや… ソレ無理あるだろうと。この後思い直すわけですが。
クリスマスツリーをiPhone対応にするためにゴリゴリ書き直してる。
http://twitter.com/uupaa/status/15439252250697728
寂しさと空腹に負けてツイート
iOSの
ドーパミンドバ〜 した時のつぶやき
そもそも状態遷移が違う
クリスマスツリーのページを、PCブラウザ向けに組んだ時は、
- mp3ファイルの一覧をAPIで取得する
- 取得したmp3ファイルを裏で読み込み、再生可能な状態になったらプレゼントとして登場させる
- クリックで再生開始
といった流れで、状態を管理していたのですが、
Apple の技術資料 http://bit.ly/fmLf5y にも書かれているとおり、ユーザアクションを伴わない形で audio.play() や audio.load() を実行しても iOS では機能しないから、PC用サイトのUIを組む感覚でiPhone用サイト組むと全くダメなことが判明。
モバイルSafariで動かすために、
- mp3ファイルの一覧をAPIで取得する
- プレゼントとして登場させる
- クリックでaudio.load()を実行 canplayイベントを待ってaudio.play()で再生開始
という別の流れを作成する必要がありました。
また、AudioPlayerクラスをPCブラウザ用(XmasPlayer)とは別に用意(XmasMobilePlayer)することで、モバイルSafariでの音声の再生も可能になりました。
# uupaa.js にはOOP的なクラスを作る機能があります。
モッサリをサクサクにする(アニメーションを軽くする)
クリスマスツリーのページのプレゼントボックスは、uupaa.js の uu.fx() を使いアニメーションをさせています。
uu.fx() の本質的なコードは以下のように、style.left と style.top といったスタイル属性を定期的に書き換えるというものです。
function tick() { node.style.left = x + "px"; node.style.top = y + "px"; } setInteval(tick, 12);
ただ、left や top をタイマーで書き換える方式では、雪を4つ降らせただけでモッサリになってしまい、とても人前に出せる状態にはありませんでした。
そこで、前々からやりたかった機能(CSS3 Transition) を uu.fx() に組込み、機能強化を行いました*2。
http://code.google.com/p/uupaa-js/source/detail?r=994
uu.fx.moveIn(), uu.fx.shlink(), uu.fx.flare(), uu.fx.puff() で、モバイルWebKitなら WebKitTransform を利用するように
uu.fx(node, duration, { tx, ty }) を指定時に、モバイルWebKitなら WebKitTransform を利用するように
従来は、
- uu.fx(node, duration, { x:100, y:100 }) や
- uu.fx(node, duration, { left:100, top:100 }) で
アニメーションを作成しますが、
これからは、
- uu.fx(node, duration, { tx:100, tx:100 }) と
するだけで、uupaa.js が動作環境を判断し、PCブラウザなら tx → left と解釈、MobileSafari なら tx → WebKitTransform:transitionX と解釈するようになります。
つまり、
// これまで uu.fx(node, duration, uu.env.mobile ? { transitionX:100, transitionY:100 } : { left:100, top:100 }) ↓↓↓ // これから uu.fx(node, duration, { tx:100, tx:100 })
スッキリ!
あっそうそう
IE やモバイルSafariで見るとクリスマスツリーが短くて、その他のブラウザだと長かったり、複数の音声を同時に再生してガヤガヤできたりします。
あと、MobileSafariの<audio>は、ボリュームいじれないみたいなので、声が聞こえない時は iPod アプリを起動して音量大きくすると聞こえるようになります。
Boy Meets Girl !!