【sgDragSize】自定义组件:自定义拖拽修改DIV尺寸组件,适用于窗体大小调整

文章介绍了sgDragSize组件,一个可定制的拖拽控件,支持设置最小和最大尺寸,以及双击边角操作来调整大小。它通过监听鼠标事件实现动态调整元素的宽高。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

核心原理就是在四条边、四个顶点加上透明的div,给不同方向提供按下移动鼠标监听 ,对应计算宽度高度、坐标变化 

特性:

  1. 支持设置拖拽的最小宽度、最小高度、最大宽度、最大高度
  2. 可以双击某一条边,最大化对应方向的尺寸;再一次双击,则会恢复到原始大小

sgDragSize.vue

<template>
  <!-- 注意:此组件要放置于被拖拽修改尺寸的元素里面最后一个子节点位置! -->
  <div
    :class="$options.name"
    :disabled="disabled"
    draggable="false"
    :sgDragSize_move="sgDragSize_move"
  >
    <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>
</template>
<script>
export default {
  name: `sgDragSize`,
  data() {
    return {
      form: {},
      dragSizeIndex: ``,
      originRect: {},
      dblclickOriginRect: {},
      sizeIndexs: [
        `top`,
        `right`,
        `bottom`,
        `left`,
        `top-left`,
        `top-right`,
        `bottom-left`,
        `bottom-right`,
      ],
      sgDragSize_move: ``,
      disabled: false, //屏蔽
      taskbarHeight: 0, //任务栏高度
      minWidth: 50, //拖拽的最小宽度
      minHeight: 50, //拖拽的最小高度
      maxWidth: innerWidth, //拖拽的最大宽度
      maxHeight: innerHeight, //拖拽的最大高度
    };
  },
  props: [`data`],
  watch: {
    disabled: {
      handler(newValue, oldValue) {
        newValue && this.__removeWindowEvents();
      },
      deep: true,
      immediate: true,
    },
    data: {
      handler(newValue, oldValue) {
        //console.log(`深度监听${this.$options.name}:`, newValue, oldValue);
        if (Object.keys(newValue || {}).length) {
          this.form = this.$g.deepCopy(newValue);
          this.$g.convertForm2ComponentParam(`disabled`, this);
          this.$g.convertForm2ComponentParam(`taskbarHeight`, this);
          this.$g.convertForm2ComponentParam(`minWidth`, this);
          this.$g.convertForm2ComponentParam(`minHeight`, this);
          this.$g.convertForm2ComponentParam(`maxWidth`, this);
          this.$g.convertForm2ComponentParam(`maxHeight`, this);
        }
      },
      deep: true, //深度监听
      immediate: true, //立即执行
    },
  },
  beforeDestroy() {
    this.__removeWindowEvents();
  },
  methods: {
    view_innerHeight() {
      return innerHeight - this.taskbarHeight;
    },
    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 offsetDis = 5; //提供一个偏差值,防止拖拽过程鼠标手型闪烁变化
      this.sgDragSize_move = `ing`;
      let { x, y } = e;
      let minWidth = this.minWidth,
        minHeight = this.minHeight,
        maxWidth = this.maxWidth,
        maxHeight = this.maxHeight;
      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 + offsetDis / 2;
          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 + offsetDis / 2;
          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 + offsetDis;
          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 + offsetDis / 2;
          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 + offsetDis;
          style.width <= minWidth &&
            ((style.width = minWidth), (style.left = this.originRect.x));
          style.height = y - this.originRect.y + offsetDis;
          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`));
      this.$emit(`dragging`, { e, style });
    },
    dblResize(d, rect, e) {
      this.$emit(`dblResize`, { e, style });
      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`));
      this.$emit(`dragging`, { e, style });
    },
    mouseup_window(e) {
      this.sgDragSize_move = ``;
      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;
    // 绘制三横线----------------------------------------
    &::after {
      pointer-events: none;
      content: "";
      position: absolute;
      width: 10px;
      height: 10px;
      background-image: linear-gradient(
        135deg,
        transparent 50%,
        #888 50%,
        #888 55%,
        transparent 55%,
        transparent 70%,
        #888 70%,
        #888 75%,
        transparent 75%,
        transparent 90%,
        #888 90%,
        #888 95%,
        transparent 95%
      );
    }
    // ----------------------------------------
  }

  .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;
  }
}

//防止拖拽物体内嵌的监听方法冲突,影响拖拽连续性----------------------------------------
body:has([sgDragSize_move="ing"]) {
  .explorer {
    pointer-events: none !important;
  }
}
</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>

扩展【sgFloatDialog】自定义组件:浮动弹窗,支持修改尺寸、拖拽位置、最大化、还原、最小化、复位-CSDN博客文章浏览阅读245次。它允许用户自定义吸附距离,可以设置拖拽行为是否禁用,以及停靠边界距离。组件在拖拽过程中提供了半透明效果,并能在不同阶段监听和处理鼠标事件以实现拖拽、吸附和停靠功能。核心原理就是在四条边、四个顶点加上透明的div,给不同方向提供按下移动鼠标监听 ,对应计算宽度高度、坐标变化。【sgDragSize】自定义组件:自定义拖拽修改DIV尺寸组件,适用于窗体大小调整_div拖拽调整大小-CSDN博客。【sgDragMove】自定义组件:自定义拖拽组件,仅支持拖拽、设置吸附屏幕边界距离。 https://blog.csdn.net/qq_37860634/article/details/146408896 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值