Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

Page 5/248: « 1 2 3 4 5 6 7 8 9 »

en-USでは定義されてて他のロケールで定義されてないエンティティ参照の有無を調べるスクリプトを書いてみた - Jul 09, 2009

機能を追加した時にen-USに追加したエンティティを他のロケールに追加し忘れて「○○語の環境で設定画面が動かねえぞゴラァ!!!」という風なバグ報告を貰うことがあまりに多いので(そして自分で気づけないので)、そういうミスを事前に防ぐためのスクリプトを書いてみた。

find_missing_entries_from_locales.js

コンテキストメニュー拡張などの、任意のスクリプトをXPConnect特権付きで実行できるツールでこのスクリプトを実行すると、以下のように動作する。はず。

  1. フォルダ選択のダイアログが開かれる。
  2. localeフォルダ、またはその上位の任意のフォルダを選択する。
  3. その中にある*.dtdなファイルがすべて読み込まれる。
  4. en-USロケールでは定義されていて他のロケールで定義されていない、というエンティティの一覧が表示される。

バイナリをBase64エンコードする - Jul 07, 2009

var file = Cc['@mozilla.org/file/local;1']
            .createInstance(Ci.nsILocalFile);
file.initWithPath('C:\\temp\\target.jpg');

var fileStream = Cc['@mozilla.org/network/file-input-stream;1']
                  .createInstance(Ci.nsIFileInputStream);
fileStream.init(file, 1, 0, false);
var binaryStream = Cc['@mozilla.org/binaryinputstream;1']
                    .createInstance(Ci.nsIBinaryInputStream);
binaryStream.setInputStream(fileStream);
var bytes = binaryStream.readBytes(fileStream.available());
binaryStream.close();
fileStream.close();

var base64 = btoa(bytes);

Bug 364586 - nsXmlRpcCLient.js conversion to base64 is slowで「JavaScriptでBase64エンコードするの遅いから組み込みの関数使えやゴラァ」と提出されていたパッチを見て知った。バイナリファイルの内容をバイト列の配列として読んでゴニョゴニョしなくても、単純に、バイナリのインプットストリームからreadBytes()でバイト列を文字列として取得してbtoa()にかければOK、だそうだ。試してみたら確かにちゃんといけた。

DOM周りの根っこの所に変更が入ったみたい - Jun 10, 2009

The Burning Edge見てたら、こんなバグがFIXEDになっていた。

text/htmlなHTMLドキュメントをXMLとして扱うにあたって、HTML5の仕様に合わせる形になるという事のようだ。namespaceURInullから"http://www.w3.org/1999/xhtml"へ、localNameがすべて大文字からすべて小文字へ、それぞれ変わる、と。

以前の挙動は以前の挙動で古い仕様には合致していたはずなので、時代の移り変わりをしみじみと感じる。

ローカルプロキシっぽいことをローカルプロキシを立てずにやろうとして挫折したことのまとめ - May 28, 2009

FireMobileSimulatorでのローカルプロキシ実装の試みを見て、UxUデバッグ用ローカルプロキシみたいな事をできるようにしてみたい、と思った。

ただ、HTTPのことはこれっぽっちも分からない。ソケット通信も、一応独自プロトコルっぽいものを使って別プロファイルで動作中のFirefoxからテスト結果を受け取るということはできるようになったけど、それ以上の事は分かってないまま。なので、まじめにローカルプロキシを立てる以外の方法で、「特定のURIにアクセスしようとした時だけ、あらかじめ定義しておいたルールに従って別のリソースを返す」ということをできるようにしてみようと考えた。

僕が現在把握している方法としては、以下の物がある。

  1. ローカルプロキシを実装して、その中でリダイレクトするやり方。
  2. http-on-modify-requestイベントのタイミングでリダイレクトするやり方。
  3. nsIContentPoilcyのshouldLoad()の中でリダイレクトするやり方。

1はthorikawaさんが頑張っておいでなので、それに期待している(ソースがGPL互換ならUxUにそのまま入れられるので)。他人のフンドシ。他力本願。ここでは後の2つについての挫折の経緯だけ書き留めておく。

結論を先に言っておくと、2も3も実装上の制限により全滅っぽい。やはり、ローカルプロキシをちゃんと実装するしか完全な解決策はないようだ。thorikawaさん期待age。

http-on-modify-requestイベントを使うやり方

これは、現在FireMobileSimulator等ですでに使われている。nsHttpChannelはリクエストを送信する前にnsIObserverServiceを通じてhttp-on-modify-requestイベントを、レスポンスが返ってきた後にhttp-on-examine-responseとかhttp-on-examine-merged-responseとかhttp-on-examine-cached-responseとかのイベントを通知する(SubjectはnsHttpChannel自身)ので、このタイミングでリダイレクトをしようという話。

リクエストし直す場合

FireMobileSimulatorの場合、nsHttpChannelの現在の通信をキャンセルした上で、nsHttpChannelからnsIWebNavigationを引っ張ってきてloadURI()で新しく通信を始めてる。

var redirected = 'http://www.example.com/';
var observer = {
  observe : function(aSubject, aTopic, aData) {
    if (aTopic == 'http-on-modify-request') {
    var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel)
                              .QueryInterface(Ci.nsIRequest);
    // ここでキャンセル
    httpChannel.cancel(Components.results.NS_ERROR_FAILURE);
    // リクエストし直し
    httpChannel.notificationCallbacks
      .getInterface(Ci.nsIWebNavigation)
      .loadURI(redirected, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
  }
};
Cc['@mozilla.org/observer-service;1']
  .getService(Ci.nsIObserverService)
  .addObserver(observer, 'http-on-modify-request', false);

通常のbrowserやiframeの場合はこれでいいんだけど、img要素やXMLHttpRequestからの通信では失敗する。具体的にはhttpChannel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)の時点でエラーになる。例えばXMLHttpRequestによる通信なら、httpChannel.notificationCallbacks.getInterface(Ci.nsIXMLHttpRequest)とすれば元のリクエストを取得できるので、こんな感じで場合に応じて再リクエストの方法を振り分けてやる必要がある。(XMLHttpRequestの場合については、どうやれば再リクエストできるのかまではたどり着けてない。もしかしたら無理なのかも。)

nsIURIのspecを書き換える場合

nsIChannelのURIプロパティはnsIURIインターフェースの読み取り専用プロパティなので、通信先のURIを書き換えることはできない……ように見える。でも実はnsIURIのspecプロパティの方は書き換え可能なので、ここに新しいURIを代入することででもリダイレクトできてしまう。

observe : function(aSubject, aTopic, aData) {
  if (aTopic == 'http-on-modify-request') {
  var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
  httpChannel.URI.spec = redirected;
}

とはいえこの方法は全くお勧めできない。動く場合もあるけど、動かない場合もある、という感じで実に不安定だ。nsHttpChannelの実装を見れば分かるけど、http-on-modify-requestが通知された段階ですでにその時のリクエスト先URIに基づいた初期化処理がいくつか終わってしまっているので、それと矛盾するURIを設定する(例えば、HTTPのリクエストだった物をFile URLにリダイレクトするとか)と、この後の内部処理でエラーが起こるっぽい。

レスポンスヘッダを書き換える

http-on-examine-responseの方ではnsIHttpChannelのsetResponseHeader()を利用できるので、LocationヘッダにURIを設定してみたんだけど、ダメだった。残念ながらヘッダを解釈してくれなかった。

nsHttpChannelの実装を見たら、HTTPのステータスコードが301とか302とかの時だけLocationヘッダを見るようになってた。ステータスコードを保持しているプロパティは読み取り専用なので、強制的にLocationヘッダを読ませるということはできそうにない。

nsIContentPoilcyのshouldLoad()を使うやり方

これはURNサポートなどで実際に使っている。詳しい方法は2007年当時のエントリに書いてあるけど、要約するとこういうことだ。

  1. nsIContentPolicyインターフェースを備えたXPCOMコンポーネントを作成して、
  2. カテゴリマネージャに登録しておき、
  3. 処理したいURIが渡ってきたら、元の通信の読み込みを中止させて、代わりに別のURIで通信を始める。

やってみると、一見上手く動いてくれてるように見えるんだけど、XPConnect特権がある実行コンテキストで作成したImageやnsIXMLHttpRequestのインスタンスからの通信が捕捉されなくて、UxUの用途では使えない感じだった。まあ仮に捕捉できたところで、元の通信を止めて別のURIで通信するようにさせる方法は分かってない(もしかしたら無いかもしれない)んだけど。

あと、shouldLoad()の第2引数はnsIURIインターフェースなのでこれのspecプロパティを書き換えたらどうだろう?と思ってやってみたけど、これもうまくいかなかった。browser要素等での読み込みの場合だと、nsDocShellのInternalLoad()でエラーが発生する。実装を見た感じでは、nsIContentPolicyに処理が渡ってくるより前に元のURIに基づいてセキュリティ関係の機能が初期化されてるので、その後でURIを書き換えたのがいけなかったんじゃないかと思う。

まとめ

とにかく、nsHttpChannelのインスタンスが作成された後からどうこうしようと思うのがそもそも手遅れくさい。それより前のステップでアクセス先のURIを書き換えようと思ったら、ローカルプロキシを立てる以外に手は無いようだ。

いいかげん諦めてMozilla Partyの発表資料作ることにします……

parseTemplate() の引数で渡すオブジェクトのプロパティをテンプレート内のコード片で普通の変数として参照したい - May 20, 2009

前書き

PHPとかerbのようなテンプレートをJavaScriptで。という話に書いたやつの続き。

大切な事なので3回言います。
<% for (var i = 0; i < 3; i++) { %>
  今日は<%= today %>です。
<% } %>
オーケー?

こういう文字列をテンプレートとして解釈したい場面は多分よくあると思う。この例だと、todayという変数をどっか外部から与えて値を埋め込むことになる。で、この変数をどうやって渡したらええねん、と。

解1:グローバル変数を使う

グローバル変数をがんがんに使って構わないのであれば、先にグローバル変数としてtodayを定義しておけばいい。eval()で実行されるコードの中からも普通に参照できる。

しかしアドオンのコードのように、個々のグローバル関数やグローバル変数の寿命が長い事が予想されるスクリプトでは、この手は使えない。

解2:テンプレート内に書くコード片にthis.を付ける

parseTemplate()の仕様としてはあくまで「書かれたコード片は第2引数で渡されたオブジェクトをthisとして実行されます」という風にしておいて、テンプレート風に書かれている方の文字列内のJavaScriptコード片では必ずthis.todayという風に書くようにする、という方法もある。

しかしいちいちthis.を付けるのは面倒だし、そもそも僕がこのような記法を知ったきっかけであるERBでは、self.なんて書く必要がなかった。同じような物は同じように使えた方がいい。なのでこの方法も使いたくない。

解3:varで宣言し直す

先のエントリに当初書いていたコードでは、parseTemplate()の第2引数で渡したオブジェクト(ハッシュ)のプロパティを走査してそれをvarで変数として宣言し直す、ということをしてた。外の名前空間をなるべく汚さないための苦肉の策だ。

  if (aContext && typeof aContext == 'object') {
    for (var prop in aContext) {
      if (!aContext.hasOwnProperty(prop)) continue;
      __parseTemplate__codes.unshift('var '+prop+' = aContext.'+prop+';');
    }
  }

こんな感じ。

でも、これだとエラーになりそうな場合がけっこう考えられる。例えばハッシュのキーが01234という文字列だと、実行されるJavaScriptのコードはvar 01234 = aContext.01234;となってしまい、eval()にかけた時点でSyntaxError: missing variable nameと怒られてしまう。

なので、こういうエラーの元になる名前のプロパティを除外して、安全な物だけをvarで宣言する、ということをやりたかった。言い換えると、そのプロパティの名前を構成している文字列がJavaScriptの識別子として妥当かどうかを判別したかった。

ということでやっと、このエントリの本題に入る。

続きを表示する ...

PHPとかerbのようなテンプレートをJavaScriptで。 - May 19, 2009

UxU 0.5.11に入れ損ねた。次版で標準のヘルパーメソッドに入れるけど、割といいかげんな実装で、たいした規模じゃないから、テストケースの中に直接書いて使ってもいいと思う。

function parseTemplate(aCode, aContext) {
  var __parseTemplate__codes = [];
  aCode.split('%>').forEach(function(aPart) {
    var strPart, codePart;
    [strPart, codePart] = aPart.split('<%');
    __parseTemplate__codes.push('__parseTemplate__results.push('+
                                strPart.toSource()+
                                ');');
    if (!codePart) return;
    if (codePart.charAt(0) == '=') {
      __parseTemplate__codes.push('__parseTemplate__results.push(('+
                                  codePart.substring(1)+
                                  ') || "");');
    }
    else {
      __parseTemplate__codes.push(codePart);
    }
  });
  var __parseTemplate__results = [];
  with(aContext|| {}) {
    eval('(function() { '+__parseTemplate__codes.join('\n')+' }).call(aContext|| {})');
  }
  return __parseTemplate__results.join('');
}

var source = <![CDATA[
      大切な事なので3回言います。
      <% for (var i = 0; i < 3; i++) { %>
        今日は<%= today %>です。
      <% } %>
      オーケー?
    ]]>.toString();
var params = {
      today : (new Date()).toString()
    };
var result = parseTemplate(source, params);

レガシーだけどクロスブラウザな書き方だったら、こうか。

function parseTemplate(aCode, aContext) {
  var __parseTemplate__codes = [];
  aCode = aCode.split('%>');
  var strPart, codePart;
  for (var i in aCode) {
    aCode[i] = aCode[i].split('<%');
    strPart = aCode[i][0];
    codePart = aCode[i].length == 1 ? null : aCode[i][1] ;
    __parseTemplate__codes.push('__parseTemplate__results.push(unescape("'+
                                escape(strPart)+
                                '"));');
    if (!codePart) continue;
    if (codePart.charAt(0) == '=') {
      __parseTemplate__codes.push('__parseTemplate__results.push(('+
                                  codePart.substring(1)+
                                  ') || "");');
    }
    else {
      __parseTemplate__codes.push(codePart);
    }
  }
  var __parseTemplate__results = [];
  with(aContext|| {}) {
    eval('(function() { '+__parseTemplate__codes.join('\n')+' }).call(aContext|| {})');
  }
  return __parseTemplate__results.join('');
}

var source = '大切な事なので3回言います。\n'+
             '<% for (var i = 0; i < 3; i++) { %>\n'+
             '  今日は<%= today %>です。\n'+
             '<% } %>\n'+
             'オーケー?';
var params = {
      today : (new Date()).toString()
    };
var result = parseTemplate(source, params);

他のアドオンと連携しやすくするためのライブラリを作ってみた - May 13, 2009

マルチプルタブハンドラについてRockridge氏ほかから「メニューをカスタマイズできるようにしてくれ」という要望が挙がっていたのだけれども、Menu Editorという素晴らしいアドオンがあるのに自前で同じような機能を再実装するのは徒労感しか無いなあと思ったので、開き直って、マルチプルタブハンドラの設定ダイアログを以下のようにしてみた。

  • Menu Editorがインストールされていなければ「Menu Editorをダウンロードする」リンクを表示する。
  • Menu Editorがインストールされていれば「Menu Editorの設定を開く」ボタンを表示する。

Menu EditorのAPIをよく分かってないので(ていうかそもそも公開APIなのかどうかも知らない)、タブ選択時のメニューをカスタマイズできるようにするためにちょっと強引な方法を使ってる。

で、同じようなこと(他のアドオンの有無を調べた上で設定を開く)を何度も書きたくなかったので、設定ダイアログに加えた変更の要点を他のアドオンと連携しやすくするためのライブラリとして分離してみた。

if (window['piro.sakura.ne.jp'].extensions.isInstalled('my.extension.id@example.com') &&
    window['piro.sakura.ne.jp'].extensions.isEnabled('my.extension.id@example.com'))
    window['piro.sakura.ne.jp'].extensions.goToOptions('my.extension.id@example.com');

アドオンがインストールされているかどうか・有効か無効かを調べるだけならFUELで事足りるので、あまり使い出がないといえば使い出がない。まあThunderbird 2あたりでだったらニーズがあるかもだけど。

ちなみにFUELで書く場合、アドオンがインストールされているかどうかはApplication.extensions.has('my.extension.id@example.com')、有効か無効かはApplication.extensions.get('my.extension.id@example.com').enabledで分かる。設定ダイアログのChrome URLを調べるAPIはなくて、それでこのライブラリを作ることにした次第です。

swapBrowsersAndCloseOtherの変更への追従と、メソッドの動的な書き換えのメリット・デメリット - Apr 30, 2009

以前書いたtabbrowser要素のswapBrowsersAndCloseOther()メソッドの上書きの話で書いてたサンプルが使えなくなっていたので直した。というか自作アドオンの機能を使おうとして正しく動かなかったのでコードを改めて見てみて、メソッドの書き換えの対象になってる箇所に変更があった事に気付いた(4月16日に変更が行われていたようだ)。

以下は、Shiretoko 3.5b5preに合わせて修正した後のバージョンのサンプルコード。

if ('swapBrowsersAndCloseOther' in aTabBrowser) {
  eval('aTabBrowser.swapBrowsersAndCloseOther = '+
    aTabBrowser.swapBrowsersAndCloseOther.toSource().replace(
      '{',
      '$& MyAddonService.destroyTab(aOurTab);'
    ).replace(
      'if (aOurTab == this.selectedTab) {this.updateCurrentBrowser(',
      'MyAddonService.initTab(aOurTab); $&'
    )
  );
}

if (aOurTab == this.selectedTab)を目印にして「タブを入れ換えた後の再初期化処理」を入れてたんだけど、同じif文がこれより前の位置に増えたせいで、コードの挿入位置がずれてしまい、本来期待していた順番とは異なる順番で処理が行われてしまっていた。if文の中まで目印に含めるようにして一応回避したけど、これはこれで、if文の中を書き換えるアドオンがあると破綻してしまう可能性があるわけで……何かいい方法はないものだろうか。

今回の場合に限らず、「メソッドの入れ替えではなく動的な書き換えを行う」やり方には、こういう変更に逐一追従しなくてはならないという弱点がある。メソッドのインターフェース(引数の数や返り値)はそうそう変わらなくても、中身は結構頻繁に変わる。なので、気をつけておかないといけない。

もう1つの弱点として、書き換え対象のメソッドの中で参照されている変数がそのメソッドが定義された変数スコープからしか参照できないものであった場合に、それへの対処も必要となる。その変数の値がグローバルな名前空間からアクセス可能なものであれば、メソッドの書き換え時にその変数を同時に宣言し直してやればいいんだけど……

(function() {
  var attrName = 'foo';
  window.ExampleAddonService = {
    method : function(aNode) {
      aNode.setAttribute(attrName, true);
    }
  };
})();

// メソッドの書き換えには成功するが、attrNameという変数が
// 見つからなくなってしまうので、実行時にエラーになる
eval(
  'window.ExampleAddonService.method = '+
  window.ExampleAddonService.method.toSource().replace(
    '{',
    '{ AnotherAddonService.preProcess(aNode);'
  )
);

この例のような場合になると、もうお手上げ。

それでも動的なメソッドの書き換えの方を僕が積極的に使う最大の理由は、同じやり方でメソッドを書き換える他のアドオンとの互換性を維持するためだ。別名で元のメソッドを保持しておくやり方の場合、他のアドオンが元のメソッドの名前で関数オブジェクトを取得しても、その関数の内容は元のメソッドとは全然違うから、書き換えられなくて動かなくなる、という事態が起こり得る。それを避けるためには、上の例のようなお手上げな事例を除き、可能な限り元のメソッドを動的に書き換えた方が良いということになる。

アニメーション効果を有効にしたツリー型タブの新版とデモ動画を公開したよ - Apr 09, 2009

Tree Style Tab 0.7.2009040901公開した。アニメーション効果の実装の他に、細かいバグ修正も色々。

あと、実際どんな感じかというのをわかりやすく示せるかなと思って、デモ動画も作ってみた。CamStudioもNiVEも使うの久しぶり(っていうかVistaにしてからは初)だから、やり方思い出すのに苦労したよ……

<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/M9dUfyoHz3E&hl=ja&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/M9dUfyoHz3E&hl=ja&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>

ヌルヌル動いてるのは倍速再生してるわけではなく、これで等倍速です(一応)。タブの影はbox-shadowで描画してるので、Firefox 3.0.xだと影無しになります。

しかしYouTubeはアップロードが簡単になってるわ画質が上がってるわで、いつの間にかすんげーパワーアップしてますね。Stage6とかあった頃とは隔世の感があります。

以下余談。

実装の最後の段階になって困った点として、画面外にタブが開かれた時にそこまで自動スクロールしてくれないという問題が起こってた。スクロールしても、新しく開かれたタブの一個前のタブの位置にスクロールしちゃったりして。上の動画で言うと、0:57あたりで画面下の方でサブツリーを展開した時に、子タブが画面内におさまるように自動でスクロールしてるけど、これがちゃんと動かなくなってた。

なんでこんな問題が起こってたかというと、「そのタブが画面外にあるのかどうかを判定する」「そのタブの位置までスクロールする」といった処理が全部「タブの座標」を基準にしてたせいで、アニメーション開始時点やアニメーション中の中途半端な座標を元に処理を行ってしまい、もうシッチャカメッチャカになってた、という……

上記の処理を行う時に、アニメーション中の座標とアニメーション終了後の座標とのズレをきちんと考慮して計算してやればいいってだけの話なんだけど、普通に考えるとこれがめんどくさい。それぞれのタブがちょっとずつズレて表示されてるわけだから、座標を調べたいタブだけじゃなくてそれより前(上)にあるタブ全部について、そのタブはアニメーション中か?とか、そのタブは非表示か?とかを判別しながらオフセット値を調べないといけないわけで……いちいちそんな計算するのはめんどくさすぎる。メンテナンスし辛そうだし、コードの量が多くなりそうだし、真面目に書く気しないです(大学生の頃だったらやってたかもだけどね……時間は有り余ってたから)。

で、代わりに以下のようなやり方を思いついた。

  • アニメーション開始時点やアニメーション実行中は、個々のタブの属性値として本来の座標からのオフセット値を持たせておく。
  • タブの座標が必要になった時には、「特定の条件にマッチするタブの当該属性値を収集して合計した値」をXPathで取得して、それを現在の座標に足してやる。

コードにするとこんな感じ。

getYOffsetOfTab : function(aTab)
{
  return document.evaluate(
    'sum((self::* | preceding-sibling::*[not(@tab-collapsed="true")])'+
         '/attribute::tab-y-offset)',
    aTab,
    null,
    XPathResult.NUMBER_TYPE,
    null
  ).numberValue;
},

sum()は、渡されたノードセットの値を数値として合計したものを返すXPathの関数。受け取る結果の型をXPathResult.NUMBER_TYPEと指定すれば、計算結果の数値を直接得られる。XPathはうまく使えばこんな風に、処理対象のノードの絞り込みだけじゃなくその後の処理(ここでは計算)まで一発で済ませられるので面白い。

アドオン用のミニマルなアニメーション実行用マネージャ - Apr 08, 2009

トゥイーンの話を受けてごろたんがさらに発展的な話を書いてくれた。で、「ほうほう、こういうのをeasingと言うのか……」と自分の無知っぷり&何も知らんくせに偉そうなことを語る厚顔さに恥じ入りながら先のエントリを少し手直しした。

で、JSTweener という Tweener (as3 のモーショントィーンライブラリ) 互換のライブラリを使うと、標準で様々な easing 関数が利用できたり、タイマーが一つなので、数百, 数千オブジェクトをモーションさせるときにはだいぶ高速になる。というのを読んで、確かに先のエントリに書いた物はいっこいっこのタブごとにタイマー走らせるから効率悪いよなあ、と思い、1個のタイマーだけで複数のアニメーションを走らせるための簡単なライブラリを作ってみた。ツリー型タブの開発版にも早速組み込んでる。

  • window['piro.sakura.ne.jp'].animationManager.addTask( )にアニメーション用の関数その他の引数を渡してやると、それを共通のタイマーで処理する、というごく単純なもの。
  • easing関数を使いやすいように、アニメーション用関数にはeasing関数が受け取るのと同じ形式の引数が渡る。
  • アニメーションを途中で止めたい時はwindow['piro.sakura.ne.jp'].animationManager.removeTask( )で関数の登録を解除してやる。

クロージャ使って書くこと前提の不親切なAPIですんません……

Page 5/248: « 1 2 3 4 5 6 7 8 9 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき