Heim  >  Artikel  >  Web-Frontend  >  Front-End-Fortschritt (10): Kapselung von Drag-and-Drop-Objekten

Front-End-Fortschritt (10): Kapselung von Drag-and-Drop-Objekten

PHPz
PHPzOriginal
2017-04-04 17:57:401323Durchsuche

Front-End-Fortschritt (10): Kapselung von Drag-and-Drop-Objekten

Abschließend

In den vorherigen Artikeln habe ich einige grundlegende Kenntnisse über JavaScript mit Ihnen geteilt Link: Nutzen Sie das Wissen aus den vorherigen Kapiteln, um ein Drag--Objekt zu kapseln. Um Ihnen das Verständnis weiterer Methoden und den Vergleich zu erleichtern, werde ich drei verschiedene Methoden zum Implementieren von Drag & Drop verwenden.

  • Direkte Implementierung ohne Kapselung von Objekten

  • Verwenden Sie natives JavaScript, um Drag-and-Drop-Objekte zu kapseln; 🎜> Drag-and-Drop-Objekte werden durch die Erweiterung von

    jQuery
  • implementiert.
  • Die Beispiele in diesem Artikel werden in codepen.io platziert, damit jeder sie beim Lesen direkt sehen kann. Wenn Sie sich mit Codepen nicht auskennen, können Sie sich etwas Zeit nehmen, um ein wenig darüber zu lernen.
Der Implementierungsprozess von Drag & Drop erfordert viel praktisches Wissen. Um meinen eigenen Wissensaufbau zu festigen und allen mehr Wissen zu vermitteln, werde ich mein Bestes geben, um es zu erklären Detail Einige Details werden geteilt. Ich glaube, dass jeder etwas lernen wird, nachdem er es sorgfältig gelesen hat.

1. So verschieben Sie ein DOM-Element

Wir ändern häufig den <a href="http://www.php.cn/wiki/904.%20html" target="_blank">top</a>

, left, translator und seine Position ändert sich. Im folgenden Beispiel wird jedes Mal, wenn auf die Schaltfläche
geklickt wird, das entsprechende Element um 5 Pixel verschoben. Sie können zum Anzeigen klicken.

<a href="http://www.php.cn/wiki/904.html" target="_blank">top</a>,left,translateKlicken Sie hier, um ein kleines Beispiel für die Animation eines Elements zu sehen

Da die Änderung des oberen/linken Werts eines Elements dazu führt, dass die Seite neu gezeichnet wird, die Übersetzung jedoch nicht von der

-Leistung. Basierend auf der Optimierung von

werden wir der Verwendung des Translate-Attributs

Vorrang einräumen. 2. So erhalten Sie die vom aktuellen Browser unterstützte transfor

m-kompatible Schreibmethode
transform ist eine Eigenschaft von CSS3, und wir müssen uns damit auseinandersetzen es, wenn wir es verwenden. Bezüglich Kompatibilitätsproblemen. Die kompatiblen Schreibmethoden verschiedener Browserversionen sind ungefähr wie folgt:

Daher müssen wir bestimmen, welches Transformationsattribut von der aktuellen Browserumgebung unterstützt wird folgt:

['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform']

Diese Methode wird verwendet, um das vom Browser unterstützte Transformationsattribut abzurufen. Wenn die zurückgegebene Zeichenfolge eine leere Zeichenfolge ist, bedeutet dies, dass der aktuelle Browser die Transformation nicht unterstützt. Zu diesem Zeitpunkt müssen wir die linken und oberen Werte verwenden, um die Position des Elements zu ändern. Falls unterstützt, ändern Sie den Wert von transform.

3. So erhalten Sie die Anfangsposition des Elements
// 获取当前浏览器支持的transform兼容写法
function getTransform() {
    var transform = '',
        pStyle = document.createElement('p').style,
        // 可能涉及到的几种兼容性写法,通过循环找出浏览器识别的那一个
        transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],

        i = 0,
        len = transformArr.length;

    for(; i < len; i++)  {
        if(transformArr[i] in pStyle) {
            // 找到之后立即返回,结束函数
            return transform = transformArr[i];
        }
    }

    // 如果没有找到,就直接返回空字符串
    return transform;
}

Wir müssen zuerst die Anfangsposition des Zielelements ermitteln, daher benötigen wir hier eine Funktion, die speziell zum Abrufen des Elementstils verwendet wird.

Aber das Abrufen des Elementstils im
IE-Browser

unterscheidet sich etwas von anderen Browsern, daher benötigen wir eine kompatible Schreibweise.

Nachdem Sie über diese Methode verfügen, können Sie mit dem Schreiben der Methode beginnen, um die Anfangsposition des Zielelements zu ermitteln.

function getStyle(elem, property) {
    // ie通过currentStyle来获取元素的样式,其他浏览器通过getComputedStyle来获取
    return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(elem, false)[property] : elem.currentStyle[property];
}
Während des Ziehvorgangs müssen wir ständig die neue Position des Zielelements festlegen, damit es sich bewegt. Daher benötigen wir eine Methode, um die Position des Zielelements festzulegen.

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
            }
        }
    }
}
5. Welche

Ereignisse

müssen wir verwenden?
// 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;
}
Im Browser auf dem PC kann uns die Kombination der drei Ereignisse von bei der Implementierung von Drag & Drop helfen.

mousedown、mousemove、mouseup

Wird ausgelöst, wenn die Maus gedrückt wird
  • mousedown

    Wird ausgelöst, wenn die Maus gedrückt und dann gezogen wird
  • mousemove

    Wird ausgelöst, wenn die Maus losgelassen wird
  • mouseup

    Auf der mobilen Seite sind die entsprechenden touchstart, touchmove, touch<a href="http://www.php.cn/wiki/1048.html" target="_blank">Ende</a>
  • .

Wenn wir diese Ereignisse an ein Element binden, wird ein Ereignisobjekt als Parameter an die touchstart、touchmove、touch<a href="http://www.php.cn/wiki/1048.html" target="_blank">end</a>Rückruffunktion

übergeben. Über das Ereignisobjekt können wir die genaue Position davon ermitteln Die aktuelle Mausposition und Mauspositionsinformationen sind der Schlüssel zur Realisierung von Drag & Drop.

Das Ereignisobjekt ist sehr wichtig. Es enthält viele nützliche Informationen. Sie können das Ereignisobjekt in der Funktion anzeigen nützlich für Sehr nützlich für Kinder, die sich nicht an wichtige Attribute von Ereignisobjekten erinnern können.

6、拖拽的原理

当事件触发时,我们可以通过事件对象获取到鼠标的精切位置。这是实现拖拽的关键。当鼠标按下(mousedown触发)时,我们需要记住鼠标的初始位置与目标元素的初始位置,我们的目标就是实现当鼠标移动时,目标元素也跟着移动,根据常理我们可以得出如下关系:

移动后的鼠标位置 - 鼠标初始位置 = 移动后的目标元素位置 - 目标元素的初始位置

如果鼠标位置的差值我们用dis来表示,那么目标元素的位置就等于:

移动后目标元素的位置 = dis + 目标元素的初始位置

通过事件对象,我们可以精确的知道鼠标的当前位置,因此当鼠标拖动(mousemove)时,我们可以不停的计算出鼠标移动的差值,以此来求出目标元素的当前位置。这个过程,就实现了拖拽。

而在鼠标松开(mouseup)结束拖拽时,我们需要处理一些收尾工作。详情见代码。

7、 我又来推荐思维导图辅助写代码了

常常有新人朋友跑来问我,如果逻辑思维能力不强,能不能写代码做前端。我的答案是:能。因为借助思维导图,可以很轻松的弥补逻辑的短板。而且比在自己头脑中脑补逻辑更加清晰明了,不易出错。

上面第六点我介绍了原理,因此如何做就显得不是那么难了,而具体的步骤,则在下面的思维导图中明确给出,我们只需要按照这个步骤来写代码即可,试试看,一定很轻松。

Front-End-Fortschritt (10): Kapselung von Drag-and-Drop-Objekten

使用思维导图清晰的表达出整个拖拽过程我们需要干的事情

8、代码实现

part1、准备工作

// 获取目标元素对象
var oElem = document.getElementById('target');

// 声明2个变量用来保存鼠标初始位置的x,y坐标
var startX = 0;
var startY = 0;

// 声明2个变量用来保存目标元素初始位置的x,y坐标
var sourceX = 0;
var sourceY = 0;

part2、功能函数

因为之前已经贴过代码,就不再重复

// 获取当前浏览器支持的transform兼容写法
function getTransform() {}

// 获取元素属性
function getStyle(elem, property) {}

// 获取元素的初始位置
function getTargetPos(elem) {}

// 设置元素的初始位置
function setTargetPos(elem, potions) {}

part3、声明三个事件的回调函数

这三个方法就是实现拖拽的核心所在,我将严格按照上面思维导图中的步骤来完成我们的代码。

// 绑定在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、封装拖拽对象

在前面一章我给大家分享了面向对象如何实现,基于那些基础知识,我们来将上面实现的拖拽封装为一个拖拽对象。我们的目标是,只要我们声明一个拖拽实例,那么传入的目标元素将自动具备可以被拖拽的功能。

在实际开发中,一个对象我们常常会单独放在一个js文件中,这个js文件将单独作为一个模块,利用各种模块的方式组织起来使用。当然这里没有复杂的模块交互,因为这个例子,我们只需要一个模块即可。

为了避免变量污染,我们需要将模块放置于一个函数自执行方式模拟的块级作用域中。

;
(function() {
    ...
})();

在普通的模块组织中,我们只是单纯的将许多js文件压缩成为一个js文件,因此此处的第一个分号则是为了防止上一个模块的结尾不用分号导致报错。必不可少。当然在通过require或者ES6模块等方式就不会出现这样的情况。

我们知道,在封装一个对象的时候,我们可以将属性与方法放置于构造函数或者原型中,而在增加了自执行函数之后,我们又可以将属性和方法防止与模块的内部作用域。这是闭包的知识。

那么我们面临的挑战就在于,如何合理的处理属性与方法的位置。

当然,每一个对象的情况都不一样,不能一概而论,我们需要清晰的知道这三种位置的特性才能做出最适合的决定。

  • 构造函数中: 属性与方法为当前实例单独拥有,只能被当前实例访问,并且每声明一个实例,其中的方法都会被重新创建一次。

  • 原型中: 属性与方法为所有实例共同拥有,可以被所有实例访问,新声明实例不会重复创建方法。

  • 模块作用域中:属性和方法不能被任何实例访问,但是能被内部方法访问,新声明的实例,不会重复创建相同的方法。

对于方法的判断比较简单。

因为在构造函数中的方法总会在声明一个新的实例时被重复创建,因此我们声明的方法都尽量避免出现在构造函数中。

而如果你的方法中需要用到构造函数中的变量,或者想要公开,那就需要放在原型中。

如果方法需要私有不被外界访问,那么就放置在模块作用域中。

对于属性放置于什么位置有的时候很难做出正确的判断,因此我很难给出一个准确的定义告诉你什么属性一定要放在什么位置,这需要在实际开发中不断的总结经验。但是总的来说,仍然要结合这三个位置的特性来做出最合适的判断。

如果属性值只能被实例单独拥有,比如person对象的name,只能属于某一个person实例,又比如这里拖拽对象中,某一个元素的初始位置,也仅仅只是这个元素的当前位置,这个属性,则适合放在构造函数中。

而如果一个属性仅仅供内部方法访问,这个属性就适合放在模块作用域中。

关于面向对象,上面的几点思考我认为是这篇文章最值得认真思考的精华。如果在封装时没有思考清楚,很可能会遇到很多你意想不到的bug,所以建议大家结合自己的开发经验,多多思考,总结出自己的观点。

根据这些思考,大家可以自己尝试封装一下。然后与我的做一些对比,看看我们的想法有什么不同,在下面例子的注释中,我将自己的想法表达出来。

点击查看已经封装好的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 = '',
            pStyle = document.createElement('p').style,
            transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],

            i = 0,
            len = transformArr.length;

        for(; i < len; i++)  {
            if(transformArr[i] in pStyle) {
                return transform = transformArr[i];
            }
        }

        return transform;
    }

    // 一种对外暴露的方式
    window.Drag = Drag;
})();

// 使用:声明2个拖拽实例
new Drag('target');
new Drag('target2');

这样一个拖拽对象就封装完毕了。

建议大家根据我提供的思维方式,多多尝试封装一些组件。比如封装一个弹窗,封装一个循环轮播等。练得多了,面向对象就不再是问题了。这种思维方式,在未来任何时候都是能够用到的。

Das obige ist der detaillierte Inhalt vonFront-End-Fortschritt (10): Kapselung von Drag-and-Drop-Objekten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn