以下仅用于学习记录!侵权请联系删除!
需求说明
原生效果就想要实现和 elementui 中 el-table 的头部可以 resizable 功能。
调研
找到一个插件,使用的时命令来定制列宽,https://github.com/onmotion/vue-columns-resizable
可以通过 npm / yarn 下载,
但是本人下载之后,发现此插件的不适用于当前需求,因为命令的inserted 执行时期:被绑定元素插入父节点时调用(仅保证父节点存在,不一定已被插入文档中),即此插件运用原理时加一些模拟的div来设置,但是如果 当前节点还没有具体宽高时 div 会没有宽高。
所以放弃该方法,然后基于需求,也搜到很多用原生写的,主要就是通过 onmousemove、onmouseout、onmousedown
三个事件来编写相关的逻辑。
直接掏出 element-ui 的 .\node_modules\element-ui\packages\table\src\table-header.js
文件学习这边是怎么实现的。
代码实现
本次具体实现的代码如下:
data() {
return {
dragState: {}, // 记录子表的列宽移动的一些数值
dragging: false, // 子表是否在重置列宽
}
},
methods: {
handleMouseMove(event) {
let target = event.target
while (target && target.tagName !== 'TH') {
target = target.parentNode
}
if (!this.dragging) {
let rect = target.getBoundingClientRect()
const bodyStyle = document.body.style
if (rect.width > 12 && rect.right - event.pageX < 8) {
// 拖拽的鼠标样式
bodyStyle.cursor = 'col-resize'
} else if (!this.dragging) {
bodyStyle.cursor = ''
}
}
},
handleMouseOut() {
document.body.style.cursor = ''
},
handleMouseDown(event) {
// 开始拖拽
this.dragging = true
// 当前拖拽的列所在的表格
let tableEl = event.target
// 当前所在列(单元格)
let thEL = event.target
while (tableEl && tableEl.tagName !== 'TABLE') {
tableEl = tableEl.parentNode
}
// 获取列宽拖拽的显示线(拖拽线)
let resizeProxy = tableEl.querySelector(
'.el-table__column-resize-proxy'
)
while (thEL && thEL.tagName !== 'TH') {
thEL = thEL.parentNode
}
const columnRect = thEL.getBoundingClientRect()
thEL.classList.add('noclick')
this.dragState = {
startMouseLeft: event.clientX, // 鼠标开始的地方
columnWidth: columnRect.width, // th开始拖拽的宽度
}
resizeProxy.style.top = tableEl.getBoundingClientRect().top + 'px'
resizeProxy.style.height =
tableEl.getBoundingClientRect().height + 'px'
resizeProxy.style.left = this.dragState.startMouseLeft + 'px'
resizeProxy.classList.remove('dn')
document.onselectstart = function () {
return false
}
document.ondragstart = function () {
return false
}
const handleMouseMove = event => {
// 拖拽中,拖拽线与鼠标的位置同步
resizeProxy.style.left = event.clientX + 'px'
}
const handleMouseUp = () => {
if (this.dragging) {
// 拖拽完毕
const { startMouseLeft, columnWidth } = this.dragState
const finalLeft = parseInt(resizeProxy.style.left, 10)
const columnWidthDiff = finalLeft - startMouseLeft
const finalColumnWidth = columnWidthDiff + columnWidth
const columnMinWidth = parseInt(thEL.style.minWidth, 10)
thEL.style.width = finalColumnWidth + 'px'
// 当单元格宽度改变时 表格宽度也进行改变: 1)有最小宽度时宽度改变了 2)无最小宽度时
if (
(columnMinWidth && finalColumnWidth >= columnMinWidth) ||
!columnMinWidth
) {
tableEl.style.width =
columnWidthDiff + tableEl.clientWidth + 'px'
}
document.body.style.cursor = ''
this.dragging = false
this.dragState = {}
resizeProxy.classList.add('dn')
}
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
document.onselectstart = null
document.ondragstart = null
setTimeout(function () {
thEL.classList.remove('noclick')
}, 0)
}
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
},
}
<table>
<thead>
<tr><th @mousemove="handleMouseMove" @mouseout="handleMouseOut" @mousedown="handleMouseDown"></th></tr>
</thead>
</table>
思路梳理
在 th 的三个事件中主要是在鼠标按下事件中,开始拖拽,给 document 来增加响应鼠标移动和松开的事件,记录 当前鼠标的 X 方向的位置,document 的移动事件记录 鼠标的移动之后 X 方向的位置,document 松开事件去进行 设置拖拽之后单元格的宽度和表格的宽度,拖拽完毕。
网友评论