核心原理就是在四条边、四个顶点加上透明的div,给不同方向提供按下移动鼠标监听 ,对应计算宽度高度、坐标变化
特性:
- 支持设置拖拽的最小宽度、最小高度、最大宽度、最大高度
- 可以双击某一条边,最大化对应方向的尺寸;再一次双击,则会恢复到原始大小
sgDragSize源码
<template>
<div :class="$options.name" :disabled="disabled" draggable="false">
<div :class="`resize-handle resize-${a}`" draggable="false" @mousedown.stop="clickResizeHandle(a)"
@dblclick.stop="dblclickResizeHandle(a)" v-for="(a, i) in sizeIndexs" :key="i"></div>
</div>
</template>
<script>
export default {
name: 'sgDragSize',
data() {
return {
dragSizeIndex: '',
originRect: {},
dblclickOriginRect: {},
sizeIndexs: [
'top',
'right',
'bottom',
'left',
'top-left',
'top-right',
'bottom-left',
'bottom-right',
],
}
},
props: [
"disabled",//屏蔽
"minWidth",//拖拽的最小宽度
"minHeight",//拖拽的最小高度
"maxWidth",//拖拽的最大宽度
"maxHeight",//拖拽的最大高度
],
watch: {
disabled: {
handler(newValue, oldValue) {
newValue && this.__removeWindowEvents();
}, deep: true, immediate: true,
},
},
destroyed() {
this.__removeWindowEvents();
},
methods: {
clickResizeHandle(d) {
this.dragSizeIndex = d;
this.mousedown(d);
},
dblclickResizeHandle(d) {
let rect = this.$el.getBoundingClientRect();
rect.width < innerWidth && rect.height < innerHeight && (this.dblclickOriginRect = rect);
this.dblResize(d, rect);
},
__addWindowEvents() {
this.__removeWindowEvents();
addEventListener('mousemove', this.mousemove_window);
addEventListener('mouseup', this.mouseup_window);
},
__removeWindowEvents() {
removeEventListener('mousemove', this.mousemove_window);
removeEventListener('mouseup', this.mouseup_window);
},
mousedown(e) {
this.originRect = this.$el.getBoundingClientRect();
this.originRect.bottomRightX = this.originRect.x + this.originRect.width;//右下角坐标.x
this.originRect.bottomRightY = this.originRect.y + this.originRect.height;//右下角坐标.y
this.$emit('dragStart', e);
this.__addWindowEvents();
},
mousemove_window({ x, y }) {
let minWidth = this.minWidth || 50, minHeight = this.minHeight || 50, maxWidth = this.maxWidth || innerWidth, maxHeight = this.maxHeight || innerHeight;
x < 0 && (x = 0), y < 0 && (y = 0), x > innerWidth && (x = innerWidth), y > innerHeight && (y = innerHeight);
let style = {};
switch (this.dragSizeIndex) {
case 'top-left':
style.left = x;
style.top = y;
style.width = this.originRect.bottomRightX - x;
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.bottomRightX - minWidth);
style.height = this.originRect.bottomRightY - y;
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.bottomRightY - minHeight);
break;
case 'top':
style.left = this.originRect.x;
style.top = y;
style.width = this.originRect.width;
style.height = this.originRect.bottomRightY - y;
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.bottomRightY - minHeight);
break;
case 'top-right':
style.left = this.originRect.x;
style.top = y;
style.width = x - this.originRect.x;
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.x);
style.height = this.originRect.bottomRightY - y;
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.bottomRightY - minHeight);
break;
case 'left':
style.left = x;
style.top = this.originRect.y;
style.width = this.originRect.bottomRightX - x;
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.bottomRightX - minWidth);
style.height = this.originRect.height;
break;
case 'right':
style.left = this.originRect.x;
style.top = this.originRect.y;
style.width = x - this.originRect.x;
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.x);
style.height = this.originRect.height;
break;
case 'bottom-left':
style.left = x;
style.top = this.originRect.y;
style.width = this.originRect.bottomRightX - x;
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.bottomRightX - minWidth);
style.height = y - this.originRect.y;
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.y);
break;
case 'bottom':
style.left = this.originRect.x;
style.top = this.originRect.y;
style.width = this.originRect.width;
style.height = y - this.originRect.y;
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.y);
break;
case 'bottom-right':
style.left = this.originRect.x;
style.top = this.originRect.y;
style.width = x - this.originRect.x;
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.x);
style.height = y - this.originRect.y;
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.y);
break;
default:
}
style.width > maxWidth && (style.width = maxWidth);
style.height > maxHeight && (style.height = maxHeight);
Object.keys(style).forEach(k => style[k] = `${style[k]}px`);
style['transition-property'] = 'width,height';
style['transition-duration'] = '0,0';
this.$emit('dragging', style);
},
dblResize(d, rect) {
let style = {};
switch (d) {
case 'top-left':
break;
case 'top':
case 'bottom':
style.left = this.originRect.x;
style.top = rect.height >= innerHeight ? this.dblclickOriginRect.y : 0;
style.width = this.originRect.width;
style.height = rect.height >= innerHeight ? this.dblclickOriginRect.height : innerHeight;
break;
case 'top-right':
break;
case 'left':
case 'right':
style.left = rect.width >= innerWidth ? this.dblclickOriginRect.x : 0;
style.top = this.originRect.y;
style.width = rect.width >= innerWidth ? this.dblclickOriginRect.width : innerWidth;
style.height = this.originRect.height;
break;
case 'bottom-left':
break;
case 'bottom-right':
break;
default:
}
Object.keys(style).forEach(k => style[k] = `${style[k]}px`);
style['transition-property'] = 'width,height';
style['transition-duration'] = '0.1s,0.1s';
this.$emit('dragging', style);
},
mouseup_window(e) {
this.$emit('dragEnd', e);
this.__removeWindowEvents();
},
}
};
</script>
<style lang="scss">
.sgDragSize {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
pointer-events: none;
.resize-handle {
position: absolute;
z-index: 100;
display: block;
pointer-events: auto;
}
&[disabled] {
.resize-handle {
pointer-events: none;
}
}
.resize-top {
cursor: n-resize;
top: -3px;
left: 0px;
height: 7px;
width: 100%;
}
.resize-right {
cursor: e-resize;
right: -3px;
top: 0px;
width: 7px;
height: 100%;
}
.resize-bottom {
cursor: s-resize;
bottom: -3px;
left: 0px;
height: 7px;
width: 100%;
}
.resize-left {
cursor: w-resize;
left: -3px;
top: 0px;
width: 7px;
height: 100%;
}
.resize-top-right {
cursor: ne-resize;
width: 16px;
height: 16px;
right: -8px;
top: -8px;
}
.resize-bottom-right {
cursor: se-resize;
width: 20px;
height: 20px;
right: -8px;
bottom: -8px;
background: url('/static/img/desktop/sgDragSize/resize_corner.png') no-repeat;
}
.resize-bottom-left {
cursor: sw-resize;
width: 16px;
height: 16px;
left: -8px;
bottom: -8px;
}
.resize-top-left {
cursor: nw-resize;
width: 16px;
height: 16px;
left: -8px;
top: -8px;
}
}
</style>
应用
<template>
<div>
<div class="box" :style="style">
<label>最小尺寸:宽度400px,高度200px</label>
<sgDragSize @dragging="d => style = d" :minWidth="400" :minHeight="200" />
</div>
</div>
</template>
<script>
import sgDragSize from "@/vue/components/admin/sgDragSize";
export default {
components: {
sgDragSize,
},
data() {
return {
style: {
height: '500px',
width: '800px',
left: '100px',
top: '100px',
},
}
},
};
</script>
<style lang="scss" scoped>
.box {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
background-color: #409EFF55;
box-sizing: border-box;
border: 1px solid #409EFF;
label {
user-select: none;
color: #409EFF;
}
}
</style>