vue自定义面板指令

vue自定义面板指令

在开发项目时,用到了许多有遮罩层的面板,这些面板很生硬,大小固定,位置固定,无法最小化等等缺点。我就很想使用一个vue自定义指令解决这些问题,对于任何一个面板,只需要在该面板元素添加一个v-panel指令,这个面板就可以拖拽了!
项目文件如下:
vue自定义面板指令_第1张图片
环境:vue2+scss,不要忘了在App.vue中引入index.scss哦!

1.定义面板和遮罩层样式panel.scss,使用scss进行扩展

.panel-dialog {
    width: 600px;
    height: 300px;
    position: absolute;
    z-index: 9000;
    top: 100px;
    left: 100px;
    border: 1px solid #d5d5d5;
    background: #fff;
    a {
        text-decoration: none;
    }
    .panel-dialog-title {
        height: 48px;
        line-height: 48px;
        padding-left: 12px;
        font-size: 16px;
        color: #535353;
        border-bottom: 1px solid #efefef;
        background: #3fa9f5;
        cursor: move;
    }
    .panel-dialog-content {
        padding: 15px 20px;
    }
    .panel-dialog-closebutton {
        width: 16px;
        height: 16px;
        display: block;
        position: absolute;
        top: 12px;
        right: 20px;
        background: url(assets/img/panel/close_def.png) no-repeat;
        cursor: pointer;
    }

    .panel-dialog-closebutton:hover {
        background: url(assets/img/panel/close_hov.png);
    }
}

/*shade*/
.panel-mask {
    width: 100%;
    height: 100%;
    background: #000;
    opacity: 0.4;
    /*兼容ie*/
    filter: Alpha(opacity=40);
    position: absolute;
    z-index: 8000;
    top: 0;
    left: 0;
}

/*panel change size -- start*/
.panel_resizeBR {
    position: absolute;
    width: 14px;
    height: 14px;
    right: 0;
    bottom: 0;
    overflow: hidden;
    cursor: nw-resize;
}
.panel_resizeL,
.panel_resizeT,
.panel_resizeR,
.panel_resizeB,
.panel_resizeLT,
.panel_resizeTR,
.panel_resizeLB {
    position: absolute;
    background: #000;
    overflow: hidden;
    opacity: 0;
    filter: alpha(opacity=0);
}
.panel_resizeL,
.panel_resizeR {
    top: 0;
    width: 5px;
    height: 100%;
    cursor: w-resize;
}
.panel_resizeR {
    right: 0;
}
.panel_resizeT,
.panel_resizeB {
    width: 100%;
    height: 5px;
    cursor: n-resize;
}
.panel_resizeT {
    top: 0;
}
.panel_resizeB {
    bottom: 0;
}
.panel_resizeLT,
.panel_resizeTR,
.panel_resizeLB {
    width: 8px;
    height: 8px;
    background: #FF0;
}
.panel_resizeLT {
    top: 0;
    left: 0;
    cursor: nw-resize;
}
.panel_resizeTR {
    top: 0;
    right: 0;
    cursor: ne-resize;
}
.panel_resizeLB {
    left: 0;
    bottom: 0;
    cursor: ne-resize;
}
/*panel change size -- end*/

2.操作元素和面板的js函数–element.js

export default {
  // 自动居中
  autoCenter(el) {
    let bodyW = document.documentElement.clientWidth
    let bodyH = document.documentElement.clientHeight

    let elW = el.offsetWidth
    let elH = el.offsetHeight

    el.style.left = (bodyW - elW) / 2 + 'px'
    el.style.top = (bodyH - elH) / 2 + 'px'
  },
  // 全屏-遮罩
  fillToBody(el) {
    el.style.width = document.documentElement.clientWidth + 'px'
    el.style.height = document.documentElement.clientHeight + 'px'
  },
  // 获取元素对象
  byId(el) {
    return document.getElementById(el)
  },
  byClass(sClass, oParent) {
    let aClass = []
    let reClass = new RegExp('(^| )' + sClass + '( |$)')
    let aElem = this.byTagName('*', oParent)
    for (let i = 0; i < aElem.length; i++) {
      reClass.test(aElem[i].className) && aClass.push(aElem[i])
    }
    return aClass
  },
  byTagName(elem, obj) {
    return (obj || document).getElementsByTagName(elem)
  },
  /**
   * 拖拽函数
   * @param{oDrag} 被拖拽的元素
   * @param{handle} 目标元素
   */
  drag(oDrag, handle, dragMinWidth, dragMinHeight) {
    let self = this
    handle.onselectstart = function () {
      return false
    }
    // 偏移量
    let disX = 0
    let disY = 0
    handle = handle || oDrag
    handle.style.cursor = 'move'
    handle.onmousedown = function (event) {
      event = event || window.event
      disX = event.clientX - oDrag.offsetLeft
      disY = event.clientY - oDrag.offsetTop
      document.onmousemove = function (event) {
        event = event || window.event
        let iL = event.clientX - disX // X轴移动距离
        let iT = event.clientY - disY // Y轴移动距离
        let maxL = document.documentElement.clientWidth - oDrag.offsetWidth // X轴最大移动距离
        let maxT = document.documentElement.clientHeight - oDrag.offsetHeight // Y轴最大移动距离

        iL = Math.min(maxL, Math.max(0, iL))
        iT = Math.min(maxT, Math.max(0, iT))

        oDrag.style.left = iL + 'px'
        oDrag.style.top = iT + 'px'
        return false
      }
      document.onmouseup = function () {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  },
  /**
   * 改变面板大小的函数
   * 
   * @param {Object} oParent 被拖拽的元素
   * @param {Object} handle 鼠标点击时选中的目标元素
   * @param {Boolean} isLeft 是否左边方向的拉伸div
   * @param {Boolean} isTop 是否顶部的拉伸div
   * @param {Boolean} lockX 是否往X轴方向拉伸
   * @param {Boolean} lockY 是否往Y轴方向拉伸
   * @param {Number} dragMinWidth 面板最小宽度
   * @param {Number} dragMinHeight 面板最小高度
   */
  resize(oParent, handle, isLeft, isTop, lockX, lockY, dragMinWidth, dragMinHeight) {
    handle.onmousedown = function (event) {
      event = event || window.event
      let disX = event.clientX - handle.offsetLeft
      let disY = event.clientY - handle.offsetTop
      let iParentTop = oParent.offsetTop
      let iParentLeft = oParent.offsetLeft
      let iParentWidth = oParent.offsetWidth
      let iParentHeight = oParent.offsetHeight

      document.onmousemove = function (event) {
        event = event || window.event

        let iL = event.clientX - disX
        let iT = event.clientY - disY
        let maxW = document.documentElement.clientWidth - oParent.offsetLeft - 2
        let maxH = document.documentElement.clientHeight - oParent.offsetTop - 2
        let iW = isLeft ? iParentWidth - iL : handle.offsetWidth + iL
        let iH = isTop ? iParentHeight - iT : handle.offsetHeight + iT

        isLeft && (oParent.style.left = iParentLeft + iL + 'px')
        isTop && (oParent.style.top = iParentTop + iT + 'px')

        iW < dragMinWidth && (iW = dragMinWidth)
        iW > maxW && (iW = maxW)
        lockX || (oParent.style.width = iW + 'px')

        iH < dragMinHeight && (iH = dragMinHeight)
        iH > maxH && (iH = maxH)
        lockY || (oParent.style.height = iH + 'px')

        if ((isLeft && iW === dragMinWidth) || (isTop && iH === dragMinHeight)) document.onmousemove = null

        return false
      }
      document.onmouseup = function () {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  },
  /**
   * 面板放大缩小--(不支持通用)
   * 
   * @param {Object} el 面板元素
   * @param {Number} minWidth 面板最小宽度
   * @param {Number} minHeight 面板最小高度
   */
  vpanelResize(el, minWidth, minHeight) {
    // 四角和四边的类名
    let classNames = ['panel_resizeL', 'panel_resizeT', 'panel_resizeR', 'panel_resizeB', 'panel_resizeLT', 'panel_resizeTR', 'panel_resizeBR', 'panel_resizeLB']
    let div = null
    for (let i = 0; i < 8; i++) {
      div = document.createElement('div')
      div.className = classNames[i]
      el.appendChild(div)
    }
    var oL = this.byClass('panel_resizeL', el)[0]
    var oT = this.byClass('panel_resizeT', el)[0]
    var oR = this.byClass('panel_resizeR', el)[0]
    var oB = this.byClass('panel_resizeB', el)[0]
    var oLT = this.byClass('panel_resizeLT', el)[0]
    var oTR = this.byClass('panel_resizeTR', el)[0]
    var oBR = this.byClass('panel_resizeBR', el)[0]
    var oLB = this.byClass('panel_resizeLB', el)[0]
    // 四角
    this.resize(el, oLT, true, true, false, false, minWidth, minHeight)
    this.resize(el, oTR, false, true, false, false, minWidth, minHeight)
    this.resize(el, oBR, false, false, false, false, minWidth, minHeight)
    this.resize(el, oLB, true, false, false, false, minWidth, minHeight)
    // 四边
    this.resize(el, oL, true, false, false, true, minWidth, minHeight)
    this.resize(el, oT, false, true, true, false, minWidth, minHeight)
    this.resize(el, oR, false, false, false, true, minWidth, minHeight)
    this.resize(el, oB, false, false, true, false, minWidth, minHeight)
  }
}

3.设置utils路径

在webpack.base.conf.js中设置js通用函数的路径别名
vue自定义面板指令_第2张图片

4.引入utils通用函数

vue自定义面板指令_第3张图片

5.在main.js中定义指令v-panel

/** 
 * 弹窗面板可拖拽、改变大小指令
 * 当前支持指令属性  drag-拖拽  resize-改变大小
 * 使用方式:v-panel.drag.resize
 * 传参方式:v-panel:{minWidth:500,minHeight:260} 注意:传参不能带空格,否则报错!!!!!
 * @param{Object} el 面板元素
 * @param{Object} binding 
 * @param{Object}
 */
Vue.directive('panel', {
    bind: function (el, binding, vnode) {
        var sports = binding.modifiers  // 指令修饰符的对象
        var params = binding.arg        // 指令传参 {minWidth:500,minHeight:260} 面板宽度和高度
        var targetElem = vnode.children.length && vnode.children[0].elm  // 目标元素
        window.onresize = function () {
            utils.ElementUtils.autoCenter(el)  // 居中
        }
        if (!targetElem) {      // 如果目标元素不存在,关闭拖拽和调整大小
            return
        }
        var minWidth = 500      // 面板默认最小宽度
        var minHeight = 260 // 面板默认最小高度
        if (params) {
            minWidth = +params.minWidth || minWidth
            minHeight = +params.minHeight || minHeight
        }
        // 2.添加v-panel.drag.resize  面板支持可拖拽、调整大小
        if (JSON.stringify(sports) !== '{}') {
            if (sports.drag) {      // 拖拽
                utils.ElementUtils.drag(el, targetElem, minWidth, minHeight)
            }
            if (sports.resize) {    // 调整大小
                utils.ElementUtils.vpanelResize(el, minWidth, minHeight)
            }
        } else {
            // 1.添加v-panel指令,面板默认支持可拖拽
            utils.ElementUtils.drag(el, targetElem, minWidth, minHeight)
        }
    },
    // 所在组件的 VNode 更新时调用
    update: function (el) {
        utils.ElementUtils.autoCenter(el)  // 居中
    }
})

6.面板Demo,最重要的是添加v-panel指令!


<template>
    <section>
        <section class="panel-mask" v-show="show">section>
        <section class="panel-dialog" v-show="show" v-panel.drag.resize>
            <section class="panel-dialog-title">
                面板
                <a class="panel-dialog-closebutton" @click="onClose">a>
            section>
            <section class="panel-dialog-content">
            section>
        section>
    section>
template>

<script>
    export default {
        components: {
        },
        data () {
            return {
                // 是否显示面板
                show: false
            }
        },
        computed: {
        },
        mounted () {
        },
        methods: {
            // 打开面板
            onShow () {
                this.show = true
            },
            // 关闭面板
            onClose () {
                this.show = false
            }
        }
    }
script>

7.效果如下

vue自定义面板指令_第4张图片

你可能感兴趣的:(vuejs)