在开发项目时,用到了许多有遮罩层的面板,这些面板很生硬,大小固定,位置固定,无法最小化等等缺点。我就很想使用一个vue自定义指令解决这些问题,对于任何一个面板,只需要在该面板元素添加一个v-panel指令,这个面板就可以拖拽了!
项目文件如下:
环境:vue2+scss,不要忘了在App.vue中引入index.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*/
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)
}
}
在webpack.base.conf.js中设置js通用函数的路径别名
/**
* 弹窗面板可拖拽、改变大小指令
* 当前支持指令属性 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) // 居中
}
})
<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>