script.aculo.usを読み解く

第12回 dragdrop.js (中編)

この記事を読むのに必要な時間:およそ 12 分

0507:  startScrolling: function(speed) {
0508:    if(!(speed[0] || speed[1])) return;
0509:    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
0510:    this.lastScrolled = new Date();
0511:    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
0512:  },
0513:  

507~513行目のstartScrollingは,スクロールを開始する関数です。

511行目で,10ミリ秒ごとにscroll関数を呼ぶタイマをセットします。

510行目で,lastScrolledに,現在時刻を代入します。タイマの10ミリ秒間は信用できないので,ブラウザの現在時刻の差分に頼る仕組みになっています。

0514:  scroll: function() {
0515:    var current = new Date();
0516:    var delta = current - this.lastScrolled;
0517:    this.lastScrolled = current;
0518:    if(this.options.scroll == window) {
0519:      with (this._getWindowScroll(this.options.scroll)) {
0520:        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
0521:          var d = delta / 1000;
0522:          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
0523:        }
0524:      }
0525:    } else {
0526:      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
0527:      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
0528:    }
0529:    
0530:    Position.prepare();
0531:    Droppables.show(Draggables._lastPointer, this.element);
0532:    Draggables.notify('onDrag', this);
0533:    if (this._isScrollChild) {
0534:      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
0535:      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
0536:      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
0537:      if (Draggables._lastScrollPointer[0] < 0)
0538:        Draggables._lastScrollPointer[0] = 0;
0539:      if (Draggables._lastScrollPointer[1] < 0)
0540:        Draggables._lastScrollPointer[1] = 0;
0541:      this.draw(Draggables._lastScrollPointer);
0542:    }
0543:    
0544:    if(this.options.change) this.options.change(this);
0545:  },
0546:  

514~546行目のscroll関数は,実際にスクロールを行う関数です。

516行目で,前回の時刻との差を計算します。

518~524行目で,options.scrollがwindowならば,scrollTo関数でブラウザのスクロールを動かします。

そうでなければ,525行目で,scrollLeft,同Topプロパティを更新して,要素のスクロールを動かします。

520行目で,Position.prepareを呼んで,スクロール位置の変更がうまく働くように調整します。

531行目で,このスクロールによって,ドラッグが上空にきたドロップ先があるかもしれません。それを表示します。

532行目で,Draggables.notifyで,'onDrag'関係のフックを呼びます。

533~542行目で,_isScrollChildがtrueのとき,つまり,element要素がoptions.scrollの子要素であるときは,このスクロールによってelement要素の位置関係に影響があるので,再描画する必要があります。

535~540行目で,スクロールによって生じたズレを求めて,_lastScrollPointerに加算します。

541行目で,drawメソッドで再描画します。

544行目で,options.changeフックがあれば呼びます。

0547:  _getWindowScroll: function(w) {
0548:    var T, L, W, H;
0549:    with (w.document) {
0550:      if (w.document.documentElement && documentElement.scrollTop) {
0551:        T = documentElement.scrollTop;
0552:        L = documentElement.scrollLeft;
0553:      } else if (w.document.body) {
0554:        T = body.scrollTop;
0555:        L = body.scrollLeft;
0556:      }
0557:      if (w.innerWidth) {
0558:        W = w.innerWidth;
0559:        H = w.innerHeight;
0560:      } else if (w.document.documentElement && documentElement.clientWidth) {
0561:        W = documentElement.clientWidth;
0562:        H = documentElement.clientHeight;
0563:      } else {
0564:        W = body.offsetWidth;
0565:        H = body.offsetHeight
0566:      }
0567:    }
0568:    return { top: T, left: L, width: W, height: H };
0569:  }
0570:});
0571:

547~571行目で,_getWindowScrollは,ブラウザが現在表示している領域を返す関数です。

550~556行目で,スクロールの位置を求めます。

550行目で,ブラウザの標準モードではdocumentElementを参照します。

553行目で,互換モードではbodyを参照します。そのscrollTopscrollLeftを調べます。

557~566行目で,領域のサイズを求めます。

557行目で,innerWidthはNetscapeとSafariとMozillaベースのブラウザがサポートしています。

560行目で,そのサポートがないとき,ブラウザの標準モードではdocumentElementを参照します。そのclientWidthclientHeightを調べます。

563行目で,互換モードではbodyを参照します。そのoffsetWidthoffsetHeightを調べます。

0572:Draggable._dragging = { };
0573:

572行目の_draggingは,現在ドラッグ中かどうか,ドラッグ可能要素ごとに真偽値で表すためのハッシュテーブルです。キーにドラッグ可能要素,値に真偽値をとります。

著者プロフィール

源馬照明(げんまてるあき)

名古屋大学大学院多元数理科学研究科1年。学部生のときにSchemeの素晴らしさを知ったのをきっかけに,関数型言語の世界へ。JavaScriptに,ブラウザからすぐに試せる関数型言語としての魅力と将来性を感じている。

ブログ:Gemmaの日記