1. 程式人生 > >自定義web彈窗/層:簡易風格的msg與可拖放的dialog

自定義web彈窗/層:簡易風格的msg與可拖放的dialog

  前言

  做過web專案開發的人對layer彈層元件肯定不陌生,作為layUI的一個重要元件,使用簡單、介面引數豐富,功能健壯,深受廣大開發者的喜愛,作為一個熱(經)愛(常)工(劃)作(水),喜歡鑽研探索技術的程式設計師(狗),我們自己來實現一個web彈窗/層,一窺layer的本源(/手動滑稽臉),進步,從模仿開始。

  首先,模仿layer的部分功能,我們先確定下我們要實現的功能:

  msg

  1、居中彈出、延時銷燬、自適應寬高、支援引數配置

  dialog

  1、可拖動

  2、可最小化、最大化、關閉

  3、右下角可對視窗進行縮放

  4、支援多個引數配置、擴充套件

 

  程式碼編寫

  大部分的思路都在程式碼註釋裡

  css樣式

        /* web彈窗 */
        .tip-msg {
            background-color: rgba(61, 61, 61, 0.93);
            color: #ffffff;
            opacity: 0;
            max-width: 200px;
            position: absolute;
            text-align: center;
            line-height
: 25px; border-radius: 30px; padding: 5px 15px; display: inline-block; } .tip-shade { z-index: 9999; background-color: rgb(0, 0, 0); opacity: 0.6; position: fixed; top: 0; left
: 0; width: 100%; height: 100%; } .tip-dialog { z-index: 10000; position: absolute; display: block; background: #e9e9e9; border-radius: 5px; opacity: 0; border: 1px solid #dad8d8; box-shadow: 0px 1px 20px 2px rgb(255, 221, 221); } .tip-title { cursor: move; padding: 5px; position: relative; height: 25px; border-bottom: 1px solid #dad8d8; user-select: none; } .tip-title-text { margin: 0; padding: 0; font-size: 15px; } .tip-title-btn { position: absolute; top: 5px; right: 5px; } .tip-content { padding: 8px; position: relative; word-break: break-all; font-size: 14px; overflow-x: hidden; overflow-y: auto; } .tip-resize { position: absolute; width: 15px; height: 15px; right: 0; bottom: 0; cursor: se-resize; }

  js

    /**
     * 自定義web彈窗/層:簡易風格的msg與可拖放的dialog
     * 依賴jquery
     */
    var tip = {

        /**
         * 初始化
         */
        init: function () {
            var titleDiv = null;//標題元素
            var dialogDiv = null;//視窗元素
            var titleDown = false;//是否在標題元素按下滑鼠
            var resizeDown = false;//是否在縮放元素按下滑鼠
            var offset = {x: 0, y: 0};//滑鼠按下時的座標系/計算後的座標
            /*
                使用 on() 方法新增的事件處理程式適用於當前及未來的元素(比如由指令碼建立的新元素)。
                問題:事件繫結在div上出現div移動速度跟不上滑鼠速度,導致滑鼠移動太快時會脫離div,從而無法觸發事件。
                解決:把事件繫結在document文件上,無論滑鼠在怎麼移動,始終是在文件範圍之內。
            */
            //滑鼠在標題元素按下
            $(document).on("mousedown", ".tip-title", function (e) {
                var event1 = e || window.event;
                titleDiv = $(this);
                dialogDiv = titleDiv.parent();
                titleDown = true;
                offset.x = e.clientX - parseFloat(dialogDiv.css("left"));
                offset.y = e.clientY - parseFloat(dialogDiv.css("top"));
            });
            //滑鼠移動
            $(document).on("mousemove", function (e) {
                var event2 = e || window.event;
                var eveX = event2.clientX;             // 獲取滑鼠相對於瀏覽器x軸的位置
                var eveY = event2.clientY;             // 獲取滑鼠相對於瀏覽器Y軸的位置
                // var height = document.body.clientHeight;//表示HTML文件所在視窗的當前高度;
                // var width = document.body.clientWidth;//表示HTML文件所在視窗的當前寬度;
                var height = window.innerHeight;//瀏覽器視窗的內部高度;
                var width = window.innerWidth;//瀏覽器視窗的內部寬度;

                //在標題元素按下
                if (titleDown) {

                    //處理滾動條
                    if (tip.hasXScrollbar()) {
                        height = height - tip.getScrollbarWidth();
                    }
                    if (tip.hasYScrollbar()) {
                        width = width - tip.getScrollbarWidth();
                    }

                    //上邊
                    var top = (eveY - offset.y);
                    if (top <= 0) {
                        top = 0;
                    }
                    if (top >= (height - dialogDiv.height())) {
                        top = height - dialogDiv.height() - 5;
                    }

                    //左邊
                    var left = (eveX - offset.x);
                    if (left <= 0) {
                        left = 0;
                    }
                    if (left >= (width - dialogDiv.width())) {
                        left = width - dialogDiv.width() - 5;
                    }
                    dialogDiv.css({
                        "top": top + "px",
                        "left": left + "px"
                    });
                }

                //在縮放元素按下
                if (resizeDown) {
                    var newWidth = (dialogDiv.resize.width + (eveX - offset.x));
                    if (dialogDiv.resize.initWidth >= newWidth) {
                        newWidth = dialogDiv.resize.initWidth;
                    }
                    var newHeight = (dialogDiv.resize.height + (eveY - offset.y));
                    if (dialogDiv.resize.initHeight >= newHeight) {
                        newHeight = dialogDiv.resize.initHeight;
                    }

                    dialogDiv.css("width", newWidth + "px");
                    dialogDiv.find(".tip-content").css("height", newHeight + "px");
                }
            });
            //滑鼠彈起
            $(document).on("mouseup", function (e) {
                //清空物件
                titleDown = false;
                resizeDown = false;
                titleDiv = null;
                dialogDiv = null;
                offset = {x: 0, y: 0};
            });
            //阻止按鈕事件冒泡
            $(document).on("mousedown", ".tip-title-min,.tip-title-max,.tip-title-close", function (e) {
                e.stopPropagation();//阻止事件冒泡
            });
            //最小化
            $(document).on("click", ".tip-title-min", function (e) {
                // var height = document.body.clientHeight;//表示HTML文件所在視窗的當前高度;
                // var width = document.body.clientWidth;//表示HTML文件所在視窗的當前寬度;
                var height = window.innerHeight;//瀏覽器視窗的內部高度;
                var width = window.innerWidth;//瀏覽器視窗的內部寬度;
                var $parent = $(this).parents(".tip-dialog");
                //當前是否為最大化
                if ($parent[0].isMax) {
                    $parent[0].isMax = false;
                    $parent.css({
                        "top": $parent[0].topMin,
                        "left": $parent[0].leftMin,
                        "height": $parent[0].heightMin,
                        "width": $parent[0].widthMin
                    });
                }
                //當前是否為最小化
                if (!$parent[0].isMin) {
                    $parent[0].isMin = true;
                    $parent[0].bottomMin = $parent.css("bottom");
                    $parent[0].leftMin = $parent.css("left");
                    $parent[0].heightMin = $parent.css("height");
                    $parent[0].widthMin = $parent.css("width");
                    $parent.css({
                        "top": "",
                        "bottom": "5px",
                        "left": 0,
                        "height": "30px",
                        "width": "95px"
                    });
                    $parent.find(".tip-title-text").css("display", "none");
                    $parent.find(".tip-content").css("display", "none");
                } else {
                    $parent[0].isMin = false;
                    $parent.css({
                        "top": $parent[0].topMin,
                        "bottom": $parent[0].bottomMin,
                        "left": $parent[0].leftMin,
                        "height": $parent[0].heightMin,
                        "width": $parent[0].widthMin
                    });
                    $parent.find(".tip-title-text").css("display", "block");
                    $parent.find(".tip-content").css("display", "block");
                }
            });
            //最大化
            $(document).on("click", ".tip-title-max", function (e) {
                // var height = document.body.clientHeight;//表示HTML文件所在視窗的當前高度;
                // var width = document.body.clientWidth;//表示HTML文件所在視窗的當前寬度;
                var height = window.innerHeight;//瀏覽器視窗的內部高度;
                var width = window.innerWidth;//瀏覽器視窗的內部寬度;
                var $parent = $(this).parents(".tip-dialog");
                //當前是否為最小化
                if ($parent[0].isMin) {
                    $parent[0].isMin = false;
                    $parent.css({
                        "top": $parent[0].topMin,
                        "bottom": $parent[0].bottomMin,
                        "left": $parent[0].leftMin,
                        "height": $parent[0].heightMin,
                        "width": $parent[0].widthMin
                    });
                    $parent.find(".tip-title h2").css("display", "block");
                }
                //當前是否為最大化
                if (!$parent[0].isMax) {
                    $parent[0].isMax = true;
                    $parent[0].topMin = $parent.css("top");
                    $parent[0].leftMin = $parent.css("left");
                    $parent[0].heightMin = $parent.css("height");
                    $parent[0].widthMin = $parent.css("width");
                    $parent.css({
                        "top": 0,
                        "left": 0,
                        "height": height - 5 + "px",
                        "width": width - 5 + "px"
                    });
                } else {
                    $parent[0].isMax = false;
                    $parent.css({
                        "top": $parent[0].topMin,
                        "left": $parent[0].leftMin,
                        "height": $parent[0].heightMin,
                        "width": $parent[0].widthMin
                    });
                }
            });
            //縮放
            $(document).on("mousedown", ".tip-resize", function (e) {
                var event1 = e || window.event;
                dialogDiv = $(this).parent();
                resizeDown = true;
                offset.x = e.clientX;
                offset.y = e.clientY;
                //點選時的寬高
                dialogDiv.resize.width = dialogDiv.width();
                dialogDiv.resize.height = dialogDiv.find(".tip-content").height();
            });
            //關閉
            $(document).on("click", ".tip-title-close", function (e) {
                $(this).parents(".tip-dialog").parent().remove();
            });
        },

        /**
         * 是否存在X軸方向滾動條
         */
        hasXScrollbar: function () {
            return document.body.scrollWidth > (window.innerWidth || document.documentElement.clientWidth);
        },

        /**
         * 是否存在Y軸方向滾動條
         */
        hasYScrollbar: function () {
            return document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);
        },

        /**
         * 計算滾動條的寬度
         */
        getScrollbarWidth: function () {
            /*
                思路:生成一個帶滾動條的div,分析得到滾動條長度,然後過河拆橋
             */
            var scrollDiv = document.createElement("div");
            scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
            document.body.appendChild(scrollDiv);
            var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
            document.body.removeChild(scrollDiv);

            return scrollbarWidth;

        },

        /**
         * tip提示
         * tip.msg("哈哈哈哈哈");
         * tip.msg({text:"哈哈哈哈哈",time:5000});
         */
        msg: function (setting) {
            var time = setting.time || 2000; // 顯示時間(毫秒) 預設延遲2秒關閉
            var text = setting.text || setting; // 文字內容

            //組裝HTML
            var tip = "<div class='tip tip-msg'>"
                + text +
                "</div>";

            //刪除舊tip
            $(".tip-msg").remove();

            //新增到body
            $("body").append(tip);

            //獲取jq物件
            var $tip = $(".tip-msg");

            //動畫過渡
            $tip.animate({opacity: 1}, 500);

            //計算位置瀏覽器視窗上下、左右居中
            // var height = document.body.clientHeight;//表示HTML文件所在視窗的當前高度;
            var width = document.body.clientWidth;//表示HTML文件所在視窗的當前寬度;
            var height = window.innerHeight;//瀏覽器視窗的內部高度;
            // var width = window.innerWidth;//瀏覽器視窗的內部寬度;
            width = ((width / 2) - ($tip.css("width").replace("px", "") / 2)) / width;
            height = ((height / 2) - ($tip.css("height").replace("px", "") / 2)) / height;
            $tip.css({
                "top": (height * 100) + "%",
                "left": (width * 100) + "%"
            });

            //延遲刪除
            setTimeout(function () {
                //動畫過渡
                $tip.animate({opacity: 0}, 500, function () {
                    $tip.remove();
                });
            }, time);
        },

        /**
         * 可拖放視窗
         * tip.dialog({title:"測試彈窗標題",content:"測試彈窗內容"});
         * tip.dialog({title:"測試彈窗標題",content:"<h1>測試彈窗內容</h1>",offset: ['100px', '50px'],area:["200px","100px"],shade:0});
         */
        dialog: function (setting) {
            var title = setting.title || "這裡是標題"; // 標題
            var content = setting.content || "這裡是內容"; // 內容
            var area = setting.area || ['393px', '222px']; // 寬高
            var offset = setting.offset || "auto" // 位置 上、左
            var shade = setting.shade != undefined ? setting.shade : 0.7;//遮陰 為0時無遮陰物件

            //組裝HTML
            var tip = "<div>\n" +
                "    <!-- 遮陰層 -->\n" +
                "    <div class=\"tip tip-shade\"></div>\n" +
                "    <!-- 主體 -->\n" +
                "    <div class=\"tip tip-dialog\">\n" +
                "        <!-- 標題 -->\n" +
                "        <div class=\"tip tip-title\">\n" +
                "            <h2 class=\"tip tip-title-text\"></h2>\n" +
                "            <div class=\"tip tip-title-btn\">\n" +
                "                <button class=\"tip tip-title-min\" title=\"最小化\">--</button>\n" +
                "                <button class=\"tip tip-title-max\" title=\"最大化\">O</button>\n" +
                "                <button class=\"tip tip-title-close\" title=\"關閉\">X</button>\n" +
                "            </div>\n" +
                "        </div>\n" +
                "        <!-- 視窗內容 -->\n" +
                "        <div class=\"tip tip-content\"></div>\n" +
                "        <!-- 右下角改變視窗大小 -->\n" +
                "        <div class=\"tip tip-resize\"></div>\n" +
                "    </div>\n" +
                "</div>";

            var $tip = $(tip);

            //新增到body
            $("body").append($tip)

            //設定遮陰
            $tip.find(".tip-shade").css("opacity", shade);
            if(shade == 0){
                $tip.find(".tip-shade").css({
                    "width":"0",
                    "height":"0"
                });
            }

            //獲取dialog物件
            $tip = $tip.find(".tip-dialog");

            //標題
            $tip.find(".tip-title-text").text(title);

            //內容
            $tip.find(".tip-content").append(content);

            //設定初始寬高
            $tip.css({
                "width": area[0],
            });
            $tip.find(".tip-content").css({
                "height": area[1]
            });

            //動畫過渡
            $tip.animate({opacity: 1}, 500);

            //計算位置瀏覽器視窗上下、左右居中
            if (offset === "auto") {
                // var height = document.body.clientHeight;//表示HTML文件所在視窗的當前高度;
                var width = document.body.clientWidth;//表示HTML文件所在視窗的當前寬度;
                var height = window.innerHeight;//瀏覽器視窗的內部高度;
                // var width = window.innerWidth;//瀏覽器視窗的內部寬度;
                width = ((width / 2) - ($tip.css("width").replace("px", "") / 2)) / width;
                height = ((height / 2) - ($tip.css("height").replace("px", "") / 2)) / height;
                $tip.css({
                    "top": (height * 100) + "%",
                    "left": (width * 100) + "%"
                });
            } else if (Array.isArray(offset)) {
                $tip.css({
                    "top": offset[0],
                    "left": offset[1]
                });
            }

            //初始值寬高
            $tip.resize.initWidth = $tip.width();
            $tip.resize.initHeight = $tip.find(".tip-content").height();
        }
    };

  呼叫

    //初始化
    tip.init();

    // tip提示
    tip.msg("哈哈哈哈哈");
    tip.msg({text:"哈哈哈哈哈",time:5000});

    //可拖放視窗
    tip.dialog({title:"測試彈窗標題",content:"測試彈窗內容"});
    tip.dialog({title:"測試彈窗標題",content:"<h1>測試彈窗內容</h1>",offset: ['100px', '50px'],area:["200px","100px"],shade:0});

 

  效果演示

  msg

  彈出、銷燬有動畫效果,設定了一個max-width,超出會換行,支援引數配置

  

  dialog

  可拖動、可最小化、最大化、關閉,右下角可進行縮放,支援引數配置,發光特效

 

  總結

  實現拖拽、縮放功能,主要是監聽了document的mousedown,mousemove,mouseup事件,並對特殊元素進行特殊處理,一波騷操作,玩出了花(滑稽),從而實現我們想要的效果。

  參考:

  http://layer.layui.com/

  http://www.17sucai.com/pins/demo-show?id=4218

  https://www.cnblogs.com/nzbin/p/8117535.html