隨手寫的一個 DataV代碼 寫到一半寫不動了 棄坑!~

!(function(v, g){
    g["DataV"] || (g["DataV"] = v());
})(function(){
    
    const zoom = [0, 20, 40, 60, 80, 99];
    
    // 獲取唯一序列碼
    let xid_i = 0;
    const getXid = function(){
        xid_i++;
        return "xid_" + (xid_i);
    }
    
    const getInt = function(string){
        let i = parseInt(string);
        return isNaN(i) ? 0 : i;
    }
    
    // 初始化DataV對象
    const exports = {};
    
    // 初始化容器內容
    const createApp = exports.createApp = function(dom, opt){
        return new DataV(dom, opt);
    }
    
    // dataV 初始化配置
    const opt_def = {
        width: "100%",
        height: "100%"
    }
    
    // dataV容器對象
    const DataV = function(dom, opt){
        this.opt = Object.assign(opt_def, opt);
        this.dom = dom;
        this.plugin = []; // 容器內部組件
        this.domInit(this.dom);
        this.dataVInit(this.dom); // 初始化對象屬性
        
        // 創建右鍵事件對象
        this.rightFunctionOpt = new RightFunction({ 
            dom: this.dom, 
            menu: [{ 
                name: "新增一個div" , value: this.addDiv.bind(this) 
            }]
        });
        
        // 首先威觸發dom綁定事件
        this.initEvent(this.dom, "contextmenu");
    }
    
    const domInit = function(dom){
        const opt = this.opt;
        dom.style.cssText = `width: ${opt.width}; height: ${opt.height};`;
        dom.setAttribute("data-contextmenu", "onDomContextmenu")
    }
    
    const dataVInit = function(dom){
        // 初始化內部組件事件監聽
        this.initEvent(dom, "click");
    }
    
    const add = function(plugin){
        this.plugin.push(plugin);
        this.dom.appendChild(plugin.dom);
    }
    
    const remove = function(){
        
    }
    
    const initEvent = function(dom, type, fnName){
        dom.addEventListener(type,(e) => {
            e.preventDefault();
            var _this = e.target;
            for( ; ; ){
                e.target = _this;
                if( !_this || !_this.parentElement ) break;
                if( _this.dataset[e.type] ) {
                    const fn = _this.dataset[e.type];
                    typeof fn === "string" && this[fn] && this[fn](e, this);
                }
                if( _this == dom ){ 
                    // if( this[fnName || "on"+e.type] ) {
                    //     this[fnName || "on"+e.type](e, this);
                    //     break;
                    // }
                    break; 
                }
                _this = _this.parentElement;
            }
        });
    }    
    
    const DataVonDomContextmenu = function(e){
        this.rightFunctionOpt.show(e.pageX, e.pageY);
    }
    
    const addDiv = function(){
        this.add(new DivElement());
    }
    
    DataV.prototype = {
        add,  // 添加組件
        remove, // 刪除組件
        initEvent, // 初始化組件事件監聽
        domInit,   // 初始化dom
        dataVInit,  // 初始化數據
        onDomContextmenu: DataVonDomContextmenu,
        addDiv: addDiv
    }
    
    
    
    // --------------------   右鍵功能
    const RightFunctionOpt = {
        dom: document.body,
        menu: [{ name: "測試", value: ()=>{ alert("測試") }, dom: null}]
    }
    const RightFunction = function(_opt){
        const opt = this.opt = Object.assign(RightFunctionOpt, _opt);
        
        this.dom = this.createDom();
        
        this.initEvent(this.dom, "contextmenu");
        
        this.initEvent(this.dom, "click");
        
        this.render();
        
        this.cssText = `
            .\${xid} {
                left: \${left};
                top: \${top};
                position: absolute;
                border: solid 1px #000000;
                border-radius: 3px;
            }
            
            .\${xid} div {
                padding: 4px;
                border-bottom: 1px #333333;
            }
        `;
        
        this.datas = {
            left: "0px",
            top: "0px",
        }
        
        this.styled = this.createStyle();
    }
    
    RightFunction.prototype = {
        createStyle (){
            return new Styled(this.dom, this.cssText, this.datas);
        },
        createDom (){
            const div = document.createElement("div");
            div.className = "rightFunction";
            return div;
        },
        render (){
            const menu = this.opt.menu;
            const optionDom = menu.map((options, i)=>{
                return `<div data-index='${i}' data-click='selectOption'>${ options.name }</div>`;
            })
            this.dom.innerHTML = optionDom;
        },
        show (x, y){
            this.datas.left = x + "px";
            this.datas.top = y + "px";
            this.styled.setDatas(this.datas);
            this.opt.dom.appendChild(this.dom);
        },
        hide (){
            this.dom.remove();
        },
        initEvent: initEvent,
        
        selectOption (e){
            const menu = this.opt.menu[e.target.dataset.index]
            menu.value && menu.value(menu, this.opt.menu, this);
        }
    }
    
    
    //  -----------------------   關於樣式管理器
    const Styled = function(dom, styled, datas, isOnlyXid){
        this.xid = isOnlyXid || getXid();    
        this.dom = dom;
        this.dom.classList.add(this.xid);
        this.styled = styled.replace(/\t|\n/g,"");
        this.datas = datas;
        
        this.styleElement = this.createStyle();
        this.render();
    }
    
    Styled.prototype = {
            
        // 生成唯一識別碼
        createStyle (){
            const o = document.head.querySelector("."+this.xid);
            if( o ){ 
                return o;
            }
            
            const style = document.createElement("style");
            document.head.appendChild(style);
            style.classList.add(this.xid);
            return style;
        },
        
        setDatas(opt) {
            this.datas = Object.assign(this.datas, opt);
            this.render();
        },
        
        render (){
            // this.styleElement.innerHTML = eval(`()=>{ var {${Object.keys(this.datas)}} = this.datas, xid=${this.xid}; return {} }()`);
            this.styleElement.innerHTML = function(){ 
                eval(`var {${Object.keys(this.datas)}} = this.datas, xid="${this.xid}"`); 
                return eval("\`"+this.styled+"\`");
            }.call(this);
        }
    }
    
    
    
    // 一個Div對象
    class DivElement {
        constructor(arg) {
            this.dom = this.createElement();
            // 數據模型
            this.datas = {};
            // 樣式數據模型
            this.styleDatas = { 
                position: "",
                top: "",
                left: "",
                transformX: 0,
                transformY: 0,
                color: "",
                width: "",
                height: "100px", 
                fontWeight: "", 
                fontSize: "12px" ,
                padding: "4px 4px 4px 4px",
                margin: "4px 4px 4px 4px",
                border: "solid 1px #f0f0f0",
                borderTop: "solid 1px #ffffff",
                borderBottom: "solid 1px #ffffff",
                borderLeft: "solid 1px #ffffff",
                borderRight: "solid 1px #ffffff",
                backgroundColor: "",
                backgroundImage: "",
            };
            // 樣式模型
            this.style = ` .\${xid} { 
                color: \${color};
                font-size: \${fontSize};
                font-weight: \${fontWeight};
                // border: \${border}; 
                border-top: \${borderTop};
                border-bottom: \${borderBottom};
                border-left: \${borderLeft};
                border-right: \${borderRight};
                padding: \${padding}; 
                margin: \${margin};
                height: \${height};
                width: \${width};
                background-color: \${backgroundColor};
                background-image: url(\${backgroundImage});
                background-repeat: no-repeat;
                background-size: contain;
                position: \${position};
                top: \${top};
                left: \${left};
                transform: translate(\${transformX}px, \${transformY}px);
            `;
            // 樣式模型
            this.styled = new Styled(this.dom, this.style ,this.styleDatas);
            // 蔣婷點擊事件
            this.initEvent(this.dom, "click");
        }
        
        isCanDrop (){
            return this.styleDatas.position === "absolute";
        }
        
        editOptionChange (ary){
            
        }
        
        // 進入選中狀態
        showSelectModel (){
            // 當前選中模式
            selectModel.setPlugin(this);
            // 彈出樣式編輯
            styleEdit.setPlugin(this);
        }
        
        clickDiv (){
            this.showSelectModel();
        }
        
        createElement (){
            const div = document.createElement("div");
            div.setAttribute("data-click", "clickDiv");
            return div;
        }
        
        render (){
            
        }
        
        // 渲染數據
        renderData (){
            
        }
        
        // 渲染樣式
        renderStyle (){
            
        }
        
        initEvent (){
            return initEvent.apply(this, arguments);
        }
        
        onstyleEdit (styleEdit){
            // 編輯模塊
            this.editOption = [{
                type: "computed", value: this.styleDatas, change: (e)=> { this.styleDatas = { ...this.styleDatas, ...e }; }
            },{
                type: "img", name: "背景圖片", value: this.styleDatas.backgroundImage, change: (e)=> { this.styleDatas.backgroundImage = e }
            },{
                type: "select", name: "定位類型", value: this.styleDatas.position, options: [{ name: "全局定位", value: "absolute" }, { name: "默認定位", value: "" }], change: (e)=> { this.styleDatas.position = e; }
            }];
            
            styleEdit.onchange = ()=>{ 
                this.styled.setDatas(this.styleDatas);
                // 當前選中模式
                selectModel.setPlugin(this);
            };
        }
    }
    
    // 選中,操作模塊
    class SelectModel {
            
        constructor(plugin) {
            
            this.parentPlugin = this.plugin;
            
            this.dom = this.createElement();
            
            this.style = `
                .\${xid} { position: absolute; top: \${top}; left: \${left}; 
                           width: \${width}; height: \${height}; border: solid 1px #2e78ff; 
                           transform: translate(\${offx}px, \${offy}px);
                }
            `;
            
            this.styleDatas = { top: "0px", left: "0px", width: "0px", height: "0px", offx: 0, offy: 0 };
            
            this.styled = new Styled(this.dom, this.style, this.styleDatas)
            
        }
        
        setDrop (Drop){
            this.drop = new Drop(this.dom, this.dropMoveChange.bind(this), this.dropMoveChangeEnd.bind(this));
        }
        
        dropMoveChangeEnd (){
            if( !this.plugin.isCanDrop() ) return;
            this.styleDatas.top = getInt(this.styleDatas.top) + this.styleDatas.offy + "px";
            this.styleDatas.left = getInt(this.styleDatas.left) + this.styleDatas.offx + "px";
            this.styleDatas.offy = 0;
            this.styleDatas.offx = 0;
            this.styled.setDatas(this.styleDatas);
            
            // 聯動當前選中的組件
            this.plugin.styleDatas.top = getInt(this.plugin.styleDatas.top) + this.plugin.styleDatas.transformY + "px";
            this.plugin.styleDatas.left = getInt(this.plugin.styleDatas.left) + this.plugin.styleDatas.transformX + "px";
            this.plugin.styleDatas.transformX = 0;
            this.plugin.styleDatas.transformY = 0;
            this.plugin.styled.setDatas(this.plugin.styleDatas);
        }
        
        dropMoveChange (x, y){
            if( !this.plugin.isCanDrop() ) return;
            
            this.styleDatas.offx = x;
            this.styleDatas.offy = y;
            this.styled.setDatas(this.styleDatas);
            
            // 聯動當前選中的組件
            this.plugin.styleDatas.transformX = x;
            this.plugin.styleDatas.transformY = y;
            this.plugin.styled.setDatas(this.plugin.styleDatas);
        }
        
        createElement (){
            const div = document.createElement("div");
            return div;
        }
        
        // 獲取當前組件的dom位置信息
        getDomRects(dom){
            return dom.getClientRects()[0];
        }
        
        setPlugin (plugin){
            this.plugin = plugin;
            const rect = this.getDomRects(plugin.dom);
            this.styleDatas.width = rect.width + "px";
            this.styleDatas.height = rect.height + "px";
            this.styleDatas.top = rect.top + "px";
            this.styleDatas.left = rect.left + "px";
            this.styled.setDatas(this.styleDatas);
            
            this.show();
        }
        
        show(){
            document.body.appendChild(this.dom);
        }
        
        hide(){
            this.dom.remove();
        }
        
    }
    
    // 創建全局得選中管理器
    const selectModel = new SelectModel();
    
    // 關於style編輯器
    class StyleEdit {
        
        constructor(styleEditOption) {
            
            // 需要控制的內容
            this.editOption = [{ name: "顏色", inputType: "color" , type: "input" }];
            
            this.editElement = [];
            
            this.dom = this.createElement();
            
            // this.initEvent(this.dom, "change");
            this.getStyled();
            
        }
        
        getStyled (){
            this.styled = new Styled(this.dom, `
                .\${xid} { 
                    position: absolute; top: 15px; right: 15px;
                    border: solid 1px #000000; padding: 8px; 
                    background: #fff;
                    z-index: ${zoom[5]};
                }`, {}, this.constructor.xid);
        }
        
        createElement (){
            const div = document.createElement("div");
            div.classList.add("styleEdit");
            return div;
        }
        
        render (){
            this.dom.remove();
            this.dom.innerHTML = "";
            // 根據配置的可編輯方案填補內容
            this.editOption.forEach((e)=>{
                this.dom.appendChild(this.getRenderDom(e));
            })
        }
        
        getRenderDom (opt){
            let el = this.editElement.find((element)=>{ return element.type === opt.type });
            el = new el(opt);
            el.setEdit(this);
            return el.dom;
        }
        
        setPlugin (plugin){
            this.plugin = plugin;
            plugin.onstyleEdit(this);
            this.editOption = plugin.editOption;
            this.render();
            this.show();
        }
        
        addElementType (EditElement){
            this.editElement.push(EditElement);
        }
        
        show (){
            document.body.appendChild(this.dom);
        }
        
        hide (){
            this.dom.remove();
        }
        
        // 完整得組件變化調用事件 外部定義得 內部不需要實現
        onchange (){ }
        
        // 每一行變化觸發
        changeElement (){
            this.onchange();
        }
        
    }
    
    // 拖動插件
    const Drop = function(dom, fn, efn){
        this.dom;
        this.cover = this.createCover();
        this.setBox(dom);
        this.onchange = fn;
        this.onchengeEnd = efn;
    }
    Drop.prototype = {
        createCover (){
            const divCover = document.createElement("div");
            divCover.style.cssText = `
                position: absolute;
                top:0;
                left: 0;
                width: 100%;
                height:100%;
            `
            return divCover;
        },
        setBox : function(dom){
            this.element = dom;
             dom.addEventListener("mousedown",this.down.bind(this));
        },
        down : function(event){
            // 再地圖上創建一個全屏
            document.body.appendChild(this.cover);
            this._move = this.move.bind(this);
            this._up = this.up.bind(this);
            // this._enter = this.enter.bind(this);
            // this.cover.addEventListener("mouseenter",this.__enter);
            this.cover.addEventListener("mousemove",this._move);
            this.cover.addEventListener("mouseup",this._up); // 綁定釋放事件
        },
        enter (event){
            this.startX = event.pageX;
            this.startY = event.pageY;
        },
        up : function(event){
            this.cover.removeEventListener("mousemove",this._move);
            this.cover.removeEventListener("mouseup",this._up); // 綁定釋放事件
            // this.cover.addEventListener("mouseenter",this.__enter);
            this._move = null;
            this._up = null;
            this.startX = null;
            this.startY = null;
            // this.__enter = null;
            this.cover.remove();
            this.onchengeEnd();
        },
        move : function(event){
            if( !this.startX ) return this.enter(event);
            var left = event.pageX - this.startX;
            var top = event.pageY - this.startY;
            console.log( this.startX, this.startY, event.pageX, event.pageY)
            this.onchange(left, top);
        }
    }

    selectModel.setDrop(Drop);
    
    // 實例化樣式編輯實例
    const styleEdit = new StyleEdit();
    
    // 輸入框的編輯類型
    class EditInputElement {
        
        static type = "input";
        
        static xid = getXid();
        
        initEvent (){
            return initEvent.apply(this, arguments);
        }
        
        constructor(arg) {
            
            this.edit = null;
            
            this.options = arg;
            
            this.dom = this.getRenderDom(arg);
            
            this.initEvent(this.dom, "change");
            
            this.getStyled();
        }
        
        getStyled (){
            this.styled = new Styled(this.dom, `
                .\${xid} { 
                    border: solid 1px #000000; padding: 8px; 
                }`, {}, this.constructor.xid);
        }
        
        getRenderDom ( { name, value, inputType } ){
            const div = document.createElement("div");
            div.classList.add("styleEditRow");
            div.innerHTML = `
                <div> ${name} </div>
                <div> <input data-change='changeValue' value='${value}' type='${inputType}' ></div>
            `
            return div;
        }
        
        setEdit (edit){
            this.edit = edit;
        }
        
        changeValue (options){
            this.options.change(options.target.value);
            this.edit.changeElement(options);
        }
    }
    
    // 盒子模型編輯插件
    class ComputedElement extends EditInputElement {
        static type = "computed";
        
        static xid = getXid();
        
        constructor (arg){
            super(arg);
            
            this.initEvent(this.dom,"click");
        }
        
        getStyled (){
            const xid = this.constructor.xid;
            const options = this.options.value;
            this.styleDatas = {
                ...options,
                borderTop: options.borderTop.split(" "), // "border 1px #ffffff".split(" "),
                borderBottom: options.borderBottom.split(" "), // "border 1px #ffffff".split(" "),
                borderLeft: options.borderLeft.split(" "), // "border 1px #ffffff".split(" "),
                borderRight: options.borderRight.split(" ") // "border 1px #ffffff".split(" "),
            };
            this.styled = new Styled(this.dom, `
                .\${xid} { 
                    border: solid 1px #000000; padding: 8px; 
                }
                .\${xid} .computedBx_${xid} { height: 35px; line-height: 35px; position: relative;text-align: center; font-size: 12px; }
                .\${xid} .top_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; top: 0px; left: 0px; width: 100%; height: 4px; background-color: \${borderTop[2]}; box-sizing: border-box; }
                .\${xid} .bottom_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; bottom: 0px; left: 0px; width: 100%; height: 4px; background-color: \${borderBottom[2]}; box-sizing: border-box;}
                .\${xid} .left_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; top: 0px; left: 0px; width: 4px; height: 100%; background-color: \${borderLeft[2]}; box-sizing: border-box;}
                .\${xid} .right_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; top: 0px; right: 0px; width: 4px; height: 100%; background-color: \${borderRight[2]}; box-sizing: border-box;}
                `, this.styleDatas, this.constructor.xid);
        }
        
        getRenderDom ( { name, value, inputType } ){
            const div = document.createElement("div");
            div.classList.add("styleEditRow");
            const xid = this.constructor.xid;
            const options = this.options.value;
            div.innerHTML = `
                <div> 盒子模型 </div>
                <div class='computedBx_${xid}'> 
                    <div data-click='border' data-bordertype='borderTop' class='top_${xid}'></div>
                    <div data-click='border' data-bordertype='borderBottom' class='bottom_${xid}'></div>
                    <div data-click='border' data-bordertype='borderLeft' class='left_${xid}'></div>
                    <div data-click='border' data-bordertype='borderRight' class='right_${xid}'></div>
                    <div data-click='border' data-borderType='border'>border顏色</div>
                </div>
                <div class='computedBx_wh'>
                    <div>
                        <div>寬度</div>
                        <input type='text' data-change='changeAttr' data-type='width' value='${options.width}' />
                    </div>
                    <div>
                        <div>高度</div>
                        <input type='text' data-change='changeAttr' data-type='height' value='${options.height}' />
                    </div>
                </div>
            `
            return div;
        }
        
        // 改變屬性
        changeAttr (e){
            this.styleDatas[e.target.dataset.type] = e.target.value;
            this.changeValue(this.getJson());
        }
        
        border (e){
            let type = [e.target.dataset.bordertype];
            if( type[0] == "border" ) { type = ["borderTop", "borderBottom", "borderLeft", "borderRight"] }
            this.getColor().then((color)=>{
                type.forEach((t)=>{
                    // 修改對應的樣式
                    this.styleDatas[t][2] = color;
                })
                this.styled.setDatas(this.styleDatas);
                this.changeValue(this.getJson());
            })
        }
        
        getJson (){
            return {
                ...this.styleDatas,
                borderTop: this.styleDatas.borderTop.join(" "),
                borderBottom: this.styleDatas.borderBottom.join(" "),
                borderLeft: this.styleDatas.borderLeft.join(" "),
                borderRight: this.styleDatas.borderRight.join(" "),
            }
        }
        
        getColor (){
            return new Promise((resovle)=>{
                const ipt = document.createElement("input");
                ipt.type = "color";
                ipt.onchange = function(e){
                    resovle(e.target.value);
                }
                ipt.click();
            })
        }
        
        changeValue (options){
            this.options.change(options);
            this.edit.changeElement(options);
        }
    }
    
    const fs = new FileReader();
    
    // 圖片編輯類型
    class ImageElement extends EditInputElement {
        static type = "img";
        
        static xid = getXid();
        
        constructor (arg){
            super(arg);
            
            this.initEvent(this.dom,"click");
        }
        
        getStyled (){
            const xid = this.constructor.xid;
            const options = this.options.value;
            this.styleDatas = {
                imagesUrl: options
            };
            this.styled = new Styled(this.dom, `
                .\${xid} { 
                    margin: 4px; 
                    padding: 8px; 
                    height: 150px;
                    width: 100%;
                }
                .computedBx_\${xid} { background: url(\${imagesUrl}); width: 100%; height: 100%; background-repeat: no-repeat;background-size: contain;}
                `, this.styleDatas, this.constructor.xid);
        }
        
        getRenderDom ( { name, value, inputType } ){
            const div = document.createElement("div");
            div.classList.add("styleEditRow");
            const xid = this.constructor.xid;
            div.innerHTML = `
                <div> ${name} </div>
                <div class='computedBx_${xid}' data-click='updateImage'> 
                
                </div>
            `
            return div;
        }
        
        getImage (){
            return new Promise((resovle)=>{
                const ipt = document.createElement("input");
                ipt.type = "file";
                ipt.onchange = function(e){
                    // resovle(e.target.value);
                    fs.onload = function(e){
                        resovle(e.currentTarget.result);
                    }
                    fs.readAsDataURL(e.target.files[0]);
                }
                ipt.click();
            })
        }
        
        updateImage (){
            this.getImage().then((base64)=>{
                this.styleDatas.imagesUrl = base64;
                this.styled.setDatas(this.styleDatas);
                this.changeValue(this.styleDatas);
            })
        }
        
        changeValue (options){
            this.options.change(options.imagesUrl);
            this.edit.changeElement(options.imagesUrl);
        }
    }
    
    // 多選
    // 圖片編輯類型
    class SelectElement extends EditInputElement {
        static type = "select";
        
        static xid = getXid();
        
        constructor (arg){
            super(arg);
            
            this.initEvent(this.dom,"click");
        }
        
        getStyled (){
            const xid = this.constructor.xid;
            const options = this.options.value;
            this.styleDatas = {
                // imagesUrl: options
            };
            this.styled = new Styled(this.dom, `
                .\${xid} { 
                    margin: 4px; 
                    padding: 8px; 
                    height: 150px;
                    width: 100%;
                }
                `, this.styleDatas, this.constructor.xid);
        }
        
        getRenderDom ( { name, value, options } ){
            const div = document.createElement("div");
            div.classList.add("styleEditRow");
            const xid = this.constructor.xid;
            div.innerHTML = `
                <div> ${name} </div>
                <div class='computedBx_${xid}'> 
                    <select data-change="changeValue">
                        ${ options.map((e)=> `<option ${e.value === value && "selected"} value='${e.value}'>${e.name}</option>` ) }
                    </select>
                </div>
            `
            return div;
        }
        
        changeValue (options){
            this.options.change(options.target.value);
            this.edit.changeElement();
        }
    }
    
    // input編輯類型
    styleEdit.addElementType(EditInputElement);
    styleEdit.addElementType(ComputedElement);
    styleEdit.addElementType(ImageElement);
    styleEdit.addElementType(SelectElement);
    
    return exports;
    
}, window)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章