フロントエンドベースステップ(十):オブジェクト向け実戦のパッケージドラッグオブジェクト

13268 ワード

前のいくつかの文章、私はみんなとJavaScriptのいくつかの基礎知識を分かち合って、この文章は、最初の実戦の一環に入ります:前のいくつかの章の関連する知識を利用して、1つのドラッグ対象をカプセル化します.より多くの方法と比較を理解するのに役立つように、私は3つの異なる方法でドラッグを実現します.
  • オブジェクトをカプセル化せずに直接実現する.
  • オリジナルJavaScriptパッケージを使用してオブジェクトをドラッグします.
  • は、jQueryを拡張することによって、ドラッグ対象を実現する.

  • 本明細書の例はcodepenに置く.ioでは、読むときに直接見ることができます.codepenの知らない学生については、少し時間をかけて理解することができます.
    牽引の実現過程は非常に多くの実用的な小さな知識に関わるので、私自身の知識の蓄積を強固にするために、みんながもっと多くの知識を学ぶことができるために、私はできるだけ詳しくいくつかの細部を分かち合い、みんなが真剣に読んだ後、きっといくつかのことを学ぶことができると信じています.
    1、DOM要素を動かす方法
    要素のtop,left,translateを変更することによって、その位置が変更されることがよくあります.次の例では、ボタンをクリックするたびに、対応する要素が5 px移動します.皆さんはクリックして見ることができます.
    クリックして要素を動かす小さな例を表示します
    要素top/left値を変更するとページが再描画され、translateは表示されないため、パフォーマンスの最適化からtranslateプロパティが優先されます.
    2、現在のブラウザでサポートされているtransform互換書き方の入手方法
    Transformはcss 3のプロパティであり、互換性の問題に直面しなければなりません.異なるバージョンのブラウザの互換性の書き方には、次のようなものがあります. ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform']
    したがって、現在のブラウザ環境でサポートされているtransformプロパティはどれかを判断する必要があります.方法は次のとおりです.
    //           transform    
    function getTransform() {
        var transform = '',
            divStyle = document.createElement('div').style,
            //              ,               
            transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],
    
            i = 0,
            len = transformArr.length;
    
        for(; i < len; i++)  {
            if(transformArr[i] in divStyle) {
                //         ,    
                return transform = transformArr[i];
            }
        }
    
        //       ,         
        return transform;
    }

    この方法は、ブラウザでサポートされているtransformプロパティを取得するために使用されます.空の文字列を返すと、現在のブラウザではtransformがサポートされていないことを示します.この場合、left、top値を使用して要素の位置を変更する必要があります.サポートされている場合はtransformの値を変更します.
    3、要素の初期位置の取得方法
    まず、ターゲット要素の初期位置を取得する必要があります.そのため、ここでは要素スタイルを取得するための機能関数が必要です.
    しかし、取得要素のスタイルはIEブラウザで他のブラウザとは少し異なるので、互換性のある書き方が必要です.
    function getStyle(elem, property) {
        // ie  currentStyle        ,       getComputedStyle   
        return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(elem, false)[property] : elem.currentStyle[property];
    }

    この方法があれば,ターゲット要素の初期位置を取得する方法を書き出すことができる.
    function getTargetPos(elem) {
        var pos = {x: 0, y: 0};
        var transform = getTransform();
        if(transform) {
            var transformValue = getStyle(elem, transform);
            if(transformValue == 'none') {
                elem.style[transform] = 'translate(0, 0)';
                return pos;
            } else {
                var temp = transformValue.match(/-?\d+/g);
                return pos = {
                    x: parseInt(temp[4].trim()),
                    y: parseInt(temp[5].trim())
                }
            }
        } else {
            if(getStyle(elem, 'position') == 'static') {
                elem.style.position = 'relative';
                return pos;
            } else {
                var x = parseInt(getStyle(elem, 'left') ? getStyle(elem, 'left') : 0);
                var y = parseInt(getStyle(elem, 'top') ? getStyle(elem, 'top') : 0);
                return pos = {
                    x: x,
                    y: y
                }
            }
        }
    }

    ドラッグ&ドロップでは、ターゲット要素の新しい位置を設定し続ける必要があります.そうすると、移動します.そのため、ターゲット要素の位置を設定する方法が必要です.
    // pos = { x: 200, y: 100 }
    function setTargetPos(elem, pos) {
        var transform = getTransform();
        if(transform) {
            elem.style[transform] = 'translate('+ pos.x +'px, '+ pos.y +'px)';
        } else {
            elem.style.left = pos.x + 'px';
            elem.style.top = pos.y + 'px';
        }
        return elem;
    }

    5、私たちはどんな事件を使う必要がありますか.
    PC上のブラウザでは、mousedown、mousemove、mouseupという3つのイベントを組み合わせることで、ドラッグ&ドロップを実現できます.
  • mousedownマウスを押すと
  • がトリガーされる.
  • mousemoveマウスを押してドラッグすると
  • がトリガーされます.
  • mouseupマウスが離すと
  • がトリガーされます.
    移動端では、それぞれに対応するのはtouchstart、touchmove、touchend .
    要素をバインドすると、パラメータとしてコールバック関数に渡されるイベントオブジェクトがあります.イベントオブジェクトを使用すると、現在のマウスの正確な位置を取得できます.マウスの位置情報はドラッグを実現する鍵です.
    イベントオブジェクトは非常に重要で、非常に多くの有用な情報が含まれています.ここでは拡張しません.関数でイベントオブジェクトを印刷して具体的なプロパティを表示することができます.この方法は、イベントオブジェクトの重要なプロパティを覚えていない子供靴に役立ちます.
    6、ドラッグの原理
    イベントがトリガーされると、イベントオブジェクトからマウスの切断位置を取得できます.これはドラッグを実現する鍵です.マウスが(mousedownトリガ)を押すと、マウスの初期位置とターゲット要素の初期位置を覚えなければなりません.私たちの目標は、マウスが移動すると、ターゲット要素も移動することを実現することです.常識に基づいて、私たちは次の関係を得ることができます.
             -        =            -          

    マウスの位置の差をdisで表すと、ターゲット要素の位置は次のようになります.
               = dis +          

    イベントオブジェクトによって、マウスの現在の位置を正確に知ることができるので、マウスをドラッグすると、マウスの移動の差を計算し続け、ターゲット要素の現在の位置を求めることができます.この過程で、ドラッグ&ドロップが実現しました.
    マウスを離してドラッグを終了する場合は、いくつかの終了作業を処理する必要があります.詳細はコードを参照してください.
    7、私はまた思考ガイドを推薦してコードを書くことを補助しました
    よく新人の友达が私に尋ねてきて、論理的な思考能力が強くなければ、コードを書くことができますか?私の答えは:できます.思考ガイドを借りて、論理の短板を簡単に補うことができるからだ.しかも自分の頭の中で論理を補うよりもはっきりしていて、間違いにくい.
    上の6つ目は原理を紹介したので、どうすればそんなに難しくないように見えますが、具体的な手順は、次の思考ガイドで明確に示しています.私たちはこの手順に従ってコードを書くだけでいいので、やってみてください.きっと楽です.
    8、コード実現
    part 1、準備
    //         
    var oElem = document.getElementById('target');
    
    //   2              x,y  
    var startX = 0;
    var startY = 0;
    
    //   2                x,y  
    var sourceX = 0;
    var sourceY = 0;

    part 2、機能関数
    前にコードを貼ったので、繰り返さない
    //           transform    
    function getTransform() {}
    
    //       
    function getStyle(elem, property) {}
    
    //          
    function getTargetPos(elem) {}
    
    //          
    function setTargetPos(elem, potions) {}

    part 3、3つのイベントを宣言するコールバック関数
    この3つの方法はドラッグ&ドロップを実現する核心であり、私は上の思考ガイドのステップに厳格に従って私たちのコードを完成します.
    //    mousedown    ,event        
    function start(event) {
        //         
        startX = event.pageX;
        startY = event.pageY;
    
        //         
        var pos = getTargetPos(oElem);
    
        sourceX = pos.x;
        sourceY = pos.y;
    
        //   
        document.addEventListener('mousemove', move, false);
        document.addEventListener('mouseup', end, false);
    }
    
    function move(event) {
        //         
        var currentX = event.pageX;
        var currentY = event.pageY;
    
        //     
        var distanceX = currentX - startX;
        var distanceY = currentY - startY;
    
        //            
        setTargetPos(oElem, {
            x: (sourceX + distanceX).toFixed(),
            y: (sourceY + distanceY).toFixed()
        })
    }
    
    function end(event) {
        document.removeEventListener('mousemove', move);
        document.removeEventListener('mouseup', end);
        // do other things
    }

    OK、簡単なドラッグで、このように楽しく実現しました.次のリンクをクリックすると、この例のdemoをオンラインで表示できます.
    オリジナルjsによるドラッグ&ドロップ
    9、パッケージドラッグ対象
    前の章では、オブジェクト向けにどのように実現するかを共有し、それらの基礎知識に基づいて、上で実現したドラッグをドラッグオブジェクトにカプセル化しました.ドラッグ・インスタンスを宣言すると、入力されたターゲット要素が自動的にドラッグ・アンド・ドロップ可能な機能を備えます.
    実際の開発では、オブジェクトを1つのjsファイルに単独で配置することがよくあります.このjsファイルは単独でモジュールとして、さまざまなモジュールで組織されて使用されます.もちろん、ここでは複雑なモジュールインタラクションはありません.この例では、モジュールが1つしか必要ありません.
    変数汚染を回避するために,モジュールを関数自己実行方式シミュレーションのブロックレベルの役割ドメインに配置する必要がある.
    ;
    (function() {
        ...
    })();

    通常のモジュール組織では、多くのjsファイルを単純に1つのjsファイルに圧縮するだけなので、ここでの最初のセミコロンは、前のモジュールの最後にセミコロンを使用しないでエラーを報告することを防止するためです.必ず少なくない.もちろんrequireやES 6モジュールなどではこのようなことは起こりません.
    オブジェクトをカプセル化するときに、プロパティとメソッドをコンストラクション関数またはプロトタイプに配置することができ、自己実行関数を追加した後、プロパティとメソッドをモジュールの内部作用ドメインから防止することができることを知っています.これは閉鎖的な知識です.
    では、私たちが直面している挑戦は、属性と方法の位置をどのように合理的に処理するかにあります.
    もちろん,各オブジェクトの状況は異なり,一概には言えないが,この3つの位置の特性を明確に知る必要がある.
  • コンストラクション関数では、属性とメソッドは現在のインスタンスに個別に所有され、現在のインスタンスにのみアクセスでき、宣言されるたびにメソッドが再作成されます.
  • プロトタイプ:プロパティとメソッドはすべてのインスタンスに共通に所有され、すべてのインスタンスにアクセスできます.新しい宣言インスタンスはメソッドを繰り返し作成しません.
  • モジュールの役割ドメイン:属性とメソッドはインスタンスによってアクセスできませんが、内部メソッドによってアクセスできます.新しく宣言されたインスタンスは、同じメソッドを繰り返し作成しません.

  • 方法の判断は比較的簡単である.
    コンストラクション関数のメソッドは、新しいインスタンスを宣言するときに繰り返し作成されるため、コンストラクション関数に表示されないように宣言します.
    コンストラクション関数の変数を使用するか、公開する必要がある場合は、プロトタイプに配置する必要があります.
    メソッドがプライベートで外部からアクセスされない必要がある場合は、モジュールの役割ドメインに配置します.
    属性がどこに置かれているかを正確に判断するのは難しい場合があります.そのため、どの属性がどこに置かなければならないかを正確に定義するのは難しいです.これは実際の開発で絶えず経験をまとめる必要があります.しかし,総じて,この3つの位置の特性と結びつけて最適な判断を下す.
    プロパティ値がインスタンスによって単独でのみ所有される場合、personオブジェクトのnameなど、personインスタンスにのみ属し、ここでオブジェクト内の要素の初期位置をドラッグしたり、この要素の現在の位置だけをドラッグしたりする場合は、構造関数に配置するのに適しています.
    一方、1つのプロパティが内部メソッドにのみアクセスできる場合、このプロパティはモジュールの役割ドメインに配置するのに適しています.
    対象について、上のいくつかの思考はこの文章の最も真剣に考える価値のある精華だと思います.パッケージの中でよく考えていないと、思いがけないバグに遭遇する可能性が高いので、自分の開発経験と結びつけて、よく考えて、自分の観点をまとめることをお勧めします.
    これらの思考に基づいて、みんなは自分でカプセル化してみることができます.それから私といくつか対比して、私たちの考えが何が違うかを見て、次の例の注釈の中で、私は自分の考えを表現します.
    パッケージされたdemoをクリックして表示
    jsソース
    ;
    (function() {
        //         ,        
        var transform = getTransform();
    
        function Drag(selector) {
            //           ,             
            this.elem = typeof selector == 'Object' ? selector : document.getElementById(selector);
            this.startX = 0;
            this.startY = 0;
            this.sourceX = 0;
            this.sourceY = 0;
    
            this.init();
        }
    
    
        //   
        Drag.prototype = {
            constructor: Drag,
    
            init: function() {
                //            
                this.setDrag();
            },
    
            //     ,            ,   getName
            getStyle: function(property) {
                return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(this.elem, false)[property] : this.elem.currentStyle[property];
            },
    
            //              ,          
            getPosition: function() {
                var pos = {x: 0, y: 0};
                if(transform) {
                    var transformValue = this.getStyle(transform);
                    if(transformValue == 'none') {
                        this.elem.style[transform] = 'translate(0, 0)';
                    } else {
                        var temp = transformValue.match(/-?\d+/g);
                        pos = {
                            x: parseInt(temp[4].trim()),
                            y: parseInt(temp[5].trim())
                        }
                    }
                } else {
                    if(this.getStyle('position') == 'static') {
                        this.elem.style.position = 'relative';
                    } else {
                        pos = {
                            x: parseInt(this.getStyle('left') ? this.getStyle('left') : 0),
                            y: parseInt(this.getStyle('top') ? this.getStyle('top') : 0)
                        }
                    }
                }
    
                return pos;
            },
    
            //            
            setPostion: function(pos) {
                if(transform) {
                    this.elem.style[transform] = 'translate('+ pos.x +'px, '+ pos.y +'px)';
                } else {
                    this.elem.style.left = pos.x + 'px';
                    this.elem.style.top = pos.y + 'px';
                }
            },
    
            //          
            setDrag: function() {
                var self = this;
                this.elem.addEventListener('mousedown', start, false);
                function start(event) {
                    self.startX = event.pageX;
                    self.startY = event.pageY;
    
                    var pos = self.getPosition();
    
                    self.sourceX = pos.x;
                    self.sourceY = pos.y;
    
                    document.addEventListener('mousemove', move, false);
                    document.addEventListener('mouseup', end, false);
                }
    
                function move(event) {
                    var currentX = event.pageX;
                    var currentY = event.pageY;
    
                    var distanceX = currentX - self.startX;
                    var distanceY = currentY - self.startY;
    
                    self.setPostion({
                        x: (self.sourceX + distanceX).toFixed(),
                        y: (self.sourceY + distanceY).toFixed()
                    })
                }
    
                function end(event) {
                    document.removeEventListener('mousemove', move);
                    document.removeEventListener('mouseup', end);
                    // do other things
                }
            }
        }
    
        //     ,      transform     
        function getTransform() {
            var transform = '',
                divStyle = document.createElement('div').style,
                transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],
    
                i = 0,
                len = transformArr.length;
    
            for(; i < len; i++)  {
                if(transformArr[i] in divStyle) {
                    return transform = transformArr[i];
                }
            }
    
            return transform;
        }
    
        //          
        window.Drag = Drag;
    })();
    
    //   :  2     
    new Drag('target');
    new Drag('target2');

    このようにオブジェクトをドラッグするとパッケージが完了します.
    私が提供した考え方に基づいて、多くのコンポーネントをカプセル化してみることをお勧めします.例えば、弾窓をカプセル化したり、循環輪播をカプセル化したりします.練習が多くなると、対象に向かうのはもう問題ではありません.この考え方は、未来のどんな時でも使えます.
    次の章では、jQueryオブジェクトの実装を分析し、ここでカプセル化したドラッグ・オブジェクトをjQueryプラグインに拡張する方法について説明します.
    フロントエンドベースステップファミリディレクトリ