jQuery 画面のリサイズ 判定オブジェクト
jQueryをつかったブラウザ画面サイズ変更を追従する便利オブジェクトを作成します。
所要時間は、3~4時間です。
■利用方法
ブラウザがリサイズされた場合、コールバック関数を起動させます。
var [変数] = new iResizer([コールバック関数],…);
(使用例)
var cbFun = function(){….}
var __resize = new iResizer(cbFun);
■サポート関数
[変数].setDelay(n)
遅延判定時間(msec)を設定
[変数].getDelay(n)
遅延判定時間(msec)を取得
[変数].start()
リサイザー判定開始(宣言時はすでに起動)
[変数].stop()
リサイザー判定開始
最初はブラウザ画面リサイズは、javascroptでどのように取得するかを確認したのち、順を追って、リサイザー汎用モジュールとして仕上げていきます。
その後、順を追ってライブラリを仕上げていきます。
1. javascript標準のリサイズ取得方法
ブラウザのリサイズ取得は、windowのリサイズ取得関数を利用します。実際に、検証用サンプルをみてみます。
図1.javascript標準のrisizeの動き
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script> $(function(){ $(window).resize(function(){ $("#log").append("<p>[" + (new Date())+"] ウインドウサイズ W:" + $(window).width() + " H:"+ $(window).width() + "</p>"); }); }) </script> <title>Fine Frontier - Lesson - jQuery - Practice - Resizer </title> </head> <body> <div> 画面をリサイズしてください。下部にリサイズメッセージが表示されます。 <div id="log"></div> </div> </body> </html> |
実際に、このソースを実行すると、リサイズログがいっぱい流れることがわかります。
マシンスペックによりますが、ブラウザ画面をわずかにリサイズするだけで、risizeメッセージを20回/秒程度受け取っています。
このrisizeメッセージのタイミングで、画面のレイアウトを計算しなおす方法は、実用には向いていません。
実用フォームを、画面サイズにあうように拡縮する場合、20回/秒で画面を書き換えは非現実的です。
仮にできたとしても、画面がチラつき使い物にならないでしょう。
やはり、一通りのリサイズが落ち着いたら、画面書き換えを1回で済ませられないものかと考えさせられます。
2. jQueryでタイマーを使った画面リサイズの単純メッセージ受け取り処理
リサイズメッセージを受け取り、実際に画面書き換え処理をするまでを遅らせるということに、タイマーを使う案が浮かびます。
早速、リサイズの扱いにタイマー処理を入れた検証をしてみましょう。
なお、完成形として外部javascriptのプロトタイプのライブラリ化を目指すため、ここから、プロトタイプ化していきます。
2.1 タイマーを使ってリサイズ制御(カウンター編1)
タイマーを使い画面リサイズから遅れて、リサイズ判定用の確認ログを出力する検証をしてみます。
javascript関数を、その役割で二つの関数に分けます。
windowリサイズメッセージを受け取る部分と、リサイズ判定シミュレーションとしてのログ出力する部分です。
図2のコンソールログをにあるよう、main側とsub側で2種類用意しました。
main関数は、画面リサイズを受け、ログ出力をします。タイマーで0.5秒遅れて、sub関数を呼び出します。
sub関数は、リサイズ判定シミュレーションログを出力しています。
カウンターはmain側でカウントアップ、sub側でカウントダウンさせます。
図2.タイマーを作ってリサイズ制御(カウンター編1)Web画面とログ(Chromeの場合)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script src='iResizer02.js'></script> <script> $(function(){ var __resize = new iResizer(); }) </script> <title>Fine Frontier - Lesson - jQuery - Practice - Resizer </title> <title>リサイズモジュール</title> </head> <body> <div> ブラウザのコンソールログを確かめながら、画面をリサイズしてください。 </div> </body> </html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
var iResizer = function(f){ "use strict"; this._d=500; // Delay Time (msec) this._c=0; // Counter this.main(); return this; }; iResizer.prototype = { sub:function(){ "use strict"; this._c--; console.log(" [sub] :"+this._c); }, main:function(){ "use strict"; var v = this; $(window).resize(function(){ v._c++; console.log("[main] :"+v._c); window.setTimeout(function(){return v.sub();}, v._d); }); return this; } }; |
属性
判定遅延時間 初期値 0.5秒(3行目)
カウンター(4行目)
プロトタイプ関数
メイン(main)関数
ブラウザリサイズで、ログ出力後、sub関数をタイマー起動します。
サブ(sub)関数
カウンター値を減らし、ログ出力します。
画面リサイズに伴い、main側でカウンター値が0からカウントアップし始めます。
カウントアップから遅れること0.5秒後にサブsub側でカウントダウンします。
カウンターは、属性として定義しているため、main側、sub側から、共通アクセスできます。
画面リサイズが停止すると、カウント値も0に戻りログ出力を停止させます。
このサンプルでカウントダウンログ出力値が0に戻ったら、リサイズが停止したと判断できないかと検証です。
検証の考察の前に、もうワンステップ、ライブラリ化を進めます。
次に、0.5秒間隔で何度もタイマーを起動繰り返している点を、SetIntervalで0.5行間隔で繰り返しタイマーに変更してみます。
SetTimeout
指定された遅延時間(msec)後に、指定された関数を1回実行します。
2.2 タイマーを使ってリサイズ制御(カウンター編2)
ブラウザ画面がリサイズされ場合、その時点でリサイズ終了判定処理(sub)が実行されていないのであれば、タイマーを使い 0.5秒間隔でリサイズ終了判定処理を繰り返し起動します。
リサイズ判定が継続中であれば、タイマーを2重起動することなく、単純にカウントアップのみ続けます。
また、リサイズ終了判定処理(sub)は、カウントダウンを行い、カウント値が0に戻れば、一通りのリサイズが終わったということ判定します。
この手法で、画面再配置処理ができるか検証してみます。
図3.タイマーを使った画面リサイズの扱い例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script src='iResizer03.js'></script> <script> $(function(){ var __resize = new iResizer(); }) </script> <title>Fine Frontier - Lesson - jQuery - Practice - Resizer </title> </head> <body> <div> ブラウザのコンソールログを確かめながら、画面をリサイズしてください。 <p id="log"></p> </div> </body> </html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
var iResizer = function(f){ "use strict"; this._d = 500; // Delay Time (msec) this._c = 0; // Counter this._i = 0; // Timer ID this.main(); return this; }; iResizer.prototype = { sub:function(){ "use strict"; console.log(" [sub] :"+this._c); if(this._c===0){ clearInterval(this._i); console.log("[Stop Timer] ***** Call Some Function *****"); this._i = 0; }else{ this._c--; } }, main:function(){ "use strict"; var v = this; $(window).resize(function(){ console.log("[main] :"+v._c); if(v._c===0){ v._c++; console.log("[Start Timer]"); v._i = window.setInterval(function(){return v.sub();}, v._d); }else{ v._c++; } }); return this; } }; |
■属性
判定遅延時間 初期値 0.5秒(3行目)
カウンター(4行目)
タイマーハンドラーID(5行目)
■プロトタイプ関数
メイン(main)関数
ブラウザリサイズで、カンター値でsub関数起動中でなければ、
ログ出力後、sub関数をタイマー起動します。
sub関数起動中であれば、
カウンター値をアップします。
サブ(sub)関数
カウンター値を減らし、ログ出力します。
カウンター値が0になったら、タイマーを停止します。
画面2のログは、実際にサンプルの画面をわずかに動かした場合に発生したログの例です。
カウンター値は、初期値0から、メイン側でカウントアップして、サブ側でカウントダウンする様子がわかります。
カウント値0になると、タイマーをクリアして、ログを表示しています。
一見すると、「画面リサイズを監視して、リサイズが停止したらリサイズ処理を行う」を実現できたようです。
しかしならがら、この検証ソースも、まだ不十分な点があります。画面リサイズを、10秒ほど続けてみてください。
リサイズが停止したという判定が、実際の停止から数十秒後に判定になります。
理由は、画面リサイズが行われた場合、メイン側で数msec間隔でカウントアップされるに対し、サブ側では、0.5秒間隔でカウントダウンされるためです。
具体的に説明すると、1秒足らずでカウント値を20にできるが、カウントダウンに10秒かかるためです。
タイマー間隔を0.5秒から0.1秒にすれば、多少よくなるでしょうが、根本的な解決になりません。
カウンターを使ったリサイズ判定は、うまくいかないことがわかりました。
setInterval補足説明
この関数が定義された時点で即時に、内部関数を初回起動します。戻り値のハンドルは、初回の内部関数実行後に、値セットされます。
以後、タイマー停止されるまで、一定間隔で内部関数を実行します。
setIntervalは、clearIntervalで停止します。
内部関数は、正確(*)に記述しないと動作しない場合があります。
* 正確な関数記述について
上記javascrpt(29行目)を、setInterval(v.sub()); の記述で動作しない場合があります。
2.3 タイマーを使ってリサイズ制御(フラグ編)
前記のサンプル カンター手法では、画面リサイズの停止判定が、実際の画面停止よりかなり遅れる不都合がありました。
今回は、この判定遅れを解決するため、同じタイマーを使いますが、今回はフラグを使ったリサイズ終了判定(画面リサイズ中・リサイズ終わりの区別)をします。
図4.画面のリサイズ判定をタイマーを使って制御する方法(フラグ編)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script src='iResizer04.js'></script> <script> $(function(){ var __resize = new iResizer(); }) </script> <title>Fine Frontier - Lesson - jQuery - Practice - Resizer </title> </head> <body> <div> ブラウザのコンソールログを確かめながら、画面をリサイズしてください。 <p id="log"></p> </div> </body> </html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
var iResizer = function(){ "use strict"; this._d = 500; // Delay Time (msec) this._c = 0; // Continue status (0:stop/1:continue) this._c2= 0; // Continue status (Prev Status) this._i = 0; // Timer ID this.main(); return this; }; iResizer.prototype = { sub:function(){ "use strict"; console.log(" Waiting....."); if(this._c2===0){ this._c = 0; window.clearInterval(this._i); console.log("[Stop Timer ("+this._i+")] ***** Call Some Function *****"); this._i = 0; } this._c2 = 0; }, main:function(){ "use strict"; var v = this; $(window).resize(function(){ console.log("Resizing....."); if(v._c===0){ v._c2 = v._c = 1; v._i = window.setInterval(function(){return v.sub();}, v._d); console.log(" [Start Timer] TimerID:("+v._i+")"); }else{ v._c2 = 1; } }); return this; } }; |
■属性
_d:判定遅延時間 初期値 0.5秒(3行目)
_c:リサイズ継続フラグ(4行目)
_c2:リサイズ継続フラグ前回バックアップ値(5行目)
_i:タイマーハンドラーID(6行目)
■プロトタイプ関数
メイン(main)関数
ブラウザリサイズで、それまでリサイズが停止状態であれば、sub関数を定期間隔で起動します。
同時に、_c, _c2ともオン状態にする。
既にリサイズ中であれば、_c2値をオン状態にする。
サブ(sub)関数
_c2値がオフ状態になれば、リサイズ停止判定とし、ログを出力します。
_c値をオフ状態にします。
最後に_c2を0にします。
実際に検証してみてください。
リサイズを開始して、リサイズ中は画面書き換えの判定に至りません。
リサイズを停止して、0.5秒後に、リサイズ判定が1回行われます。
ゆっくりとブラウザをリサイズしたとしても、素早くリサイズしても、どちらもリサイズ判定ができます。
これで、今回のライブラリの目的である、リサイズ判定を望み通り取得できました。
この次から、ライブラリの仕上げに移ります。
3. リサイズ判定のコールバック
前述までライブラリ作成過程で、リサイズの判定ができました。
次に、このリサイズ判定のタイミングで、実際に使う画面を書き換える処理を実現します。
書き換える処理は、ライブラリを利用する側それぞれなので、当然、ライブラリが直接画面書き換えをすることはありません。
そこで、登場するのがコールバックの仕組みです。
まず画面の書き換え処理は、利用する側でjavascriptの関数の形で用意します。
ライブラリは、リサイズが終えたタイミングで、用意した画面書き換え関数をコールバックします。
今回のサンプルでは、画面書き換えの検証用として、「リサイズされました。」メッセージ表示する関数を用意しました。
ライブラリから、その用意した関数をキックする仕組について説明します。
図5.画面のリサイズ判定からのコールバック検証
図6.画面のリサイズ判定からの複数のコールバック検証
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script src='iResizer05.js'></script> <script> $(function(){ var cbFun = function(){ $("#log").append("<p>[" + (new Date())+"] ウインドウサイズ W:" + $(window).width() + " H:"+ $(window).width() + "</p>"); } var __resize = new iResizer(cbFun); }) </script> <title>Fine Frontier - Lesson - jQuery - Practice - Resizer </title> </head> <body> <div> 画面をリサイズしてください。リサイズ後に関数呼び出しされていることを確認できます。 <p id="log"></p> </div> </body> </html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script src='iResizer05.js'></script> <script> $(function(){ var cbFun = function(){ $("#log").html($("#log").html()+"<br/>リサイズされました。"+(new Date())); } var cbFun2 = function(){ $("#log2").html($("#log2").html()+"<br/>リサイズされました。"+(new Date())); }
var __resize = new iResizer(cbFun, cbFun2); }) </script> <title>Fine Frontier - Lesson - jQuery - Practice - Resizer </title> </head> <body> <div> 画面をリサイズしてください。リサイズ後に二つの関数呼び出しされていることを確認できます。 <p id="log" style="background:#fcc;"></p> <p id="log2" style="background:#cfc;"></p> </div> </body> </html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
var iResizer = function(){ "use strict"; this._f_= []; // CallBackFunctions this._d = 500; // Delay Time (msec) this._c = 0; // Continue status (0:stop/1:continue) this._c2= 0; // Continue status (Prev) this._i = 0; // Timer ID console.log("<<args:"+ arguments.length + ">>"); for(var i=0; i<arguments.length; i++){ this.setCallbackFun(arguments[i]); } this.main(); return this; };
iResizer.prototype = { sub:function(){ "use strict"; console.log(" Waiting....."); if(this._c2===0){ this._c = 0; window.clearInterval(this._i); console.log("[Stop Timer ("+this._i+")] ***** Call Function *****"); this._i = 0; for(var i=0; i<this._f_.length; i++){ (this._f_[i])(); } } this._c2 = 0; }, main:function(){ "use strict"; var v = this; $(window).resize(function(){ console.log("Resizing....."); if(v._c===0){ v._c2 = v._c = 1; v._i = window.setInterval(function(){return v.sub();}, v._d); console.log(" [Start Timer] TimerID:("+v._i+")"); }else{ v._c2 = 1; } }); return this; }, setCallbackFun:function(f){ "use strict"; if(f===null || typeof f!=="function"){return this;} var i, j; j = this._f_.length; for(i=0; i<j; i++){ if(this._f_[i]===f){break;} } if(i==j){ this._f_[i] = f; } return this; } }; |
■属性
_f_:コールバック関数配列(3行目)
複数の画面書き換えコールバック関数に対応します。
_d:判定遅延時間 初期値 0.5秒(4行目)
_c:リサイズ継続フラグ(5行目)
_c2:リサイズ継続フラグ前回バックアップ値(6行目)
_i:タイマーハンドラーID(7行目)
■プロトタイプ関数
メイン(main)関数
ブラウザリサイズで、それまでリサイズが停止状態であれば、sub関数を定期間隔で起動します。
同時に、_c, _c2ともオン状態にする。
既にリサイズ中であれば、_c2値をオン状態にする。
サブ(sub)関数
_c2値がオフ状態になれば、リサイズ停止判定とし、ログを出力します。
_c値をオフ状態にします。
最後に_c2を0にします。
コールバック設定(setCallbackFun)関数
setCallbackFun:コールバック関数ポインタを配列変数に格納する関数(46-58行目)
利用者側から直接setCallbackFun関数を呼び出すことはありません。
ライブラリのコンストラクタ時の引数を分解して、初期設定で外部関数ポインタを配列にいれています。(9-11行目)
リサイズがあり、リサイズが終了と判定された時点で、配列保存されているコールバック関数ポインタを利用して外部関数を呼び出します。(26行目)
setCallbackFunの機能
まず、引き渡しパラメタが、functionであるかチェックします。(48行目)
次に、既に登録済関数とダブりがないかチェックします。(51-53行目)
新たなコールバックであると判断された場合(54行目)
関数ポインタ配列に登録します。(55行目)
実際に検証してみてください。
リサイズを開始して、リサイズ中は画面書き換えの判定に至りません。
リサイズを停止して、0.2秒後に、リサイズ判定が1回行われます。
リサイズ判定は、利用者側にコールバックの形で返ることが検証されました。
これで、今回の目的である、リサイズ判定を望み通り取得できました。
4. リサイザライブラリ 補助関数追加
最後に、以下の補足関数を追加して完成とします。
遅延時間[msec]のgetter,setter関数
リサイザーの機能起動・停止関数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="description" content="HTML,javascript,jQuery,css"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="http://fine-frontier.com/javascript/jquery.js"></script> <script src="http://fine-frontier.com/javascript/iResizer_full.js"></script> <script> $(function(){ var cbFun = function(){ $("#log").html($("#log").html()+"<br/>リサイズされました。"+(new Date())); } var __resize = new iResizer(cbFun); }) </script> <title>リサイズとは</title> </head> <body> <div> ブラウザのコンソールログを確かめながら、画面をリサイズしてください。 <p id="log"></p> </div> </body> </html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
var iResizer = function(){ "use strict"; this._f_=[]; // CallBackFunctions this._d = 200; // Delay Time (msec) this._c = 0; // Continue status (0:stop/1:continue) this._c2= 0; // Continue status (Prev) this._i = 0; // Timer ID this._x = 0; // Hold status for(var i=0; i<arguments.length; i++){ this.setCallbackFun(arguments[i]); } this.m(); return this; };
iResizer.prototype = { s:function(){ "use strict"; if(this._c2===0){ this._c = 0; window.clearInterval(this._i); this._i = 0; for(var i=0; i<this._f_.length; i++){ (this._f_[i])(); } } this._c2 = 0; }, m:function(){ "use strict"; if(this._x===0){ var v = this; $(window).resize(function(){ if(v._c===0){ v._c2 = v._c = 1; v._i = window.setInterval(function(){return v.s();}, v._d); }else{ v._c2 = 1; } }); } return this; }, setDelay:function(d){ "use strict"; d = d-0; if(d<10){ this._d_ = 10; }else if(d>10000){ this._d_ = 10000; }else{ this._d_ = d; } return this; }, getDelay:function(d){ "use strict"; return this._d_; }, setCallbackFun:function(f){ "use strict"; if(f===null || typeof f!=="function"){return this;} var i, j; j = this._f_.length; for(i=0; i<j; i++){ if(this._f_[i]===f){break;} } if(i==j){ this._f_[i] = f; } return this; }, start:function(){ this._x = 0; return this.m(); }, stop:function(){ this._x = 1; if(this._i!==0){ window.clearInterval(this._i); this._i=this._c=this._c2=0; } return this; } }; |
この値は、setter(46行目)、getter(56行目)関数を利用してライブラリ利用者側から設定・取得できます。
遅延時間は、10msec~10secの範囲になります。
このリサイザーはコンストラクト時からリサイズ判定の動作を開始します。
ライブラリ利用者側から、停止・起動するために、start(73行目)、stop(77行目)の関数を用意してます。
追加された4関数は単純なのでソースレベルで確認できるでしょう。
さらに、今まで多くの説明したメイン・サブ関数は、実際の利用者側が知る必要はない為、関数名をm,sに変更しています。
いかがでしたか、リサイザーオブジェクトは実用性があるのでどこでも利用できます。
ソースは、公開しているため、安心して使っていただけることでしょう。
次回、パネルオブジェクトのテーマで、このリサイザーをつかいますので、そちらも参考にしてください。
更新日付:2017/08/06 05:33:55