需求:模拟微信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')