核心原理就是在四条边、四个顶点加上透明的div,给不同方向提供按下移动鼠标监听 ,对应计算宽度高度、坐标变化
特性:
- 支持设置拖拽的最小宽度、最小高度、最大宽度、最大高度
- 可以双击某一条边,最大化对应方向的尺寸;再一次双击,则会恢复到原始大小
sgDragSize源码
<template> <div :class="$options.name" :disabled="disabled" draggable="false"> <div :class="`resize-handle resize-${a}`" draggable="false" @mousedown.stop="clickResizeHandle(a)" @dblclick="dblclickResizeHandle(a, $event)" v-for="(a, i) in sizeIndexs" :key="i"></div> </div> </template> <script> export default { name: 'sgDragSize', data() { return { tbHeight: 0, dragSizeIndex: '', originRect: {}, dblclickOriginRect: {}, sizeIndexs: [ 'top', 'right', 'bottom', 'left', 'top-left', 'top-right', 'bottom-left', 'bottom-right', ], } }, props: [ "disabled",//屏蔽 "taskbarHeight",//任务栏高度 "minWidth",//拖拽的最小宽度 "minHeight",//拖拽的最小高度 "maxWidth",//拖拽的最大宽度 "maxHeight",//拖拽的最大高度 ], watch: { disabled: { handler(newValue, oldValue) { newValue && this.__removeWindowEvents(); }, deep: true, immediate: true, }, taskbarHeight: { handler(d) { this.tbHeight = d || 0; }, deep: true, immediate: true, }, }, destroyed() { this.__removeWindowEvents(); }, methods: { view_innerHeight() { return innerHeight - this.tbHeight; }, clickResizeHandle(d) { this.dragSizeIndex = d; this.mousedown(d); }, dblclickResizeHandle(d, $event) { let rect = this.$el.getBoundingClientRect(); rect.width < innerWidth && rect.height < this.view_innerHeight() && (this.dblclickOriginRect = rect); this.dblResize(d, rect, $event); }, __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(e) { let { x, y } = e; 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 > this.view_innerHeight() && (y = this.view_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', { e, style }); }, dblResize(d, rect, e) { let style = {}; switch (d) { case 'top-left': break; case 'top': case 'bottom': style.left = this.originRect.x; style.top = rect.height >= this.view_innerHeight() ? this.dblclickOriginRect.y : 0; style.width = this.originRect.width; style.height = rect.height >= this.view_innerHeight() ? this.dblclickOriginRect.height : this.view_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', { e, 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/Windows7/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="dragging" :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', }, } }, methods: { dragging({ style }) { this.style = style; }, }, }; </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>