JavaScript實現[網易雲音樂Web站登陸窗口]拖拽功能

說明

你可能發現有不少網站他們的登陸窗口或者說是登陸框是能夠拖動的, 更有甚者他們的站點提示框均可以拖動, 你也許可能會對這個功能的實現感興趣, 那麼這篇文章可能會對你有所幫助!具體的網站示例以 網易雲音樂 Web站點爲例,具體效果以下圖所示:

網易雲音樂登陸窗口

JavaScript實現登陸窗口的拖拽原理解析

預先假設要實現的登陸框容許點擊鼠標獲取拖拽事件的具體位置就是登陸框的標題區塊也就是下圖所示登陸區塊黑色部分在文章中以 容許點擊鼠標獲取拖拽事件區域 說明問題, 而且假定 紅色十字 圖標就是鼠標狀態:

容許點擊鼠標獲取拖拽事件的具體位置

  • 當鼠標在 容許點擊鼠標獲取拖拽事件區域 點擊鼠標時觸發 onmousedown 事件
  • 獲取鼠標在 容許點擊鼠標獲取拖拽事件區域 點擊時的具體位置

點擊時的具體位置

  • 當鼠標移動時改變 登陸窗口 左上角位置(也是就是座標點位置)距離頁面可視區左上角位置, 那麼這個 登陸窗口 也就移動了, 也就實現了 登陸窗口 的拖拽功能html

    • 當鼠標移動時觸發 onmousemove 事件
  • 當鼠標擡起時, 觸發 onmouseup 事件git

    • 釋放 onmousemove 事件
    • 釋放 onmouseup 事件自身
以上過程就是一個完整的 登陸窗口 拖拽的過程, 不過要注意如下幾點:
  1. 拖拽移動 登陸窗口 時爲 document 綁定 onmousemove 事件, 而不是 容許點擊鼠標獲取拖拽事件區域github

    • 爲何這樣作呢? 若是隻是爲 容許點擊鼠標獲取拖拽事件區域 綁定 onmousemove 件事, 當鼠標移動的快了就會致使事件綁定丟失, 不過你能夠去驗證
    • 下圖是將 onmousemove 綁定到 容許點擊鼠標獲取拖拽事件區域 這樣其是不對的!
    • 示例
    • 下圖是將 onmousemove 綁定到 document 上的事件, 這樣纔是最完美的, 由於你無論怎麼拖它都不會丟失事件綁定
    • 示例
  2. 擡起鼠標時一樣也是爲 document 綁定 onmouseup 事件, 而不是 容許點擊鼠標獲取拖拽事件區域web

    • 爲何呢?若是隻是爲 容許點擊鼠標獲取拖拽事件區域 綁定 onmouseup 事件, 你會發現當鼠標移動到脫離文檔可視區域時,擡起點擊的鼠標按鍵你會發現當再一次移動鼠標時它依然能夠移動這就不符合常理了不是嘛!那就給 document 綁定 onmouseup 事件時,它就能夠很好的解決這個怪異的問題!
    • 下圖是將 onmouseup 綁定到 容許點擊鼠標獲取拖拽事件區域 這樣其是不對的!
    • 示例
    • 下圖是將 onmouseup 綁定到 document 上的事件, 這樣纔是最完美的
    • 示例
    • 不過你會發現拖不回來了, 這是問題也是後面要說的優化問題, 先留一坑,一會填坑時再說!

JavaScript實現登陸窗口的拖拽效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS拖拽</title>
    <style>
        /* public style start */
        * { margin: 0; padding: 0; }
        body, textarea, select, input, button {font-size: 12px;color: white;font-family: Arial, Helvetica, sans-serif;-webkit-text-size-adjust: none;}
        .fl-l { float: left }
        .fl-r { float: right }
        .clearfix:after {visibility:hidden;display:block;font-size:0;content:'.';clear:both;height:0;}
        .clearfix {*zoom:1;}
        /* public style end */

        /* dialog window style start */

        /* dialog window main */
        #dialog {
            height: 200px;
            width: 400px;
            background-color: #EAF6DB;
            border: 1px solid lightslategray;
            position: absolute;
            top: 200px;
            left: 400px;
        }

        /* dialog window title section */
        .dialog-title {
            height: 40px;
            line-height: 40px;
            background-color: #3a3333;
            cursor: move;
        }

        /* dialog window title inner detail */
        .login, .close {
            display: inline-block;
            margin: 0 15px;
        }
    </style>
    <script>
        window.onload = function () {
            // 獲取DOM元素
            var oDialog_title = document.getElementById( 'dialog-title' );  // 容許點擊鼠標獲取拖拽事件區域
            var oDialog = oDialog_title.parentNode; // 登陸窗口

            // 初始化鼠標默認位置
            var iDisX = 0;
            var iDisY = 0;

            // 爲獲取到的DOM元素添加鼠標按下事件 `onmousedown`
            oDialog_title.onmousedown = function ( ent ) {
                // 保存鼠標事件對象
                var oEvent = ent || event;

                // 距離計算鼠標位於彈出框內的位置
                iDisX = oEvent.clientX - oDialog.offsetLeft;    // 鼠標X軸位置 - 彈出框X外左邊距
                iDisY = oEvent.clientY - oDialog.offsetTop;     // 鼠標Y軸位置 - 彈出框Y外上邊距

                // 點擊彈出框後拖動鼠標, 移動彈出框
                document.onmousemove = function ( ent ) {
                    // 保存鼠標事件對象
                    var oEvent = ent || event;

                    // 當鼠標移動時改變彈出框的位置
                    oDialog.style.left = oEvent.clientX - iDisX + 'px';
                    oDialog.style.top = oEvent.clientY - iDisY + 'px';
                };

                // 當點擊鼠標拖動彈出框, 擡起鼠標時
                document.onmouseup = function () {
                    // 清除彈出框的移動事件及自己
                    document.onmousemove = null;
                    document.onmouseup = null;
                };

                // 阻止默認事件, 若是不加這個阻止默認事件, 在firefox下會有一個獲取焦點的光標一直在閃動, 在3.0及如下會出現拖動出現重影的狀況
                return false;
            };


        };
    </script>
</head>
<body>
    <!-- 假設這個DIV就是一個登陸窗口 -->
    <div id="dialog">
        <div id="dialog-title" class="dialog-title clearfix">
            <span class="fl-l login">登陸</span>
            <span class="fl-r close">X</span>
        </div>
    </div>
</body>
</html>
以上就是以一個簡單的DIV模擬 登陸窗口 實現的一個簡單拖拽過程

JavaScript實現登陸窗口的拖拽具體效果

JavaScript實現登陸窗口的拖拽具體效果

你可能已經發現了這個 登陸窗口網易雲音樂 最大的區別在於它能夠拖拽出文檔可視區域, 它甚至能夠拖拽到文檔不可見區域, 那就永遠拖不回了, 就像那已分手並結婚了的前女朋友永遠也回不來同樣

就像那已分手並結婚了的前女朋友永遠也回不來同樣

(好了, 找一沒人的地哭暈在廁所好了 是否是同時又想起來那幾句話[得不到的永遠在騷動, 失去的永遠在懷念, 身邊的永遠成爲風景, ......]), 以上是逗逼時刻就當沒發生同樣好了

其實這也是上面留的一個坑, 如今來優化填坑, 就是實現和 網易雲音樂 網站 登陸窗口 同樣的效果, 禁止 登陸窗口 拖拽出文檔可視區之外! 下面是填坑時刻, 非戰鬥人員請火速離開現場 學習

JavaScript實現登陸窗口的拖拽優化填坑

其實思路很簡單就是當 登陸窗口 的四個邊和文檔窗口的其一邊界重合時, 就讓 登陸窗口 的那一個邊的外邊距值與重合的文檔那一個邊的值相等, 那這個事情就妥妥的搞定了!
  • 只須要修改 documnet.onmousemove 事件方法 登陸窗口 當前位置便可!
// 當鼠標移動時改變彈出框的位置
oDialog.style.left = oEvent.clientX - iDisX + 'px';
oDialog.style.top = oEvent.clientY - iDisY + 'px';
  • 改寫以下:
// 優化填坑 - 禁止 `登陸窗口` 拖拽出文檔可視區域, 保存 `登陸窗口` 在文檔中具體位置
var iCurrentDialogDisLift = oEvent.clientX - iDisX; // `登陸窗口` 當前位置於X軸具體值
var iCurrentDialogDisTop = oEvent.clientY - iDisY;  // `登陸窗口` 當前位置於Y軸具體值

// 檢測當前 `登陸窗口` X軸是否位於文檔可視區域最左側或最右側
if ( iCurrentDialogDisLift < 0 ) {
    iCurrentDialogDisLift = 0;
} else if ( iCurrentDialogDisLift > document.documentElement.clientWidth - oDialog.offsetWidth  ) {
    // 當前文檔X軸可視區域大小包括左右邊框線寬度 - `登陸窗口` X軸區域大小包括左右邊框線寬度
    iCurrentDialogDisLift = document.documentElement.clientWidth - oDialog.offsetWidth;
}

// 檢測當前 `登陸窗口` Y軸是否位於文檔可視區域最上端或最下端
if ( iCurrentDialogDisTop < 0 ) {
    iCurrentDialogDisTop = 0;
} else if ( iCurrentDialogDisTop > document.documentElement.clientHeight - oDialog.offsetHeight ) {
    // 當前文檔Y軸可視區域大小包括上下邊框線寬度 - `登陸窗口` Y軸區域大小包括上下邊框線寬度
    iCurrentDialogDisTop = document.documentElement.clientHeight - oDialog.offsetHeight;
}

// 當鼠標移動時改變彈出框的位置
oDialog.style.left = iCurrentDialogDisLift + 'px';
oDialog.style.top = iCurrentDialogDisTop + 'px';

JavaScript實現登陸窗口的拖拽優化填坑全文檔

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS拖拽</title>
    <style>
        /* public style start */
        * { margin: 0; padding: 0; }
        body, textarea, select, input, button {font-size: 12px;color: white;font-family: Arial, Helvetica, sans-serif;-webkit-text-size-adjust: none;}
        .fl-l { float: left }
        .fl-r { float: right }
        .clearfix:after {visibility:hidden;display:block;font-size:0;content:'.';clear:both;height:0;}
        .clearfix {*zoom:1;}
        /* public style end */

        /* dialog window style start */

        /* dialog window main */
        #dialog {
            height: 200px;
            width: 400px;
            background-color: #EAF6DB;
            border: 1px solid lightslategray;
            position: absolute;
            top: 200px;
            left: 400px;
        }

        /* dialog window title section */
        .dialog-title {
            height: 40px;
            line-height: 40px;
            background-color: #3a3333;
            cursor: move;
        }

        /* dialog window title inner detail */
        .login, .close {
            display: inline-block;
            margin: 0 15px;
        }
    </style>
    <script>
        window.onload = function () {
            // 獲取DOM元素
            var oDialog_title = document.getElementById( 'dialog-title' );  // 容許點擊鼠標獲取拖拽事件區域
            var oDialog = oDialog_title.parentNode; // 登陸窗口

            // 初始化鼠標默認位置
            var iDisX = 0;
            var iDisY = 0;

            // 爲獲取到的DOM元素添加鼠標按下事件 `onmousedown`
            oDialog_title.onmousedown = function ( ent ) {
                // 保存鼠標事件對象
                var oEvent = ent || event;

                // 距離計算鼠標位於彈出框內的位置
                iDisX = oEvent.clientX - oDialog.offsetLeft;    // 鼠標X軸位置 - 彈出框X外左邊距
                iDisY = oEvent.clientY - oDialog.offsetTop;     // 鼠標Y軸位置 - 彈出框Y外上邊距

                // 點擊彈出框後拖動鼠標, 移動彈出框
                document.onmousemove = function ( ent ) {
                    // 保存鼠標事件對象
                    var oEvent = ent || event;

                    // 優化填坑 - 禁止 `登陸窗口` 拖拽出文檔可視區域, 保存 `登陸窗口` 在文檔中具體位置
                    var iCurrentDialogDisLift = oEvent.clientX - iDisX; // `登陸窗口` 當前位置於X軸具體值
                    var iCurrentDialogDisTop = oEvent.clientY - iDisY;  // `登陸窗口` 當前位置於Y軸具體值

                    // 檢測當前 `登陸窗口` X軸是否位於文檔可視區域最左側或最右側
                    if ( iCurrentDialogDisLift < 0 ) {
                        iCurrentDialogDisLift = 0;
                    } else if ( iCurrentDialogDisLift > document.documentElement.clientWidth - oDialog.offsetWidth  ) {
                        // 當前文檔X軸可視區域大小包括左右邊框線寬度 - `登陸窗口` X軸區域大小包括左右邊框線寬度
                        iCurrentDialogDisLift = document.documentElement.clientWidth - oDialog.offsetWidth;
                    }

                    // 檢測當前 `登陸窗口` Y軸是否位於文檔可視區域最上端或最下端
                    if ( iCurrentDialogDisTop < 0 ) {
                        iCurrentDialogDisTop = 0;
                    } else if ( iCurrentDialogDisTop > document.documentElement.clientHeight - oDialog.offsetHeight ) {
                        // 當前文檔Y軸可視區域大小包括上下邊框線寬度 - `登陸窗口` Y軸區域大小包括上下邊框線寬度
                        iCurrentDialogDisTop = document.documentElement.clientHeight - oDialog.offsetHeight;
                    }

                    // 當鼠標移動時改變彈出框的位置
                    oDialog.style.left = iCurrentDialogDisLift + 'px';
                    oDialog.style.top = iCurrentDialogDisTop + 'px';
                };

                // 當點擊鼠標拖動彈出框, 擡起鼠標時
                document.onmouseup = function () {
                    // 清除彈出框的移動事件及自己
                    document.onmousemove = null;
                    document.onmouseup = null;
                };

                // 阻止默認事件, 若是不加這個阻止默認事件, 在firefox下會有一個獲取焦點的光標一直在閃動, 在3.0及如下會出現拖動出現重影的狀況
                return false;
            };
        };
    </script>
</head>
<body>
    <!-- 假設這個DIV就是一個登陸窗口 -->
    <div id="dialog">
        <div id="dialog-title" class="dialog-title clearfix">
            <span class="fl-l login">登陸</span>
            <span class="fl-r close">X</span>
        </div>
    </div>
</body>
</html>

JavaScript實現登陸窗口的拖拽優化填坑後具體效果

JavaScript實現登陸窗口的拖拽優化填坑後具體效果

文章寫到這裏可能也有不夥伴說了, 滾動一小段距離也就是出現滾動條時, 再拖拽仍是會出現 登陸窗口 脫離可視區域的狀況!

對於小夥伴提出的問題至少有二種解決方案!優化

  • 添加滾動距離計算, 當頁面滾動後實時讓 登陸窗口 位於正中間而且只容許在可視區域拖拽
  • 添加模態框, 就是當出現 登陸窗口 時禁止滾動
下面來一個一個的說:

JavaScript實現登陸窗口的拖拽優化填坑 - 滾動距離計算

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS拖拽擴展-計算滾動距離</title>
    <style>
        /* public style start */
        * { margin: 0; padding: 0; }
        body, textarea, select, input, button {font-size: 12px;color: white;font-family: Arial, Helvetica, sans-serif;-webkit-text-size-adjust: none;}
        .fl-l { float: left }
        .fl-r { float: right }
        .clearfix:after {visibility:hidden;display:block;font-size:0;content:'.';clear:both;height:0;}
        .clearfix {*zoom:1;}
        /* public style end */

        /* dialog window style start */

        /* dialog window main */
        #dialog {
            height: 200px;
            width: 400px;
            background-color: #EAF6DB;
            border: 1px solid lightslategray;
            position: absolute;
        }

        /* dialog window title section */
        .dialog-title {
            height: 40px;
            line-height: 40px;
            background-color: #3a3333;
            cursor: move;
        }

        /* dialog window title inner detail */
        .login, .close {
            display: inline-block;
            margin: 0 15px;
        }
    </style>
    <script>
        window.onload = function () {
            // 獲取DOM元素
            var oDialog_title = document.getElementById( 'dialog-title' );  // 容許點擊鼠標獲取拖拽事件區域
            var oDialog = oDialog_title.parentNode; // 登陸窗口

            // 初始化 `登陸窗口` 默認居中位置 start

            // 獲取當前文檔可視區寬度和高度
            var iScreenHeight = document.documentElement.clientHeight;  // 可視區高度
            var iScreenWidth = document.documentElement.clientWidth;  // 可視區寬度

            // 獲取當前 `登陸窗口` 寬度和高度
            var iCurrentDialogHeight = oDialog.offsetHeight;    // 窗口高度
            var iCurrentDialogWidth = oDialog.offsetWidth;    // 窗口寬度

            // 計算保存初始化後 `登陸窗口` 的默認垂直水平居中位置
            var iCurrentDialogTop = parseInt( ( iScreenHeight / 2 ) - ( iCurrentDialogHeight / 2 ) );
            var iCurrentDialogLeft = parseInt( ( iScreenWidth / 2 ) - ( iCurrentDialogWidth / 2 ) );

            // 修改 `登陸窗口` 默認位置
            oDialog.style.top = iCurrentDialogTop + 'px';
            oDialog.style.left = iCurrentDialogLeft + 'px';

            // 初始化 `登陸窗口` 默認居中位置 end


            // 初始化當前滾動條滾動距離
            var iCurrentScrollTop = 0;
            // 初始化當前 `登陸窗口` 應該滾動距離
            var iRealTimeTop = 0;

            // 計算當前滾動條滾動距離
            window.onscroll = function () {
                // 兼容寫法獲取當前滾動條滾動距離
                iCurrentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;

                // 直接這麼改變top值, 會出現抖動, 這樣感受太生硬, 體驗也不太好
                // oDialog.style.top = iCurrentDialogTop + iCurrentScrollTop + 'px';

                // 當前 `登陸窗口` 應該實時滾動距離
                iRealTimeTop = iCurrentDialogTop + iCurrentScrollTop;

                // 爲了防止出現抖動, 這樣感受也不太生硬, 優化體驗能夠採用緩衝運動方式
                fnBufferMotion( oDialog, iRealTimeTop );
            };

            // 初始化鼠標默認位置
            var iDisX = 0;
            var iDisY = 0;

            // 爲獲取到的DOM元素添加鼠標按下事件 `onmousedown`
            oDialog_title.onmousedown = function ( ent ) {
                // 保存鼠標事件對象
                var oEvent = ent || event;

                // 距離計算鼠標位於彈出框內的位置
                iDisX = oEvent.clientX - oDialog.offsetLeft;    // 鼠標X軸位置 - 彈出框X外左邊距
                iDisY = oEvent.clientY + iCurrentDialogTop - oDialog.offsetTop;     // 鼠標Y軸位置 + 當前滾動條滾動距離 - 彈出框Y外上邊距

                console.log( iCurrentDialogTop );

                // 點擊彈出框後拖動鼠標, 移動彈出框
                document.onmousemove = function ( ent ) {
                    // 保存鼠標事件對象
                    var oEvent = ent || event;

                    // 優化填坑 - 禁止 `登陸窗口` 拖拽出文檔可視區域, 保存 `登陸窗口` 在文檔中具體位置
                    var iCurrentDialogDisLift = oEvent.clientX - iDisX; // `登陸窗口` 當前位置於X軸具體值
                    var iCurrentDialogDisTop = oEvent.clientY + iCurrentDialogTop - iDisY;  // `登陸窗口` 當前位置於Y軸具體值

                    // 檢測當前 `登陸窗口` X軸是否位於文檔可視區域最左側或最右側
                    if ( iCurrentDialogDisLift < 0 ) {
                        iCurrentDialogDisLift = 0;
                    } else if ( iCurrentDialogDisLift > document.documentElement.clientWidth - oDialog.offsetWidth  ) {
                        // 當前文檔X軸可視區域大小包括左右邊框線寬度 - `登陸窗口` X軸區域大小包括左右邊框線寬度
                        iCurrentDialogDisLift = document.documentElement.clientWidth - oDialog.offsetWidth;
                    }

                    // 檢測當前 `登陸窗口` Y軸是否位於文檔可視區域 + 當前滾動條滾動距離 的最上端或最下端
                    if ( iCurrentDialogDisTop < iCurrentScrollTop ) {
                        iCurrentDialogDisTop = iCurrentScrollTop;
                    } else if ( iCurrentDialogDisTop > document.documentElement.clientHeight + iCurrentScrollTop - oDialog.offsetHeight ) {
                        // 當前文檔Y軸可視區域大小包括上下邊框線寬度 + 當前滾動條滾動距離 - `登陸窗口` Y軸區域大小包括上下邊框線寬度
                        iCurrentDialogDisTop = document.documentElement.clientHeight + iCurrentScrollTop - oDialog.offsetHeight;
                    }

                    // 當鼠標移動時改變彈出框的位置
                    oDialog.style.left = iCurrentDialogDisLift + 'px';
                    oDialog.style.top = iCurrentDialogDisTop + 'px';
                };

                // 當點擊鼠標拖動彈出框, 擡起鼠標時
                document.onmouseup = function () {
                    // 清除彈出框的移動事件及自己
                    document.onmousemove = null;
                    document.onmouseup = null;
                };

                // 阻止默認事件, 若是不加這個阻止默認事件, 在firefox下會有一個獲取焦點的光標一直在閃動, 在3.0及如下會出現拖動出現重影的狀況
                return false;
            };
        };

        // 初始化定義器
        var oTimer = null;
        /**
         * 緩衝運動
         * @param oElement   運動對象
         * @param iTarget   目標位置或者說目標點
         */
        function fnBufferMotion( oElement, iTarget ) {
            // 首先就是清除定時器, 由於當下面開啓定時器以前禁止存在還有再執行的定時器, 要否則會存在問題, 你能夠驗證一下
            clearInterval( oTimer );
            // 開啓定時器
            oTimer = setInterval( function () {

                // 緩衝運動速度
                var iSpeed = ( iTarget - oElement.offsetTop ) / 8;

                // 緩衝運動速度取整
                iSpeed = iSpeed > 0 ? Math.ceil( iSpeed ) : Math.floor( iSpeed );

                // 判斷是否緩衝運動到目標點, 是就關閉定時器, 不然就接着運動唄
                if ( iTarget == oElement.offsetTop ) {
                    clearInterval( oTimer );    // 關閉定時器
                } else {
                    oElement.style.top = oElement.offsetTop + iSpeed + 'px';    // 緩衝運動改變 `登陸窗口` 的上外邊距
                }
            }, 30 );
        }
    </script>
</head>
<body style="height: 4000px;">
    <!-- 假設這個DIV就是一個登陸窗口 -->
    <div id="dialog">
        <div id="dialog-title" class="dialog-title clearfix">
            <span class="fl-l login">登陸</span>
            <span class="fl-r close">X</span>
        </div>
    </div>
</body>
</html>

JavaScript實現登陸窗口的拖拽優化填坑 - 滾動距離計算效果

JavaScript實現登陸窗口的拖拽優化填坑 - 滾動距離計算效果

至於添加模態框, 就是當出現 登陸窗口 時禁止滾動這種方法你能夠試着實現一下!

Github: JavaScript實現【網易雲音樂Web站登陸窗口】拖拽功能網站

以上就是實現與 網易雲音樂 Web站 登陸窗口 拖拽效果一致的具體過程

但願本文對你的工做和學習有所幫助spa

若是以爲還不錯怎麼感謝我呢? 媽呀! 點贊啊!firefox

Good Luck! from warnerwu at 2017.08.19 AM3d

相關文章
相關標籤/搜索