DD; Using Interaction Groups
2009/6/7 tetsuya_odaka js中のコメントの修正
===================================================
このInteraction Groupを用いたExampleも、さきのReordering a Listと同じくタフなExample。
解析してソースに注釈をいれるのに1時間半もかかってしまった。
画面は以下のようになっていて、画面上部にSlotと呼ぶDDTarget、下部にはPlayerと呼ぶDDProxyを拡張したDDPlayerというオブジェクトを配置するようになっている。
DDTargetとDDPlayerは、Interaction Groupに分けられていて、同一のGroupに属するSlotにしか、Playerを配置できない。同一GroupのSlot上でPlayerをDropすれば、そこに配置されるが、間違うとスタート地点に戻ってしまう。
また、SlotにPlayerが収まっていても、あらたなPlayerで上書きできるようになっている。
画面上部には、interactionモードを選択できるList Boxがあって、Point(デフォルト)とIntersectが選べる。
ソースコードは比較的平易に書かれており、YAHOO.util.DragFropMgrが、ヘルパーとしての多くの機能をもっていることが理解できる(APIドキュメントはこちら)。
面倒なのは、上の青字で書いた部分回りの処理で、そのためのフラグ類が読みづらい。
(そのため、1文だけ、コードを追加した)
以下にhtmlの全文を載せる。
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <META http-equiv="Content-Style-Type" content="text/css"> <TITLE>Ajax_Sampling</TITLE> <style type="text/css"> .slot { border:2px solid #aaaaaa; background-color:#dddddd; color:#666666; text-align:center; position: absolute; width:60px; height:60px; } .player { border:2px solid #bbbbbb; color:#eeeeee; text-align:center; position: absolute; width:60px; height:60px; } .target { border:2px solid #574188; background-color:#cccccc; text-align:center; position: absolute; width:60px; height:60px; } #t1 { left: 10px; top: 0px; } #t2 { left: 378px; top: 0px; } #b1 { left: 84px; top: 50px; } #b2 { left: 158px; top: 50px; } #b3 { left: 232px; top: 50px; } #b4 { left: 306px; top: 50px; } #pt1 { background-color:#7E695E; left: 84px; top: 150px; } #pt2 { background-color:#7E695E; left: 84px; top: 230px; } #pb1 { background-color:#416153; left: 195px; top: 150px; } #pb2 { background-color:#416153; left: 195px; top: 230px; } #pboth1 { background-color:#552E37; left: 306px; top: 150px; } #pboth2 { background-color:#552E37; left: 306px; top: 230px; } #usercontrols { top: -36px; } #workarea { position: relative; height: 300px; } </style> <!-- 読み込むjs --> <script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js" > </script> <script type="text/javascript" src="scripts/yui/dragdrop/dragdrop-min.js" > </script> <script type="text/javascript"> // YAHOO.examplr.DDPlayerのコンストラクタの定義 YAHOO.example.DDPlayer = function(id, sGroup, config) { // superclassのコンストラクタの呼び出し YAHOO.example.DDPlayer.superclass.constructor.apply(this, arguments); // initPlayerメソッドの呼び出し this.initPlayer(id, sGroup, config); }; //YAHOO.util.DDProxyを継承してDDPlayerを作成する。 YAHOO.extend(YAHOO.example.DDPlayer, YAHOO.util.DDProxy, { TYPE: "DDPlayer", // Playerの初期化 initPlayer: function(id, sGroup, config) { // idがnullならイニシャライズできない。 if (!id) { return; } // proxyを取得(getDragEl()) var el = this.getDragEl() YAHOO.util.Dom.setStyle(el, "borderColor", "transparent"); YAHOO.util.Dom.setStyle(el, "opacity", 0.76); // Targetではない。 this.isTarget = false; // styleの退避領域 this.originalStyles = []; this.type = YAHOO.example.DDPlayer.TYPE; // DropしたTargetオブジェクトを入れる this.slot = null; // DropしたPlayerをいれる。 this.player = null; // 移動する前のPlayer要素(getEl())の位置を保持しておく。 this.startPos = YAHOO.util.Dom.getXY( this.getEl() ); }, /** * startDragのoverride * */ startDrag: function(x, y) { var Dom = YAHOO.util.Dom; // proxyを取得 var dragEl = this.getDragEl(); // クリックされたPlayer要素そのもの var clickEl = this.getEl(); // proxy要素に、Player要素のinnerHTMLとclass名(style)をコピー dragEl.innerHTML = clickEl.innerHTML; dragEl.className = clickEl.className; // proxy要素のcolor属性、backgroundColor属性を、Player要素からコピー Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color")); Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor")); // Player要素の透過度を0.1まで下げる。 Dom.setStyle(clickEl, "opacity", 0.1); // Player要素と同一のDDグループのTargetオブジェクトをDDMより取得する。 // 第2引数=trueでターゲットだけ取得できる。 var targets = YAHOO.util.DDM.getRelated(this, true); // YAHOO.log(targets.length + " targets", "info", "example"); for (var i=0; i<targets.length; i++) { var targetEl = this.getTargetDomRef(targets[i]); // このPlayer(this)にターゲットのスタイル(クラス名)を保存していなければ、保存する。 if (!this.originalStyles[targetEl.id]) { this.originalStyles[targetEl.id] = targetEl.className; } // class名をtargetに変更(targetのstyleが変更される) targetEl.className = "target"; } }, /** * oDD(Target)の状況を調べて、適当なオブジェクトを返却する。 * 要素の取得(itemが入っていればitem、入っていなければ、targetそのものがどもってくる。 */ getTargetDomRef: function(oDD) { // oDDにitemプロパティーが存在する(itemが入った)場合、入っているitemのエレメント(HTMLDivElement)を返す if (oDD.player) { return oDD.player.getEl(); } else { // oDDにitemプロパティーが存在しない場合(cartの場合)、そのエレメント(HTMLDivElement)を返す return oDD.getEl(); } }, /** * Drag終了時の処理。 * Targetの位置にPlayer要素が入る */ endDrag: function(e) { // dropした要素の透過度を1に戻す。 YAHOO.util.Dom.setStyle(this.getEl(), "opacity", 1); // dropした要素のスタイルを元に戻す。 this.resetTargets(); }, /** * ターゲットのスタイルを、Drop時に置き換えたitemのスタイルにリセットする。 */ resetTargets: function() { // 同一のDDグループのTargetオブジェクトの取得 var targets = YAHOO.util.DDM.getRelated(this, true); for (var i=0; i<targets.length; i++) { // 要素の取得(itemが入っていればitem、入っていなければ、targetそのものがどもってくる) var targetEl = this.getTargetDomRef(targets[i]); // class名をもとに戻す。=> original なスタイルに戻す。 var oldStyle = this.originalStyles[targetEl.id]; if (oldStyle) { targetEl.className = oldStyle; } } }, /** * DragされたオブジェクトがDropした際に発火する。 * idは、mode=POINTの際はDropされたTargetのidか、mode=INTERSECTの場合は、配列 * (TargetにまたがってDropされた場合の考慮)のいずれか。 */ onDragDrop: function(e, id) { // thisはプロキシオブジェクト // DropされるTarget(=cart)のオブジェクトを入れる。 var oDD; if ("string" == typeof id) { // mode=pointの場合は、oDDにTargetを入れる。 oDD = YAHOO.util.DDM.getDDById(id); } else { // mode=intersectの場合は、BestMatchするTargetをoDD入れる。 // TargetにまたがってDropされない場合には、pointと同じこと。 oDD = YAHOO.util.DDM.getBestMatch(id); } // プロキシオブジェクトの要素の取得 var el = this.getEl(); // Targetにplayerプロパティーが存在する場合;itemが入っている場合 if (oDD.player) { // 移動中のplayerが既にスロットに入っていた場合=>スロット間の移動の場合 if (this.slot) { // 移動中のitemのスロットが、Dropされたitemのターゲットとしてlegalな場合。 // => swapする。 if ( YAHOO.util.DDM.isLegalTarget(oDD.player, this.slot) ) { // elの(x,y)に、Targetに入っているPlayerの要素の(x,y) // をコピー(thisの移動元に戻す)。 YAHOO.util.DDM.moveToEl(oDD.player.getEl(), el); this.slot.player = oDD.player; oDD.player.slot = this.slot; } else { // illegalならば、移動を中止する。 YAHOO.util.Dom.setXY(oDD.player.getEl(), oDD.player.startPos); this.slot.player = null; oDD.player.slot = null } } else { // スロット間移動以外 oDD.player.slot = null; YAHOO.util.DDM.moveToEl(oDD.player.getEl(), el); } } else { // Targetにitemプロパティーが存在しない場合;itemが入っていない場合 if (this.slot) { this.slot.player = null; } } // Targetに入っているPlayerの要素の(x,y)に、この移動元の(x,y) // をコピー。 YAHOO.util.DDM.moveToEl(el, oDD.getEl()); // Drop後の状態にスタイルをリセットする。 this.resetTargets(); // このオブジェクトのslotに、DropしたTargetオブジェクトを入れる。 this.slot = oDD; // このオブジェクトのslot.itemに、this(Dropしたオブジェクト)をいれる。 this.slot.player = this; }, swap: function(el1, el2) { var Dom = YAHOO.util.Dom; var pos1 = Dom.getXY(el1); var pos2 = Dom.getXY(el2); Dom.setXY(el1, pos2); Dom.setXY(el2, pos1); }, // 何もしないようにoverride; レスポンス向上のためと思われる onDragOver: function(e, id) { }, // 何もしないようにoverride; レスポンス向上のためと思われる onDrag: function(e, id) { } }); // page scopeで変数を定義&Alias var slots = [], players = []; var Event = YAHOO.util.Event, DDM = YAHOO.util.DDM; Event.onDOMReady(function() { // slots slots[0] = new YAHOO.util.DDTarget("t1", "topslots"); slots[1] = new YAHOO.util.DDTarget("t2", "topslots"); slots[2] = new YAHOO.util.DDTarget("b1", "bottomslots"); slots[3] = new YAHOO.util.DDTarget("b2", "bottomslots"); slots[4] = new YAHOO.util.DDTarget("b3", "bottomslots"); slots[5] = new YAHOO.util.DDTarget("b4", "bottomslots"); // players players[0] = new YAHOO.example.DDPlayer("pt1", "topslots"); players[1] = new YAHOO.example.DDPlayer("pt2", "topslots"); players[2] = new YAHOO.example.DDPlayer("pb1", "bottomslots"); players[3] = new YAHOO.example.DDPlayer("pb2", "bottomslots"); players[4] = new YAHOO.example.DDPlayer("pboth1", "topslots"); players[4].addToGroup("bottomslots"); players[5] = new YAHOO.example.DDPlayer("pboth2", "topslots"); players[5].addToGroup("bottomslots"); DDM.mode = document.getElementById("ddmode").selectedIndex; Event.on("ddmode", "change", function(e) { YAHOO.util.DDM.mode = this.selectedIndex; }); }); </script> </HEAD> <BODY> <div id="usercontrols"> Interaction Mode: <select id="ddmode" > <option value="0" selected>Point</option> <option value="1">Intersect</option> </select> </div> <div id="workarea"> <!-- Top Slot --> <div class="slot" id="t1" >1</div> <div class="slot" id="t2" >2</div> <!-- Bottom Slot --> <div class="slot" id="b1" >3</div> <div class="slot" id="b2" >4</div> <div class="slot" id="b3" >5</div> <div class="slot" id="b4" >6</div> <!-- Top SlotだけをTargetにするPlayer --> <div class="player" id="pt1" >1</div> <div class="player" id="pt2" >2</div> <!-- Bottom SlotだけをTargetにするPlayer --> <div class="player" id="pb1" >3</div> <div class="player" id="pb2" >4</div> <!-- Top,Bottom Slotを共にTargetにするPlayer --> <div class="player" id="pboth1" >5</div> <div class="player" id="pboth2" >6</div> </div> </BODY> </HTML>