Vue实现拖动鼠标改变DIV高度

需求:模拟微信PC客户端的聊天窗口-发送框,通过拖动发送框的上沿,可手动调节高度。
想起 element-ui 的 table 组件,列可以通过拖动改变宽度,于是去扒源码...

1.思路剖析:

element-ui 思路:

表格-列边缘,拖出一条线(可见),线随着鼠标的拖动而移动,当鼠标放开,线消失,列重置宽度。

我的思路:

文本框-上边缘,添加一条线(不可见),鼠标拖动线时,实时改变文本框-包裹框的高度,从而改变文本框的高度。

2.注意事项:

  • 涉及的鼠标事件:mousedown、mousemove、mouseup。
  • mousedown 时,需要禁止用户选择网页中的文字和拖动元素;mouseup 时恢复。
  • 文本框-包裹框 = 文本框 + 线,文本框的 height 需设为 calc(100% - 线高度)。

3.代码实战:

<div id="app">
    <template>
      <h2>鼠标拖动 DIV 顶部边缘,改变 DIV 高度</h2>

      <!-- 文本框 wrap -->
      <div 
        ref="rTextareaWrap" 
        class="drag-textarea"
      >
        <!-- 辅助线(用于调整文本框高度) -->
        <div
          ref="rResizeLine"
          class="resize-line"
          @mousedown="handleMouseDown"
        ></div>

        <!-- 文本框 -->
        <textarea></textarea>
      </div>
    </template>
  </div>
body {
  padding-top: 200px;
}
.drag-textarea {
  position: absolute;
  bottom: 200px;
  width: 300px;
  height: 250px;
}
.drag-textarea .resize-line {
  height: 5px;
  cursor: move;
}
.drag-textarea textarea {
  width:100%;
  /* 重点属性 */
  height: calc(100% - 5px);
  padding: 0;
  border: 1px solid #333;
  resize: none;
}
// 枚举 - 文本框 wrap 最大/小高度
const TextAreaWrap = {
  MaxHeight: 400,
  MinHeight: 150
};

new Vue({
  data () {
    return {}
  },
  methods: {
    /**
     * 处理鼠标按下事件
     *
     * @param {MouseEvent} 鼠标事件
     */
    handleMouseDown (event) {
      // 禁止用户选择网页中文字
      document.onselectstart = () => false;
      // 禁止用户拖动元素
      document.ondragstart = () => false;

      // 保存鼠标最后移动的位置(Y轴)
      this.dragState = {
        // 鼠标开始移动的位置(Y轴)
        'startMouseTop': event.clientY,
        // 鼠标最后移动的位置(Y轴)
        'endMouseTop': event.clientY
      };

      // 绑定鼠标移动事件
      document.addEventListener('mousemove', this.handleMouseMove);
      // 绑定鼠标放开事件
      document.addEventListener('mouseup', this.handleMouseUp);
    },

    /**
     * 处理鼠标移动事件
     *
     * @param {MouseEvent} 鼠标事件
     */
    handleMouseMove (event) {
      const { rTextareaWrap, rResizeLine } = this.$refs;
      // 获取鼠标最后移动的位置(Y轴)
      const { endMouseTop } = this.dragState;
      // 获取当前的文本框高度
      const oldTextAreaHeight = rTextareaWrap.getBoundingClientRect().height;
      // 新的文本框高度
      let newTextAreaHeight = 0;

      // 计算鼠标移动的距离
      const distance = Math.abs(
        parseInt(((endMouseTop - event.clientY) * 100).toString(), 10) / 100
      );

      // 若鼠标向上移动
      if (endMouseTop > event.clientY) {
        // 发送框高度达到最大
        if (oldTextAreaHeight >= TextAreaWrap.MaxHeight)
        {
          // 修改光标为可被向下移动
          rResizeLine.style.cursor = 's-resize';
          return false;
        }

        // 计算新的发送框高度
        newTextAreaHeight = oldTextAreaHeight + distance;
      }
      // 若鼠标向下移动
      else
      {
        // 发送框高度达到最小
        if (oldTextAreaHeight <= TextAreaWrap.MinHeight)
        {
          // 修改光标为可被向上移动
          rResizeLine.style.cursor = 'n-resize';
          return false;
        }

        // 计算新的发送框高度
        newTextAreaHeight = oldTextAreaHeight - distance;
      }

      // 兼容鼠标快速拖动的情况:新的发送框高度不能超过最大值
      if (newTextAreaHeight > TextAreaWrap.MaxHeight)
      {
        newTextAreaHeight = TextAreaWrap.MaxHeight;
      }

      // 兼容鼠标快速拖动的情况:新的发送框高度不能小于最小值
      if (newTextAreaHeight < TextAreaWrap.MinHeight)
      {
        newTextAreaHeight = TextAreaWrap.MinHeight;
      }

      // 修改发送框高度
      rTextareaWrap.style.height = newTextAreaHeight + 'px';
      // 修改光标为可移动
      rResizeLine.style.cursor = 'move';

      // 更新鼠标最后移动的位置(Y轴)
      this.dragState.endMouseTop = event.clientY;
    },

    /**
     * 处理鼠标放开事件
     */
    handleMouseUp () {
      // 移除鼠标移动事件
      document.removeEventListener('mousemove', this.handleMouseMove);
      // 移除鼠标放开事件
      document.removeEventListener('mouseup', this.handleMouseUp);
      // 允许用户选择网页中文字
      document.onselectstart = null;
      // 允许用户拖动元素
      document.ondragstart = null;
    }
  }
}).$mount('#app')
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,825评论 6 546
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,814评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,980评论 0 384
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 64,064评论 1 319
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,779评论 6 414
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,109评论 1 330
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,099评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,287评论 0 291
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,799评论 1 338
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,515评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,750评论 1 375
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,221评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,933评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,327评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,667评论 1 296
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,492评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,703评论 2 380

推荐阅读更多精彩内容