Drag and Drop をマルチタッチ対応に

uupaa2010-06-24


シングルマウスなDrag and Dropなコードを、マルチタッチ対応にしてみました。

http://jsdo.it/uupaa/MultiTouchDnD

iPhoneでテストしています。
マルチタッチ初挑戦なので、色々と分かってません。

マルチタッチ対応の要点

今回は、シングルマウスのコードを流用し、マルチタッチ対応を行なっています。

Revision: r653

// 0.8/src/ui/drag.js
    // 初期化処理で、mousedown の代わりに touchstart をハンドリング
    uu.event(grip, uu.ver.touch ? "touchstart"
                                : "mousedown", this)
// 0.8/src/ui/drag.js

function dragHandleEvent(evt) {
    uu.event.stop(evt);

    uu.ui.dragbase(evt, this.node, this.grip, this.option);
    var code = evt.code, fn;

    if (code < 5) {
        if (code < 3) { // 1: mousedown, touchstart
                        // 2: mouseup,   touchend
            fn = code === 1 ? uu.event : uu.event.unbind;

            // タッチデバイスなら touchmove と touchend をキャプチャー
            fn(_ie678 ? this.grip : document,
               uu.ver.touch ? "touchmove+,touchend+"
                            : "mousemove+,mouseup+", this);
        } else if (code === 3) { // 3: mousemove
        } else if (code === 4) { // 4: wheel
//            this.option.wheel && this.mousewheel(evt);
        }
    }
}
// 0.8/src/ui/dragbase.js

uu.ui.dragbase || (function(uu) {

uu.ui.dragbase = uuuidragbase; // drag & drop base handler

var _uuuidrag = "data-uuuidrag", // node["data-uuuidrag"] = { dragging: Boolean, x, y }
    _touch = uu.ver.touch;

// uu.ui.dragbase -
function uuuidragbase(evt,      // @param event:
                      node,     // @param Node: move target node
                      grip,     // @param Node: grip target node
                      option) { // @param Hash(= {}): { mouseup, mousemove, mousedown, shim }
                                //  option.mouseup   - Function: mouseup callback
                                //  option.mousemove - Function: mousemove callback
                                //  option.mousedown - Function: mousedown callback
                                //  option.shim      - Object: shim object
    var opt = option || {},
        pageX = evt.pageX,
        pageY = evt.pageY,
        code  = evt.code,
        dragInfo = grip[_uuuidrag] || {},
// この行を追加
        touches, finger, identifier, i, iz; // for iPhone

    if (code === 1 && !dragInfo.dragging) { // 1: mousedown, touchstart

// ここから追加〜
        if (_touch) {
            finger = evt.touches[evt.touches.length - 1];
            identifier = finger.identifier;
            pageX = finger.pageX;
            pageY = finger.pageY;
        }
// 〜ここまで追加

        dragInfo = grip[_uuuidrag] = {
            x: pageX - (parseInt(node.style.left) || 0),
            y: pageY - (parseInt(node.style.top)  || 0),
// この行を追加し、identifier を保存
            id: identifier, // touch.identifier
            dragging: 1
        };

        opt.mousedown && opt.mousedown(evt, node, option, dragInfo);

        uu.ui.zindex.beginDrag(node);

    } else if (code === 2 && dragInfo.dragging) { // 2: mouseup, touchend

// このへんはそのまま
        dragInfo.dragging = 0;

        opt.mouseup && opt.mouseup(evt, node, option, dragInfo);

        uu.ui.zindex.endDrag(node);

    } else if (code === 3 && dragInfo.dragging) { // 3: mousemove, touchmove

// ここから追加〜
        if (_touch) {
            touches = evt.touches;
// touchmodeイベントが発生したノードと、event.touches[...] の identifier を比較し
// 同じなら pageX, pageY を加算する
// これで、個々の finger を識別し同時に動かしている
            for (i = 0, iz = touches.length; i < iz; ++i) {
                finger = touches[i];
                if (dragInfo.id === finger.identifier) {
                    pageX = finger.pageX;
                    pageY = finger.pageY;
                }
            }
        }
// 〜ここまで追加

        node.style.left = (pageX - dragInfo.x) + "px";
        node.style.top  = (pageY - dragInfo.y) + "px";

        opt.mousemove && opt.mousemove(evt, node, option, dragInfo);
    }
}

})(uu);

ということをやっています。identifierの識別方法は、もっとうまい方法があるのかもしれません。