D0409161 ブラウザのように伸縮するテキスト・スクロールバーのつくり方?
Name ほたる
Date 2004年09月16日 (木) 08時32分
Message 憲ちゃんさんはじめまして、ほたると申します。
このサイトを知ってから毎日新しい発見の連続で、うれしくてうれしくて寝不足の日々が続いています(笑い)
それでこの前からテキストスクロールに取り組んでいてようやく動くようになったのですが、さらに欲がでて、スクロールの必要の無い時はボタンもスライダーも無くしたいと思っています。
いろいろ自分なりに考えてみたのですがどうしても上手くいきません。
何かヒントだけでもご教授いただければと思い投稿させていただきました。
お忙しいと思いますがよろしくお願いします。
あ、それとできましたら、ブラウザのスクロールバーのように文字の量に応じてバーの長さも変わるようにすることなんてことはできますでしょうか?
Response 01
2004年09月16日 (木) 23時32分> 憲ちゃん 
ほたるさん、はじめまして♪


profile2.fla profile2.txt

とりあえずサンプルだけ作ってみました!!
これを、どのようにご説明してよいやら、これから考えます(^^;
Response 02
2004年09月17日 (金) 07時58分> ほたる 
うわ〜すごい!!
しかもこんなに早く!!!
憲ちゃんさんありがとうございます。
早速ダウンロードさせていただきました。
これからじっくり中身を見当させていただきます。
ほんとにありがとうございました。
お忙しいと思いますができましたら解説もどうかよろしくお願いします。
Response 03
2004年09月17日 (金) 22時33分> 憲ちゃん   
まず、上下ボタンやスライダーを見せるか見せないかの処理ですが・・・

”スクロールの必要がないとき”というのはテキストボックスに文字が表示された段階で得られる、
「テキストボックス変数名.maxscroll(スクロールの最大値)」が「1」のときですから・・・
最初にこれを調べて「1」のとき表示しない、そうでないとき表示するようにすればいいですね。
表示・非表示の方法は「MCインスタンス名._visibletrue(表示)又はfalse(非表示)」という
プロパティを使って簡単にできます。
ボタンとスライダーをまとめてひとつのMCに入れておけば、そのMCインスタンス名を指定して、
一度に表示・非表示させることができます。
したがって、サンプルの構造は・・・



図の細かい内容はあとにして・・・
このように「スクロールセット」MCの中に、「スライダー」MCと上下ボタンが入った状態にします。
「スクロールセット」MCはさらにその上の階層の「テキストMC」MCの中にあって、そこに、
外部テキストの内容を表示するテキストボックス(変数名”message※”)があります。
ここでは「スライダーMC(インスタンス名”slider”)」にスクリプト記載するとして、

  theMaxScroll = _parent._parent.message.maxscroll;変数theMaxSceollにS最大値を代入※※
  if (theMaxScroll == 1) { スクロールの最大値が「1」(スクロール無し)なら
    _parent._visible = false;→ひとつ上の階層(=スクロールセットMC)を非表示
  } else {    そうでないなら
    _parent._visible = true;→ひとつ上の階層(=スクロールセットMC)を表示
  }

※このサンプルはFlash5バージョンのスクリプトで記載しますから、”変数名message”は、
「A」プロパテーパネルの「変数」欄に記載し、「インスタンス名」欄は空欄のままです。
※※「スライダー」MCからみて、テキストボックスは2つ上の階層のMCにありますから、
 ひとつ上の階層を指す相対パス「_parent」を2つ並べて記載します。

次に、スクロールの最大値に応じて、スクロールMCの長さ(高さ)を変化させる仕組みですが…

以下、過去記事の■外部テキストをスクロール表示(行単位)をご理解いただいているとして、
ご説明します。

まず、座標計算を簡単にするため「スライダー」MC(中にDrag「ボタン」)の基準点を左上にして、
「スクロールセット」MCの座標の原点(基準点)に合わせて配置します。
「スライダー」MCがムービー上を移動できる最大幅をサンプルでは140pxとして、
ステージに描く大きさは、その幅一杯に描きます。このままでは上にも下にも動けない状態ですが、
スクロールの最大値の大きさに応じて、「スクロール」MCの高さを小さくしていくようにすれば、
その分下の隙間が広がって移動できる範囲が大きくなります。(上図参照)
それには、前回過去記事のときのように、1スクロール当たりの移動量をスクロールの最大値から
逆算して定める方法ではダメで、最初に移動量を”固定”しておく必要があります。
その値を「slideMin」とし、「スライダー」MCがムービー上を上下に移動できる最大幅を「slideY」、
その結果、縮小拡大される「スクロール」MCの仮の高さを「theHeight」とすると・・・

   theHeight = slideY - slideMin * theMaxScroll;←@

という式であらわすことができます。
そして「theHeight」が上下に移動できる範囲を「slideH」とすると、

   slideH = slideY - theHeight;又は、slideH = slideMin * theMaxScroll;←A

となります。この値は、上図でわかるように縮小拡大後の「スライダー」MCが、
ドラッグ移動する範囲の下のY座標になります。
そこで、「スライダー」MCの中の「スライダーB」ボタンに・・・

 on (press) {
   this.startDrag(false,0,0,0,slideH);
 }
 on (release, releaseOutside, rollOut) {
   this.stopDrag();
 }

矩形の「下」の座標を変数で指定しておきます。
ここで、1スクロール当たりの固定移動量の実際の値をどれくらいに設定するかですが・・・
サンプルでは 5px としています。「slideY」が 140px ですから上記@の式ににあてはめると、
「theMaxScroll」の値が「28」で「スライダー」MCの仮の高さ「theHeight」が 0px、高さなし。
「28」を超えると、マイナスの値になってしまいます。
そこで、どんな場合でも、少なくとも1スクロール当たりの移動量分だけは高さを確保するため、
実際の「スライダーMC(自分)」の高さである「this._height」の値を・・・

   theHeight = slideY - slideMin * theMaxScroll;
   if (theHeight >= 0) { theHeight が正の値なら
     this._height = theHeight+slideMin; ←slideMin分を加えて、自分の高さとする
   } else {      負の値なら
     this._height = slideMin; ←slideMinをそのまま自分の高さとするB
   }

実際の「スライダー」MCの高さを「theHeight」から「this._height」と変更しましたので、
ドラッグ移動する範囲「slideH」の式も・・・

   slideH = slideY - this._height;←C

と変更しておきます。
では、結果が負の(サンプルではスクロールの最大値theMaxScrollが「28」を超えた)場合の、
1スクロール当たりの移動量とはどういうものでしょうか?
theMaxScrollがさらに大きくなっていっても、BとCの式からみて、
それに応じてスライダーの移動範囲「slideH」の値は固定されたまま変わりませんね。
「slideH」が固定し、maxscrollの値が変化する・・・ということは、
考えてみると、勉強してこられた過去記事と同じ状態です(*^.^*)
つまり結果が負の場合、1スクロール当たりの移動量は、固定ではなく、過去記事の式・・・

   slideM = slideH / theMaxScroll;←D

負になったときは、これをそのまま適用すると上手く行くということです。
ここで…正の場合の「slideH」は、Aの右の式で「slideMin * theMaxScroll」でしたから、
Dの式の「slideH」に代入すると・・・

   slideM = slideMin * theMaxScroll / theMaxScroll;
       = slideMin;

「slideH」が固定でも変動でも「slideM」と「slideMin」は同じものであることがわかるので、
Dの式をそのまま正の場合にも適用できることが証明でたというわけです♪

そして、ドラッグ移動した「スライダー」MCのY座標に基づいて、スクロールを実行します。

   _parent._parent.message.scroll = Math.round( this._y / slideM);

前回はこれをフレームアクションのループの中で使いましたが、今回はクリップアクションの
「enterFrame」イベントで記載しました。

以上のような考え方で。。。「スライダー」MCのクリップアクションでは…

 onClipEvent (load) { MCが読み込まれた最初に
   slideMin = 5; ←1スクロール当たりの移動量を5pxに設定
   slideY = this._height;←スライドの最大移動量を当初の自分の高さに設定
   function Settei() { スライダーとスクロールの関連を「Settei()」メソッドとして定義
     _parent._parent.message.scroll = 1;←テキストスクロール位置を最上位に設定
     theMaxScroll = _parent._parent.message.maxscroll;theMaxScrollに最大値を代入
     if (theMaxScroll == 1) { スクロールの最大値が1(スクロールなし)なら
       _parent._visible = false;←自分を非表示
     } else {  そうでないなら
       _parent._visible = true;←自分を表示
       theHeight = slideY - slideMin * theMaxScroll;※以下、上記説明のとおり
       if (theHeight >= 0) {
         this._height = theHeight+slideMin;
       } else {
         this._height = slideMin;
       }
       this._y = 0; ←自分を一番上に戻す
       slideH = slideY - this._height;
       slideM = slideH/theMaxScroll;
     }
   }
   function mesScroll(Num) { 上下ボタンの処理を「mesScroll()」メソッドとして定義
     nextScroll = _parent._parent.message.scroll + Num;※以下、過去記事参照
     if (nextScroll >= theMaxScroll) {
       this._y = slideH
     } else if (nextScroll <=1) {
       this._y = 0;
     } else {
       this._y = nextScroll * slideM
     }
   }
 }
 onClipEvent (enterFrame) {
   _parent._parent.message.scroll = Math.round(this._y/slideM);←スクロール移動
 }

「mesScroll()」ですが・・・
上下ボタンのボタンアクションで、

 on (press) { ボタンが押し込まれた瞬間(だけ)
   Num = -1;←変数Numに「-1」を代入(※下ボタンは「1」を代入)
   slider.mesScroll(Num);←インスタンス”slider(上記)”の「mesScroll(Num)」を実行
   this.play();←自分(スクロールセットMC)のタイムラインを進める
 }
 on (release) { 押し込まれたボタンが離れた瞬間
   this.gotoAndStop(1);←自分のタイムラインを1フレームに戻して停止
 }

いきなり、slider.mesScroll(-1);としないで、一旦変数Num に値を代入後処理しているのは、
上図のフレームアクションと連動して、ボタンを押し続けると連続スクロールさせるためです。
5フレームに・・・

   mesScroll(Num)
   this.gotoAndPlay( _currentframe - 1 );

として、ボタン処理メソッドを呼び出しています。
ボタンの「on(press)」イベントは、押し込んだその瞬間だけイベントが発生し、
そのまま押し続けていてもあとは何も起こりません。
その瞬間1回だけ「mesScroll(Num)」を実行してスクロールし、自分のタイムラインを進めます。
すぐにボタンを離して「on(release)」イベントが発生すると、1フレームに戻って停止しますが、
押し続けていると、次のgotoAndPlayで、4〜5フレームのループを続けますので、
ボタンを離すまで、フレームレートごとに「mesScroll(Num)」が実行されます。
このとき上ボタンならNumが「-1」なので上に連続スクロール、下ボタンならその逆というわけです。
※「_currentframe」というのは、「これが記載されているフレームのフレーム番号」のことで、
この場合、5フレームに記載されているので「5」という数字が入っていることになります。
したがって「_currentframe - 1」は「4」フレームを指します。
なぜこんな遠回りなことをするかといえば、1〜5フレームの間隔が大きければ、
連続スクロールまでのタイミングが遅くなり、小さければ反応が早くなります。
それを上手くフレームの間隔で調整するのですが、そのときこのように記述をしておけば、
いちいちフレーム番号を書き換える手間が省ける、それだけのことです(^^;

ところで、肝心のスクロールの設定を行う「Settei()」メソッドは、どこで実行するかといえば…
外部テキストの読み込みは、テキストボックスが配置されている「テキストMC」MCで行いますが、
読み込んだ時点で「スクロールの最大値」が決まりますから、そこで実行します。
「テキストMC」のクリップアクション・・・

 onClipEvent (load) { MCが読み込まれた最初に
   this.loadVariables("profile2.txt");←外部テキストの読み込みを開始
 }
 onClipEvent (data) { データの読み込みが完了したら
   message = 外部テキストの変数名;←テキストボックス変数に外部テキストのデータを表示
   s_set.slider.Settei();←「Settei()」を実行してスクロールの準備をする。
 }

※「スライダー」MCを包む「スクロールセット」MCのインスタンス名を”s_set”として、
”s_set”の中の”slider”にある「Settei()」と指定します。

基本的には以上ですが・・・
サンプルでは、テスト用に様々なバリエーションが必要なため、外部テキストの内容を、

 &mes1=■ハンドルネームは”憲ちゃん”・・・・・2人暮らし。&
 &mes2=■丑年の2月7日生まれ。・・・口髭を生やしています(^^;&
 &mes3=■趣味は、賭け事以外は・・・ようになって反省の日々を送っています(;^_^A&
 &mes4=■パソコン暦は永いのですが、WindowsなどのOS・・・&

「mes1」〜「mes4」の4つの変数に分けて記載しています。
※このように記載するときは「変数=値」の組の両端を必ず「&(半角英字)」で挟みます。
そして、読み込んだ当初は・・・

 onClipEvent (data) {
   message = mes1; ←読み込んだ変数のうち「mes1」の内容だけ表示して
   s_set.slider.Settei();←その内容でスクロールを設定
 }

としています。
そして、左の選択ボタンの例えば「Step3」の場合・・・

 on (release) {
   message = mes1+mes2+mes3;←変数を3つ繋いで内容を表示して
   s_set.slider.Settei();←その内容でスクロールを設定
 }

その都度「Settei()」を呼び出して、スクロール条件を再設定します。
実際のflaファイルは、これに加えて、選択中のボタンに色を付けボタン機能を無効にしています。
そのため、ボタンをさらにMCで包んだり、別途制御用の変数step なども設けていますので、
研究してみてください(*^.^*)
Response 04
2004年09月18日 (土) 09時03分> ほたる 
ひえ〜こんなに詳しく;◎◎;
すぐに理解できそうもありませんので、印刷してじっくり読ませていただきます。
憲ちゃん様、本当に本当にありがとうございました。
わからないことがあったらまた是非質問させてください。
これからもよろしくお願いします<<<(__)>>>

このページの先頭へ