05 | 2017/06 |  07

  1. 無料サーバー

User forum-FC2BLOG-Info-Edit Template-Post-Edit-Upload-LogOut

CSSやJavascript自習の苦闘史を綴っていきたい。恐縮ですがJavascriptを有効にしてご覧ください。
2005年12月から社会問題も掲載!

スポンサーサイト

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

 

jquery.js のアニメーションコードの活用 ( 2 ) アニメを使ってグラフを動的に描く

アニメーションを使ってグラフを描く

アニメーション活用エントリイの 2 つ目は、アニメーションを活用してグラフを書くコンテンツです。

そもそも、easing 関数のグラフ化を考えていたのですが、エクセルなどで作って貼り付けるのは余りにも芸がないと思われ、何とか Web サイト上で動的にグラフが描けないだろうか、と前から思っていました。

そして jquery.js のアニメーション解読を進めていく内に、easing 関数それ自体に興味を持ったことと併せて、アニメーションの options.step メソッドを使えば、アニメ途中の情報を取得できるのでグラフが書ける───入浴中にふと(苦笑)、そのことに気付きました。

構想ニ 1 日間、コーディングに約 2 日を要して( と言っても勤めながらなので、夜と朝だけのマイタイムですが )、何とか具体的な成果に漕ぎ着けました。

以下がその結果ですが、何はともあれ、アニメ継続時間を 2 秒か 4 秒程度にし、適当な easing 関数を選択してから、グラフ作成ボタンをクリックしてみてください。

僅か 3 クリックで、今選択した継続時間を要して青いボックスが左下に閉じられ、その後また同じ継続時間を使って右上方向に開かれます。これで「 グラフの座標 」が完成します。

そしてボックスが開き終わると、数10ミリ~数100ミリ秒後にそのボックス内に、今適用された easing 関数のグラフが描かれます。このときボックス下辺はグラフ横軸となって継続時間の推移割合を示し、、ボックス左辺は縦軸となり easing 関数値を表しています。

なお、グラフの大きさは相対的な問題に過ぎません、以下の例示の場合には、青いボックスの大きさ(つまりグラフ座標)を、レイアウト上の必要性から偶々 350 px 角としました。その結果X軸、Y軸両方向とも、時間推移割合と関数値を 350 倍してグラフを作成しています。

【注意】
IE では関数値が 0 未満または 1 を越える easing 関数の場合、固まってしまうようです。ですから Back、Elastic などは IE ではグラフ描画は出来ませんが、もし固まった場合には、この頁をリロードすれば、Javascript の 「 凝固状態 」 を解除出来ます。

  1. まず選択した easing 関数の効果を、ボックス隠蔽 / 表示の 2 つのアニメーションを通じて確認してください。
  2. ボックスが開き終わると直ぐに、今選択し、ボックスの開閉に利用した easing 関数のグラフが、開き終わったボックス内に描かれます。
アニメ継続時間
適用 easing

▲ToTop

グラフ描画の仕組み

jquery.js における easing 関数の扱い
easing 関数とは

「 easing 」 は速度を増減するときに使うもの、とされています。( イージング (Easing) とは | FLASH関連用語集 | ミツエーリンクス )。つまり easing 関数は動きを加速し、あるいは減速する加速度の役割をもっています。

そして、横軸に時間を、縦軸に変動値を取って「 動き 」をグラフ化した場合において、それが直線であれば等速で動いたことを表し、直線の角度は速度の高低を意味します。そして直線でない曲線部分は、加速度が働いた部分であり、上に凸ならば減速、下に凸ならば加速となります。

さて、easing 関数は Tween クラスの場合も jquery.js の場合も、基本的に次の 4 つの変数により変化する二次元関数です。初期値(b)、変動幅(c)、アニメの継続時間(d)、そして現在までの経過時間(t)です。つまり、中学校時代に倣った馴染みの関数式 y = f (x) になぞらえれば、easing 関数の基本形式は次のようになります。

変動後の値 = f ( 初期値, 変動幅, 経過時間, 継続時間 )

ここにおいて時間と共に変化する変数はただ 1 つ 経過時間であり、後は全て定数値となります。勿論、変動幅を時間の関数にすれば、それも変化することになりますが、その場合であっても変動幅は経過時間の関数であり、結局経過時間が唯一の変数となります。

jquery.js の easing 関数

jquery.js では変動幅( c : change で表される場合が多い )を正規化しています。つまり 0 から始まり 1 で終わるように引数が設定されています(jquery.js ver1.32 #4139)。これにより関数はいわば純化され、事の本質が分かりやすくなっていますし、そのことが easing 関数への理解を深める一助ともなっていると思います。

jquery.js における easing 関数の詳細(plugin 30種類を含む)

jquery.js に含まれている easing 関数は linear と swing の 2 類だけですが、それに30種類を追加するプラグイン ( George Smith 氏による 30 種類の easing 関数 ) があります。

エントリイを改めて、これらの 32 種類の easing 関数の全貌を、昔習った数学の関数式を思い出しながら解明しようと思っています。

▲ToTop

どのように関数値を取得するか

options.step メソッドを使います。

私が今自宅で使用しているパソコンでは、 Firefox でこの頁を開いて計測してみたところ、jquery.js の内部メソッドである e.step メソッドは、1 つのアニメ対象要素の 1 つのアニメ対象プロパティ当たり、1 秒間に約 80 回実行されます。(IE の場合、IE 8 であっても回数はもっと少なくなります。Javascript 実行速度が遅いためです。)

このそれぞれの瞬間毎に、 animate メソッドの引数として指定する options.step メソッドを利用して、( jquery.js の内部で作成される ) e インスタンスに保持されているアニメ諸情報を取得します。

easing 関数値は e.pos プロパティに保持されていますので、これを取りだして配列に格納し、その後その配列の各要素を使って、e.step が間歇的に取得した情報から、取得しなかった中間値を補間する計算を行わせて、空隙部を埋めます。

こうして横軸の全てのピクセル数 350 個に対応する関数値を取得又は推定し、グラフ化します。

グラフは 1 × 1 dot の点を 順に青いボックス内に append することにより描きます。

詳細はコード解説部分で触れます。

options.step メソッドで取得できない座標値の補間方法

「 無視できるほど小さい部分では曲線は直線と見なすことが出来る 」という微分の考え方に拠りました。決して複雑ではなく、つまりは単なる直線補間です(苦笑)。

当然アニメ継続時間を長くすればするほど、stepメソッドの呼び出し回数が増えるので、補間値が減り、step メソッドによる算出値が増え、グラフは正確さを増します。

このことは継続時間を長短変えてグラフを描かせてみると良く分かります。継続時間が短いとグラフは直線部分が増え、長くすると直線が減っていきます。これは計算により算出した補間値の数が減るためです。

この件に関する詳細もコード解説部分で触れます。

コーディングの苦労話

ここで行ったことは、ボックスアニメーションを引き起こしつつ、そのボックス内にそのアニメに使用した easing 関数のグラフを書かせるという複合的なアニメーションです。

それ故に、コーディングではグラフを描かせるタイミングについて、幾つかテストしながら調整しました。辿り着いたのは、options.step メソッドの最中でもなく、完全にアニメが終わってからでもなく、step メソッドによる情報取得が終わった段階で直ぐに補間値算出を始めさせ( この段階ではアニメーション動作は進行中です )、かつその補間値計算が終わったら直ぐにグラフ描画を始めさせる、という併行的なコード進行でした。

補間値計算やグラフのプロット作業がアニメーションの進行に干渉せず、かつアニメ完了後出来るだけ短時間でグラフ描画を行わせる最適な方法を探った結果こうなりました。

次に、2 つのバージョンを作った経緯を書いておきたいと思います。

最初に作成したのは以下に掲げた Ver 1 です。このコードは良く見渡すと、drawGraph メソッドが呼ばれる度毎に、アニメ対象の高さ・幅を計算し、隠蔽/表示用アニメ CSS を作り、duration・easing・complete を算出し、step メソッドを登録しています。これはいかにも無駄です。

そこで二度目以降の drawGraph メソッド呼び出し時には、初回呼び出し時に算出した各値を可能な限り再利用するようなコード進行に変更すべきだと思い、Ver 2 を作りました。

▲ToTop

プラグイン drawGraph() メソッドコード
■プラグイン drawGraph() メソッドコード
// ▼ Ver 2 : コード進行の効率化を考慮した改訂版
 1:(function($){
 2:$.fn.drawGraph = function(d,e,fn){
 3: var $box=this, o={}, cnt=0, allsteps=0,layout=[], dotAry=[], arrgAry=[];
   // メソッド実行後も o オブジェクトのプロパティ値を保持させるために、
   // drawGraph メソッドの opts プロパティを作る。
 4: if (!arguments.callee.opts) arguments.callee.opts={};
   // drawGraph メソッドの opts プロパティがあれば、o オブジェクトに併合する。
   // これにより 2 度目以降の drawGraph 呼び出し時に o オブジェクトに opts が複写される。
   // その opts は、直前の drawGraph メソッド実行時に 6 ~ 19 行で作成された o オブジェクトが
   // 20 行において複写されたものなので、2 度目以降の drawGraph 呼び出し時には、
   // 直前実行時に作成したプロパティが o オブジェクトに格納される。
 5: else $.extend(o, arguments.callee.opts);
   // box プロパティがないか(初回呼び出し時)、登録されているノードが異なる場合
   //(今回の使い方では呼び出し元は常に同一なので、ノードが異なることはないが
   // 一般化するために敢えて後者の条件を書いておく。)
 6: if (!o.box || o.box[0] !== $box[0] ) {
 7:  o.box = $box; // this への参照を登録する
 8:  o.log = $box.next(); // log 表示用ノードへの参照を獲得する。
 9:  o.h = $box.height(); // アニメ対象ノードの高さを測定する。
10:  o.w = $box.width(); // アニメ対象ノードの幅を測定する。
11:  o.hideCSS = {height:"toggle",width:"toggle",top:"+="+o.h+"px"}; // 隠蔽アニメ用CSS
12:  o.showCSS = {height:"toggle",width:"toggle",top:"-="+o.h+"px"}; // 表示アニメ用CSS
13: }
   // duration プロパティがないか、あっても引数と異なる場合にのみ 引数 d を o.duration に登録する。
14: if (!o.duration || o.duration !== d) o.duration = d;
   // easing プロパティがないか、あっても引数と異なる場合にのみ 引数 e を o.easing に登録する。
15: if (!o.easing || o.easing !== e) typeof o.duration ==="object" ? "" : o.easing = e;
   // fn が存在して関数で complete プロパティがないか、complete が fnと異なる場合
   // fn を o.complete に代入し、その他の場合には何もしない関数を代入する。
16: o.complete = typeof o.duration ==="object" ? "" :
17:  fn && $.isFunction(fn) && (!o.complete || o.complete!==fn) ? fn : function(){};
   // stepObj は毎回作成しないと doStep が初期化されない。
18: o.stepObj = typeof o.duration ==="object" ? $.extend({},o.duration,{step:doStep}) :
19:  {duration:o.duration, easing:o.easing, complete:o.complete, step:doStep};
   // 以上迄で作成された o オブジェクトを opts に複写し、2 度目以降の drawGraph
   // メソッド起動時に o オブジェクトの複写元とする。
20: $.extend(arguments.callee.opts, o);
21:
22: function doStep(){ // step メソッドを使ってグラフの横軸値と縦軸値を取得する
23:  var e = arguments[1]; ++allsteps; // e.options.step の第 2 引数である e オブジェクトを代入
24:  if (e.prop!=="height") return; // height プロパティ以外の時には何もしない。
25:  ++cnt ;
26:  layout[cnt]={};
27:  layout[cnt]["top"] = ( 1-e.pos ) * o.h; // グラフの縦軸値( easing 関数値 )
28:  layout[cnt]["left"] = e.state * o.w; // グラフの横軸値( 時間推移率 )
29:  dotAry.push(layout[cnt]); // layout 配列の要素を dptAry 配列に格納する。
    // 時間推移率が 100 %以上になったら直線補間関数を呼び出す。
30:  if (e.state >= 1) arrangeGraph();
31: }
32: function arrangeGraph(){ // 直線補間関数
33:  var len = dotAry.length;
34:  while (len){
     // dotAry の先頭要素を削除して arrgAry 配列に格納
35:   arrgAry.push(dotAry.shift());len--;
     // dotary 配列の次の要素のleft値と、格納されたばかりの dotstack 配列要素の left 値の差をとり
36:   if (!len) break;
37:   var diffLeft = Math.round(dotAry[0].left) - Math.round(arrgAry[arrgAry.length-1].left);
     // 差がなければ同一値と見なして dotary 配列の先頭要素を削除
38:   if (diffLeft == 0) {dotAry.shift();len--;}
     // 差が 1 ならば次の値なので dotAry 配列の先頭要素を削除し、それを arrgAry 配列に追加する
39:   else if (diffLeft == 1) {arrgAry.push(dotAry.shift());len--;}
40:   else {// 差が 2 以上あれば、補完値算出用に、その差で top 値を割って平均値を出す
41:    var avr = (dotAry[0].top - arrgAry[arrgAry.length-1].top) / diffLeft;
42:    for (var j=1, obj={}; j<diffLeft ;j++){
43:     // 差の数-1 回だけ補完値を算出し、arrgAry 配列に追加する。
44:     obj ={
45:      top: arrgAry[arrgAry.length-1].top + avr, 
46:      left: ++arrgAry[arrgAry.length-1].left
47:     };
48:     arrgAry.push (obj);
49:    }
50:   }
51:  }
52:  plotGraph();
53: }
54: function plotGraph(){ // ボックス内にグラフを描く
55:  $box.append("<div id='selectedEasingName' style='position:absolute;top:5px;left:15px;'>"+ o.easing +"</div>");
56:  o.log.html("height prop の step 起動回数 :"+ cnt +"<br />全ての step 起動回数:" + allsteps);
57:  for (var i=0, len = arrgAry.length; i<len; i++){
58:   $("<div class='dot hack' />").css({top:arrgAry[i].top+"px",left:arrgAry[i].left+"px"}).appendTo($box);
59:  }
60: }
61:
62: $box.empty(); o.log.empty(); // 表示されている easing 名と log を消す。
   // 隠蔽及び表示アニメを引き起こす。表示アニメの時にグラフも描く。
63: return $box.animate(o.hideCSS,o.duration,o.easing ).animate(o.showCSS, o.stepObj);
64:};
65:})(jQuery);
// ▲ Ver 2 終わり

// ▼ Ver 1 :サイズ測定と o オブジェクト作成を毎回行っている非効率版
(function($){
$.fn.drawGraph=function(duration,easing,fn){
 var cnt=0, allsteps=0, $box=this, layout=[], dotAry=[], arrgAry=[],
   o = $.extend({},{duration:duration, easing:easing});
 $box.h=$box.height(); $box.w=$box.width(); $log=$box.parent().next();
 var hideCSS =  {width:"toggle",height:"toggle",top:"+="+$box.h+"px"},
   showCSS =  {width:"toggle",height:"toggle",top:"-="+$box.h+"px"};
 o.step = doStep;
 o.complete = function(){$.isFunction(fn) ? fn() :function(){} };

 function doStep(){
  var e = arguments[1]; ++allsteps;
  if (e.prop!=="height") return;
  ++cnt ;
  layout[cnt]={};
  layout[cnt]["top"] = ( 1-e.pos ) * $box.h;
  layout[cnt]["left"] = e.state * $box.w;
  dotAry.push(layout[cnt]);
  if (e.state >= 1) arrangeGraph();
 }

 function arrangeGraph(){
  var len = dotAry.length;
  while (len){
   // dotAryの先頭要素を削除して arrgAry 配列に格納
   arrgAry.push(dotAry.shift());len--;
   // dotary 配列の次の要素の left 値と、格納されたばかりの dotstack 配列要素の left 値の差をとり
   if (!len) break;
   var diffLeft = Math.round(dotAry[0].left) - Math.round(arrgAry[arrgAry.length-1].left);
   // 差がなければ同一値と見なして dotary 配列の先頭要素を削除
   if (diffLeft == 0) {dotAry.shift();len--;}
   // 差が 1 ならば次の値なので dotAry 配列の先頭要素を削除し、それを arrgAry 配列に追加する
   else if (diffLeft == 1) {arrgAry.push(dotAry.shift());len--;}
   else {// 差が 2 以上あれば、補完値算出用に、その差で top 値を割って平均値を出す
    var avr = (dotAry[0].top - arrgAry[arrgAry.length-1].top) / diffLeft;
    for (var j=1, obj={}; j<diffLeft ;j++){
     // 差の数 - 1 回だけ補完値を算出し、arrgAry 配列に追加する。
     obj ={
      top: arrgAry[arrgAry.length-1].top + avr, 
      left: ++arrgAry[arrgAry.length-1].left
     };
     arrgAry.push (obj);
    }
   }
  }
  plotGraph();
 }

 function plotGraph(){
  $box.append("<div id='selectedEasingName' style='position:absolute;top:5px;left:15px;'>"+ o.easing +"</div>");
  $log.html("height prop の step 起動回数 :"+cnt+"<br />全ての step 起動回数:" + allsteps);
  for (var i=0, len = arrgAry.length; i<len; i++){
   $("<div class='dot' />").css({top:arrgAry[i].top+"px",left:arrgAry[i].left+"px"}).appendTo($box);
  }
 }

 $box.empty();$log.empty();
 return $box.animate(hideCSS,duration,easing ).animate(showCSS, o);
};})(jQuery);
// ▲ Ver 1 終わり

// drawGraph プラグインを使って実際にグラフを書かせるためのコード
(function($){
 var selDuration=2000,selEasing="swing";
 // アニメ継続時間コンボから 選択された duration を取得
 $("#sel1_728").change(function(){selDuration = +$(this).val();});
 // easing リストから 選択された easing 関数を取得
 $("#sel2_728").change(function(){selEasing = $(this).val();});
 $("#btn1_728").click(function(){$("#box1_728").drawGraph(selDuration,selEasing);});

 $("input").mouseup(function(){$(this).css({backgroundColor:""}).blur();})
  .hover(function(){$(this).css({backgroundColor:"pink"});}
  ,function(){
    $(this).css({backgroundColor:""});
  })
  .mousedown(function(){$(this).css({backgroundColor:"lightgreen"})});
})(jQuery);

▲ToTop

drawGraph() メソッド Ver 2 に関する補足説明

直前起動時に作成したアニメ対象に関する諸情報を出来る限り 2 度目以降に活用する───これが Ver 2 作成の動機であり目的です。『 Javascript 第 5 版 』p.144 を参考にして、メソッドのプロパティを活用する方法を作ったまでは良かったのですが、なかなか順調に動きませんでした。18 行目の stepObj も他のプロパティと同様に初回作成時の値を使うコードを書いたのですが、これが躓きの原因でした。

14~17 の 4 行では、o.duration、o.easing、o.complete の各プロパティの存在を確認し、それがあれば何もしないようなコードを書きました。こうしてコード進行の効率化を図ったのでした。

そしてそれ自体は問題なく動いたので、安心して 18 行も if (o.stepObj) 文を書いてしまったのです。

ところが、こうすると 2 度目以降においては o.stepObj は再定義されないので、初回時の値をそのまま保持し続けてしまいます。その結果 3 行目の cnt = 0 が 2 度目以降に読み込まれ初期化されても、その後の過程で opts プロパティに o オブジェクトを複写・待避させているため、o.stepObj 内の cnt は初期化されず累積されてしまうのです。

そのことが分かるまでに数時間を要したのですが、大きな落とし穴に落ち込んでしまった結果、貴重な経験を重ねることが出来ました。

▲ToTop

 

■ コメントの投稿 ■

管理者にだけ表示を許可する

●トラックバック●

■トラックバックURLはこちら■
http://hkom.blog1.fc2.com/tb.php/728-8a446a10

●参照元一覧●

<provided Fc2>
<provided i2i>

▲ToTop

 90%近いシェアを握っているインターネットエクスプローラの描画エンジンを利用したタブbrowser。沢山のタブbrowserがあるが、多機能、カスタマイズフリー、スクリプト利用等で一日の長がある。Gekkoエンジンへの対応も行われ、IEからの自立独立の方向に向かっている。2005年7月にはIE7が登場する見通しの中で、今後の発展が望まれる。

 多様なCSS作成支援機能を備えた、タグ入力式 HTML&CSS作成支援エディタ。スキンデザインもすっきりしている。テキストエディター上で作成するよりも確実で安全にタグ打ちが出来る。
文字コードを選べないのが欠点。

 StyleNote同様のタグ入力式 HTML&CSS 作成支援エディタ。長年使用してきたが現在StyleNoteに乗り換えつつある。

 クリップボード履歴情報を活用する為のソフト。画像まで履歴を取ってくれるのが嬉しい。このソフトを使わない日は絶対ない程に重宝し、愛用している。

 起動中のウィンドウの「コピーできない」説明文などの文字列を取得し、コピー可能な文字データにするツール。何かと便利。

 ストリーミングデータを保存することが出来るソフト。動画利用には不可欠なソフトだ。

 無料ながらレイヤー機能を有し、スクリプトによる拡張も可能な、sleipnir作者が提供している優れもの画像編集ソフト。

 画面キャプチャソフトと言えばこれに勝るものなし、ではないだろうか? 様々な取得方法を有しており、ブログ作成にもHomepage作成に不可欠だ。Jtrimと並んでWoodyBellsの作品。

 複数ファイルの同時編集は出来ないが、透過pngも作れる画像編集ソフト。
(以下当該サイトから抜粋)初心者にも簡単に操作が出来るフォトレタッチソフトです。多くの加工機能で画像に様々な効果を与えることができます。非常に軽快に動作するため、ストレスなく操作できます。

 Animation Gifファイルを作れる無料ソフト。

 キャプチャソフト。画面内にサイト全体が表示しきれない場合でも、これを使えば全体をキャプチャすることが出来る。

 画像処理。画像のフォーマット変換のみならず、色数やサイズ、圧縮率の変更まで一括処理できてしまう『BatchGOO!』は、大量の画像をまとめて処理したいときに大変便利なソフト。BMP, TIFF, JPEG, PCX, PNG の相互変換をはじめ、色数・サイズ・解像度の統一、JPEG圧縮率の調節など、ホームページ用の画像や携帯電話用の壁紙を揃えるのに抜群の相性を見せる。(Vectorの当該ソフト紹介頁より抜粋引用)

 名前から直ぐに想像が付くように画像のサイズを測るためのソフトだ。Homepage作成には欠かせない。2カラム、3カラムのレイアウトを行う場合に大変重宝する。

 ランチャーソフトは沢山あるが、中でもこれが一押しだ。2年以上使ってきたがその操作性には毎日満足している。これを使い始めてからデスクトップには一切のアイコンを表示することをやめてしまった。

 AdobeReader7によって、起動時間が長すぎるという長年のユーザーの不満はある程度解消した。そのためこの高速化ソフトは存在価値が低下してしまったかもしれない。AdobeReader6迄はこのソフトによる起動高速化で恩恵を受けてきた。

 IE専用が難点だが、様々なサイト内でIDやパスワードを入力するのに重宝するソフト。コンテキストメニューから簡単に起動できるのがGood! sleipnir等のIEの描画エンジンを利用しているブラウザでも使える。

 利用しているパソコンの諸元値を取得するには、このソフトがベストだ。インストール済みソフトの一覧が取得できるのも嬉しい。

 WMPは機能が豊富なだけ重い。RealPlayerも同様だ。そこでMedia Player Classicを使いたい。動作が軽快なだけではなく、対応しているファイル形式もすこぶる多く、これひとつで、wmvもrmも表示できてしまうのだから凄い! 数多あるMedia Playerの王様と言えるだろう。

 自宅でPCを起動しているときには必ず起動しているメディアプレーヤー。何かと過剰なWinampよりも、起動も速くスキンはシンプルだ。

 DivX, Xvid, Mov, Vob, Mpeg, Mpeg4, avi, wmv, dv, などの動画をDVD-Video形式に変換できるフリーソフト。クリックするとDVD関連ソフト紹介サイト=「DVDなToolたち」なるHomepageが開きます。

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