スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

jQuery.Tween.propHooks でアニメーションをカスタマイズ

jQuery 1.8 からjQuery.Tween.propHooksという オブジェクトが存在します。 これを利用すると、 指定したプロパティの値の取得・設定の処理を アニメーション中に限って変更することができます。 (※jQuery.propHooksとは違うので注意して下さい)

主に jQuery 2.0.2 を使って調べたので、他のバージョンでは 当てはまらないことがあるかもしれませんのでご注意下さい。 この機能は現時点で公式にはドキュメント化されていませんので、 将来告知無しで変更されるかもしれません。

jQuery.Tween.propHooksとは

jQuery.Tween.propHooks (以下propHooks)は、 アニメーション処理の実行中に 対象要素の対象プロパティの値を取得・設定する処理を担うフック を保持するオブジェクトです。 アニメーション処理での対象プロパティへのアクセス処理の ほぼ全てをpropHooksから取得したフックの関数が行います。

アニメーション中、.animate()で指定した プロパティにアクセスする際、 指定したプロパティ(キャメルケース化済み)に対応するフックを propHooksから探して使用します。 対応するフックが見つからなかったら jQuery が用意するデフォルトのフックが用いられます。

jQuery.cssHooksでも同じようなことができますが、 jQuery.cssHooksと違ってpropHooksは アニメーション処理中のみに適用範囲が絞られます。

propHooksにフックを登録すれば、 アニメーションでのプロパティの取得・設定処理を自由に 定義することができます。 jQuery 内部では、IE の scrollTop, scrollLeft への設定に対して propHooksを利用しています。

リストに戻る

フックの追加方法

フックはプロパティ名ごとに登録します。 フックを登録する関数等は用意されていないようです。 取得用関数、設定用関数を保持したオブジェクト(以下 フックオブジェクトと呼称)を propHooksに登録します。 取得用関数は get プロパティに、設定用関数は set プロパティに 設定します。

// フックを登録する
jQuery.Tween.propHooks["プロパティ名"] = {
  get: function( tween ){ // 取得用関数
    // 数値を返すようにすること
  },
  set: function( tween ){ // 設定用関数
    // 返り値に制約はない
  }
}

引数には取得用・設定用関数のどちらも Tween オブジェクトが 渡されます。 コンテキスト(this)はフックオブジェクトです。 Tween オブジェクトの elem プロパティに対象要素が 入っていますので、それを利用してプロパティの値を 取得したり設定したりします。

取得用関数の返り値は数値として扱われ処理されるので 数値を返すようにします。 設定用関数の返り値は特に利用されてないようなので 返り値は設定しなくても大丈夫です。

取得・設定の両方の関数を登録する必要はなく、 取得・設定いずれかだけの登録でも大丈夫です。 既存のプロパティ名だけでなく 架空のプロパティ名でも登録できます。

リストに戻る

フックの削除方法

削除用関数は用意されていないようなので手動で削除します。 falseを返すような値にすれば大丈夫なようです。

// フックを削除する
delete jQuery.Tween.propHooks.プロパティ名
// もしくは
jQuery.Tween.propHooks["プロパティ名"] = false; // null 等でも大丈夫
リストに戻る

使用する際の注意点

propHooksに独自のプロパティを設定して使う場合は、 その値に "show","hide","toggle" を指定するのは避けましょう。 アニメーション処理での対象プロパティへのアクセス処理の ほぼ全てをpropHooksのフックが行うと前述しましたが、 これらの文字列値を処理する過程ではフックを使用しておらず うまく動かない場合があります。

リストに戻る

使用例

実用性がありませんが、既存のプロパティを短縮指定できるように してみます。

// 既存の CSS プロパティの別名を作る
jQuery.Tween.propHooks["ml"] = { // marginLeft の短縮
  get: function( tween ){
    return parseFloat( jQuery.css( tween.elem, "marginLeft" ) );
  },
  set: function( tween ){
    jQuery.style( tween.elem, "marginLeft",
                  tween.now + tween.unit );
  }
};

// cssHooks と違って .css() に影響しない
// (element は DOM 要素とする)
console.log( $( element ).css("marginLeft") ); // 数値
console.log( $( element ).css("ml") );         // undefined

// propHooks に登録したプロパティは .animate() で使用できる
$( element )
  .animate({ ml: "+=100px" })    // 相対値も指定可能
    .animate({ ml: "-=100px" })
      .animate({ ml: "hide" })
        .animate({ ml: "show" }); // ← 動かない

propHooksに登録したプロパティは .animate()で使用できます。 目的値として相対値も指定できます。 "hide"などの文字列値はうまく動かない場合があります。

次に、color プロパティの RGBA 値を操作する独自プロパティを 作ってみます。色々と妥協していますのでご注意下さい。

// color の RGBA を操作するプロパティを作成
// jquery.color プラグインから色々拝借しています
// color の値が常に rgba( ... ) の形式で返すと想定
// (#... の形式や transparent 等の文字列値は想定せず)

// rgba( ... ) をそれぞれの成分に分解する正規表現
var re = /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/;

// rgb(a)( ... ) をそれぞれの成分の配列に分解
function parse( str ){
  var match  = re.exec( str.toLowerCase() );
  if( match ){
    return [ match[ 1 ],
             match[ 2 ],
             match[ 3 ],
             match[ 4 ]   ];
  }
}

// 成分の配列から rgb(a)( ... ) の文字列表現に変換
function toRgbaString( values ){
  var prefix = "rgba(",
  rgba = jQuery.map( values, function( v, i ) {
    return v == null ? ( i > 2 ? 1 : 0 ) : v;
  });
  if ( rgba[ 3 ] === 1 ) {
    rgba.pop();
    prefix = "rgb(";
  }
  return prefix + rgba.join() + ")";
}

// red から alpha までのプロパティのフックを定義
jQuery.each({
  red:   0,
  green: 1,
  blue:  2,
  alpha: 3
}, function( seibun, index ){
  jQuery.Tween.propHooks[ seibun ] = {
    get: function( tween ){
      var rgba = parse( jQuery.css( tween.elem, "color" ) );
      if( rgba ){
        var val = +rgba[ index ];
        // alpha 値が未定義の場合は 1 を返す
        return (seibun == "alpha" && isNaN( val ) )? 1: val;
      }
      return 0;
    },
    set: function( tween ){
      var rgba = parse( jQuery.css( tween.elem, "color" ) );
      if( rgba ){
        // alpha 値は浮動小数、その他は整数値で設定する
        rgba[ index ] =
          (seibun != "alpha")? parseInt( tween.now ): tween.now;
        jQuery.style( tween.elem, "color", toRgbaString( rgba ) );
      }
    }
  };
});

// 登録したプロパティは .animate() で指定可能
$( element ).animate({ red: "255",
                       green: "255",
                       blue: "255" }, 1000)                         
  .animate({ alpha: 0.5 }, 1000)
    .animate({ red: 0 }, 1000)
      .animate({ green: 0 }, 1000)
        .animate({ blue: 0 }, 1000)
          .animate({ alpha: 1 }, 1000);

上のように個別にプロパティを登録した後、 jQuery.cssHooksの expand プロパティ(これも ドキュメント化されていません)の機能を利用すれば、 登録したプロパティそれぞれを一括で .animate()に指定できるようになります。 上の例で言えば .animate()で rgb( ... ) の形で 指定できるようになります。

expand を定義すると、そのプロパティを.animate()に 指定した際、目的値を複数のプロパティと値に分解することができます。 jQuery の内部では、margin プロパティで使用されています (margin を.animate()に指定すると、目的値が marginTop, marginRight, marginBottom, marginLeft に 分解されます)。

// red から alpha を登録した後
// それぞれのプロパティを一括で指定できるようにする
jQuery.cssHooks["mycolor"] = {
  expand: function( value ) { // value には目的値が入る
    var i = 0,
    expanded = {},
    parts = typeof value === "string"? parse( value ): [],
    len = parts.length,
    seibun = ["red", "green", "blue", "alpha"];

    // 分解したプロパティと値を持つオブジェクトを作成して返す
    for( ; i < len; i++){
      if( isNaN( +parts[i] ) ){ break; } 
      expanded[ seibun[i] ] = parts[i];
    }
    return expanded;
  }
};

// expand を利用すると一括指定ができる
// 例えば下の mycolor: "rgb(200, 30, 30)" は
// red: "200", green: "30", blue: "30" と指定したのと同意になる
$( element ).animate({ mycolor: "rgb(200, 30, 30)" }, 1000)
  .animate({ alpha: 0.1 }, 1000)
    .animate({ mycolor: "rgba(0, 0, 0, 1)" }, 1000);

これはあくまで例として簡単に書いたものですので、 color プロパティのアニメーションを導入する場合は自前で書くか jQuery Color プラグイン 等を使うようにして下さい。

リストに戻る

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。