script.aculo.usを読み解く

第12回 dragdrop.js (中編)

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

0445:  keyPress: function(event) {
0446:    if(event.keyCode!=Event.KEY_ESC) return;
0447:    this.finishDrag(event, false);
0448:    Event.stop(event);
0449:  },
0450:  

445~450行目のkeyPressは,ドラッグ中にESCキーを押すとドラッグをキャンセルする関数です。documentオブジェクトのkeypressイベントのハンドラから呼ばれます。

447行目で,finishDrag関数を,2番目の引数をfalseで呼びます。

0451:  endDrag: function(event) {
0452:    if(!this.dragging) return;
0453:    this.stopScrolling();
0454:    this.finishDrag(event, true);
0455:    Event.stop(event);
0456:  },
0457:  

451~457行目のendDragは,ドラッグの終了時に呼ばれる関数です。DraggablesのendDragイベントハンドラから呼ばれます。

453行目で,スクロールを中止します。

454行目で,finishDrag関数を,2番目の引数をtrueで呼びます。

0458:  draw: function(point) {
0459:    var pos = Position.cumulativeOffset(this.element);
0460:    if(this.options.ghosting) {
0461:      var r   = Position.realOffset(this.element);
0462:      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
0463:    }
0464:    
0465:    var d = this.currentDelta();
0466:    pos[0] -= d[0]; pos[1] -= d[1];
0467:    
0468:    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
0469:      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
0470:      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
0471:    }
0472:    
0473:    var p = [0,1].map(function(i){ 
0474:      return (point[i]-pos[i]-this.offset[i]) 
0475:    }.bind(this));
0476:    
0477:    if(this.options.snap) {
0478:      if(Object.isFunction(this.options.snap)) {
0479:        p = this.options.snap(p[0],p[1],this);
0480:      } else {
0481:      if(Object.isArray(this.options.snap)) {
0482:        p = p.map( function(v, i) {
0483:          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
0484:      } else {
0485:        p = p.map( function(v) {
0486:          return (v/this.options.snap).round()*this.options.snap }.bind(this))
0487:      }
0488:    }}
0489:    
0490:    var style = this.element.style;
0491:    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
0492:      style.left = p[0] + "px";
0493:    if((!this.options.constraint) || (this.options.constraint=='vertical'))
0494:      style.top  = p[1] + "px";
0495:    
0496:    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
0497:  },
0498:  

458~498行目のdraw関数は,ドラッグ中の要素を描画する関数です。ドラッグ中の要素の適切な位置は,Position.cumulativeOffsetやrealOffsetを駆使して求めます。

459行目のPosition.cumulativeOffsetで,ドキュメント基点からのオフセットを求めます。

460~463行目では,options.ghostingで要素のクローンを作った分,座標にズレが生じているので,補正します。Position.realOffsetで,スクロール可能なオブジェクトの階層を遡って,すべてのスクロール量を積算します。さらにそこから,ページ自体のスクロール量であるdeltaX,deltaYとの差分をとって,posを補正します。

466行目で,現在の要素の位置を引きます。

468~471行目で,options.scrollが有効で,かつ,それがwindowで無い場合は,options.scroll要素のスクロール量について補正します。

473行目で,pに,ドラッグ中の要素の実際の描画位置を求めます。このとき,ドラッグ開始時のマウスポインタと要素の位置のズレであるoffsetの量も補正します。

477~488行目で,optiotns.snapの設定がある場合は,もう一手間あります。

478行目で,それが関数なら,その呼び出しの結果をpにします。

481行目で,それが配列なら,座標をsnapの値で割ってからround関数で丸めてsnapの値をかけるという操作を行います。これはグラフにすると幅snapの階段状になることから,ドラッグの座標の動きが幅snapごとに引っかかるようになります。

484行目で,それが値なら,配列と同様の処理をします。

490~494行目で,実際にelement要素のCSSのleft,topプロパティの値に代入して,描画位置を更新します。このとき,options.constraintの制限があるときは,水平方向の動きだけ,あるいは,垂直方向の動きだけを,反映します。

0499:  stopScrolling: function() {
0500:    if(this.scrollInterval) {
0501:      clearInterval(this.scrollInterval);
0502:      this.scrollInterval = null;
0503:      Draggables._lastScrollPointer = null;
0504:    }
0505:  },
0506:  

499~506行目のstopScrollingは,スクロールを中止する関数です。

501行目で,タイマを無効にします。その他の状態も初期化します。

著者プロフィール

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

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

ブログ:Gemmaの日記