弹窗插件封装 js插件封装-弹窗组件封装-类型判断-发布订阅

https://gitee.com/eric167/js---

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/modalplugin.css">
      <style>
          .box .form{
              height: 40px;
              display:flex;
              justify-content:flex-start;
              align-items:center;
          }

          .box .form span{
            margin-right:3%;
            width:30%;
            text-align:right;
          }

          .box .form input{
              width: 100%;
              padding:0 5px;
              height:30px;
              box-sizing:border-box;
              border:1px solid #eee;
          }
      </style>  
</head>
<body>
    
    <!-- <div class="drag_modal"></div>
    <div class="drag_content">
        <div class="drag_head">
            系统温馨提示
            <a href="javascript:;" class="drag_close"></a>
        </div>
        <div class="drag_main">
            哈哈哈
        </div>
        <div class="drag_foot">
            <a href="javascript:;" class="drag_button">取消</a>
            <a href="javascript:;" class="drag_button">确定</a>
        </div>
    </div> -->

    
    <button id="btn">点我</button>
    <script src="./webpack/dist/modalplugin.min.js"></script>

    <script>
        console.log(window);
        btn.onclick = function(){
            // let m1 = M({ 
            //     drag:true,  // 是否开启拖拽 默认开启
            //     title:"登录",
            //     template :  `<div class="box">
            //         <div class="form">
            //             <span>用户名:</span>
            //             <input type="text" id="username"/> 
            //         </div> 
            //         <div class="form">
            //             <span>密码:</span>
            //             <input type="password" id="password"/> 
            //         </div>
            //     </div>`,
            //     buttons:[
            //         {
            //             text:'取消',
            //             click(self){
            //                 console.log(self);
            //                 self.close();
            //             }
            //         },{
            //             text:'确定',
            //             click(self){
            //                 let username = document.querySelector('#username'),
            //                     password = document.querySelector('#password'); 
            //                 console.log(username.value,password.value);
            //                 self.close();
            //             }   
            //         }
            //     ]
            // })


            let m2 = M({ 
                template:"实现了插件组件封装",
                title:"提示", 
                buttons:[
                    {
                        text:'确定',
                        click(self){  
                            self.close();
                        }   
                    }
                ],
                // 周期函数 [回调函数,用户需要处理的事情都要写在两个回调函数当中]
                onopen:self=>{
                    console.log('已打开',self);
                },
                onclose(){
                    console.log('已关闭',this);
                }
            })

            // 周期函数 发布订阅,可以灵活的给某一个事件订阅自己要执行的方法
            m2.ondragstart.on(self=>{
                console.log('拖拽开始');
            })  
            m2.ondraging.on(self=>{
                console.log('拖拽中');
            }) 
            m2.ondragend.on(self=>{
                console.log('拖拽结束');
            }) 
        }
    </script>
</body>
</html>
 
------------------------------
   
/*
 * 插件支持的配置信息(基于不同的配置信息,实现不同的功能): 
 *    + title[string] 标题
 *    + template[string] 自定义的内容或者模板(基于ES6的模板字符串,拼接更丰富的内容结构)
 *    + buttons[array] 自定义按钮(组)
 *      [{text:'确定',click:[callback]},...]
 *    + modal[boolean] 控制遮罩层是否显示 默认是true
 *    + drag[boolean] 是否允许拖拽 默认是true
 *    + onopen[function] 打开
 *    + onclose[function] 关闭
 * 拖拽的生命周期函数「当前操作的某个节点上,允许用户自定义处理的事情」
 *   「发布订阅」
 *    + 拖拽开始 ondragstart
 *    + 拖拽中 ondraging
 *    + 拖拽结束 ondragend
 * 
 * // 发布订阅
 *  // 工具库
 */

import utils from './lib/utils'; 
import Sub from './lib/sub';


// 插件的核心
function ModalPlugin(config){

  let self = this;
  self.config = config;
  self.$drag_modal = null;
  self.$drag_content = null;

  self.startX = 0;
  self.startY = 0;

  self.startL = 0;
  self.startT = 0;

  self._MOVE = null;
  self._UP = null;

  // 如果开启拖拽 那就需要创建三个事件池
  if(self.config.drag){
      self.ondragstart = new Sub;
      self.ondraging = new Sub;
      self.ondragend = new Sub;
  }
  self.init();
}

ModalPlugin.prototype = {
    version:'1.0.0',
    constructor:ModalPlugin,
    init(){
        let self = this;
        self.create();

        // 基于事件委托实现 容器中元素点击处理的相关事务 关闭按钮 && 自定义按钮
        if(self.$drag_content){
            self.$drag_content.addEventListener('click',function(ev){
                let target = ev.target,
                    targetTag = target.tagName,
                    targetClass = target.className;
                // 关闭按钮
                if(targetTag === 'A' && targetClass === 'drag_close') {
                    self.close();
                    return ;
                }
                
                // 自定义按钮
                if(targetTag === 'A' && targetClass === 'drag_button') {
                    let index = +target.getAttribute('index'),
                        item = self.config.buttons[index];
                        // 判断 不为空 并且 是函数 就执行函数
                     if(item && utils.isFunction(item.click)){
                        item.click.call(self,self);
                     }
                    return ;
                }

            })
        }

        if(self.config.drag){
            // 开启拖拽
            let $drag_head = self.$drag_content.querySelector('.drag_head');
                $drag_head.style.cursor = 'move'; 
                $drag_head.addEventListener('mousedown',self.down.bind(self));
        }
 
    },
    // 动态创建结构
    create(){
        let self = this,
        config = self.config,
        fragment = document.createDocumentFragment(); 
        // 创建遮罩层
        if(config.modal){
            self.$drag_modal = document.createElement('div');
            self.$drag_modal.className = 'drag_modal';
            fragment.appendChild(self.$drag_modal);
        }

        // 创建内容
        self.$drag_content = document.createElement('div');
        self.$drag_content.className = 'drag_content'; 
        self.$drag_content.innerHTML = `
                <div class="drag_head">
                ${config.title}
                <a href="javascript:;" class="drag_close"></a>
            </div>
            <div class="drag_main">${config.template} </div>
                ${config.buttons.length > 0 ? 
                    ` <div class="drag_foot"> 
                        ${config.buttons.map((item,index) => {
                            return  `<a href="javascript:;" class="drag_button" index="${index}">${item.text}</a>`
                        }).join('')} 
                    </div> ` 
                 : ``} 
            `;

        fragment.appendChild(self.$drag_content);
        // 把动态元素添加到页面当中
        document.body.appendChild(fragment);
        fragment = null;

        // 控制元素显示
        self.$drag_content.offsetHeight; // 刷新渲染队列
        self.$drag_modal ?   self.$drag_modal.style.opacity = 1 : null;
        self.$drag_content ? self.$drag_content.style.opacity = 1 : null;

        // 改变盒子居中的方式
        let HTML = document.documentElement;
        self.$drag_content.style.left = `${(HTML.clientWidth - self.$drag_content.clientWidth)/2}px`;
        self.$drag_content.style.top = `${(HTML.clientHeight - self.$drag_content.clientHeight)/2}px`;
        self.$drag_content.style.transform = 'translate(0,0)';

        // 触发开发的周期函数
        self.config.onopen.call(self,self);
    },
    // 关闭弹窗
    close(){
        let self = this,
            body = document.body; 
        if(self.$drag_modal){
            self.$drag_modal.style.opacity = 0;
            // 动画结束之后移除元素
            self.$drag_modal.ontransitionend = () => {
                body.removeChild(self.$drag_modal);
                self.$drag_modal.ontransitionend = null;
            }
        }

        if(self.$drag_content){
            self.$drag_content.style.opacity = 0;
            // 动画结束之后移除元素
            self.$drag_content.ontransitionend = () => {
                body.removeChild(self.$drag_content);
                self.$drag_content.ontransitionend = null;
                // 触发关闭的周期函数
                self.config.onclose.call(self,self);
            }
        }
    },
    // 拖拽系列方法
    down(ev){
        let self = this;

        self.startX = ev.pageX;
        self.startY = ev.pageY;
 
        self.startL = self.$drag_content.offsetLeft;
        self.startT = self.$drag_content.offsetTop;
 
        self._MOVE = self.move.bind(self);
        self._UP = self.up.bind(self);
        document.addEventListener('mousemove',self._MOVE); 
        document.addEventListener('mouseup',self._UP); 
        // 通知事件池中的方法执行
        self.ondragstart.fire(self);
    },
    move(ev){
        let self = this,
            curL = ev.pageX - self.startX + self.startL,
            curT = ev.pageY - self.startY + self.startT;

            // 边界判断
            let HTML = document.documentElement,
                minL = 0,
                minT = 0,
                maxL = HTML.clientWidth - self.$drag_content.offsetWidth,
                maxT = HTML.clientHeight - self.$drag_content.offsetHeight; 

            curL = curL < minL ? minL : (curL > maxL ? maxL : curL);
            curT = curT < minT ? minT : (curT > maxT ? maxT : curT);

            self.$drag_content.style.left = `${curL}px`;
            self.$drag_content.style.top = `${curT}px`;  
        // 通知事件池中的方法执行
        self.ondraging.fire(self);
    },
    up(){
        let self = this;
        document.removeEventListener('mousemove',self._MOVE); 
        document.removeEventListener('mouseup',self._UP);  
         // 通知事件池中的方法执行
         self.ondragend.fire(self);
    }
}
 
// 暴露的API 
// 定义每一个参数规则
const props = {
    title:{
        type:'string',
        default:"系统温馨提示"
    },
    template:{
        type:'string',
        required: true 
    },
    buttons:{
        type:'array',
        default:[]
    },
    modal:{
        type:'boolean',
        default:true
    },
    drag:{
        type:'boolean',
        default:true
    },
    drag:{
        type:'boolean',
        default:true
    },
    onopen:{
        type:'function',
        default:()=>{}
    },
    onclose:{
        type:'function',
        default:()=>{}
    },
}

const proxyModal = function proxyModal(options){
    // 验证配置项
    !options || !utils.isPlainObject(options) ? options = {} : null;

    // console.log(options);
    let config = {};
    utils.each(props,(key,rule)=>{
        let opValue = options[key],
            {type,default:defaultValue,required} = rule; 
        // options 没有传递配置项 : 验证是否为必传 && 取参数默认值
        if(typeof opValue === 'undefined'){
            if(required) throw new TypeError(`${key} must be required`);
            config[key] = defaultValue
            return ;
        }
        //  options 有传递 key 这一项, 验证值的格式 && 取传进来的值
        if(utils.toType(opValue) !== type ) throw new TypeError(`${key} must be an ${type}`);
        // 对象合并
        config[key] =  utils.merge(defaultValue,opValue); 
    });
    
    return new ModalPlugin(config);
};

if(typeof window !== "undefined"){
    window.M = window.ModalPlugin = proxyModal;
}

if(typeof module === "object" && typeof module.exports === 'object') {
    module.exports = proxyModal;
}
   
-----------------------------------------
     sub.js 
 
function Sub() {
    this.pond = [];
}
Sub.prototype = {
    constructor: Sub,
    on(func) {
        let pond = this.pond;
        !pond.includes(func) ? pond.push(func) : null;
    },
    off(func) {
        let pond = this.pond;
        pond.forEach((item, index) => item === func ? pond[index] = null : null);
    },
    fire(...params) {
        let pond = this.pond;
        for (let i = 0; i < pond.length; i++) {
            let itemFunc = pond[i];
            if (typeof itemFunc !== "function") {
                pond.splice(i, 1);
                i--;
                continue;
            }
            itemFunc(...params);
        }
    }
};
export default Sub;
 
---------------------------
   
var getProto = Object.getPrototypeOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call(Object);

[
    "Boolean",
    "Number",
    "String",
    "Symbol",
    "Function",
    "Array",
    "Date",
    "RegExp",
    "Object",
    "Error"
].forEach(function (name) {
    class2type["[object " + name + "]"] = name.toLowerCase();
});

function toType(obj) {
    if (obj == null) {
        return obj + "";
    }
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[toString.call(obj)] || "object" :
        typeof obj;
}

function isPlainObject(obj) {
    var proto,
        Ctor,
        type = toType(obj);
    if (!obj || type !== "object") {
        return false;
    }
    proto = getProto(obj);
    if (!proto) {
        return true;
    }
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
}

function isFunction(obj) {
    return typeof obj === "function" && typeof obj.nodeType !== "number";
};

function isWindow(obj) {
    return obj != null && obj === obj.window;
};

function isArrayLike(obj) {
    var length = !!obj && "length" in obj && obj.length,
        type = toType(obj);
    if (isFunction(obj) || isWindow(obj)) {
        return false;
    }
    return type === "array" || length === 0 ||
        typeof length === "number" && length > 0 && (length - 1) in obj;
}

function merge(obj1, obj2) {
    let isPlain1 = isPlainObject(obj1),
        isPlain2 = isPlainObject(obj2);
    if (!isPlain1) return obj2;
    if (!isPlain2) return obj1;
    [
        ...Object.getOwnPropertyNames(obj2),
        ...Object.getOwnPropertySymbols(obj2)
    ].forEach(key => {
        obj1[key] = merge(obj1[key], obj2[key]);
    });
    return obj1;
}

function each(obj, callback) {
    var length, i = 0;
    if (isArrayLike(obj)) {
        length = obj.length;
        for (; i < length; i++) {
            if (callback.call(obj[i], i, obj[i]) === false) {
                break;
            }
        }
    } else {
        var keys = [
            ...Object.getOwnPropertyNames(obj),
            ...Object.getOwnPropertySymbols(obj)
        ];
        for (; i < keys.length; i++) {
            var key = keys[i],
                value = obj[key];
            if (callback.call(value, key, value) === false) {
                break;
            }
        }
    }
    return obj;
}

export default {
    toType,
    isPlainObject,
    isFunction,
    isWindow,
    isArrayLike,
    merge,
    each
};
utils.js -> 封装好的工具类-》js类型验证
posted @ 2021-08-04 18:00  13522679763-任国强  阅读(116)  评论(0)    收藏  举报