样式变更
This commit is contained in:
@@ -3,15 +3,18 @@
|
||||
* 隐藏表格的横向滚动条,按住鼠标左键横向拖动即可查看后面的字段。
|
||||
* 通过 document 事件委托全局生效,所有 el-table 自动支持,无需逐页绑定。
|
||||
*
|
||||
* 手感优化:
|
||||
* - 文本选中已在全局样式中禁用(user-select: none),拖拽过程不会再误选文字
|
||||
* - 位移以横向为主且超过阈值才触发滚动,轻微手抖、普通点击、纵向拖拽(如行排序)不受影响
|
||||
* - 光标仅在真正拖动时变为抓手,平时保持默认
|
||||
* - 拖拽结束(确实发生了滚动)后屏蔽本次 click,避免误触发行点击、复选框等
|
||||
* 实现要点(全面优化):
|
||||
* - 采用 Pointer Events + setPointerCapture:按下瞬间把指针捕获到滚动容器上,
|
||||
* 之后无论光标移动到固定列、表格下方还是窗口外,移动事件都不会中断,真正"跟手"。
|
||||
* - requestAnimationFrame 合并每帧滚动,避免高频事件造成卡顿/闪烁。
|
||||
* - 横向位移超过阈值即触发,允许轻微斜向/弧线;仅当纵向明显占优时才不触发,
|
||||
* 保留普通点击与纵向拖拽(如行排序)的区分。
|
||||
* - 拖拽期间整页锁定光标与文本选中,光标保持默认箭头,不闪变为抓手/手型。
|
||||
* - 拖拽结束(确实发生了滚动)后屏蔽本次 click,避免误触发行点击、复选框等。
|
||||
*/
|
||||
const DRAG_THRESHOLD = 4 // 横向位移超过该像素且以横向为主,才判定为拖动
|
||||
const DRAG_THRESHOLD = 2 // 横向位移超过该像素即判定为拖动,越小越跟手
|
||||
|
||||
// 在这些元素上按下鼠标不触发拖拽,保证按钮、复选框等交互不受影响
|
||||
// 在这些元素上按下不触发拖拽,保证按钮、复选框等交互不受影响
|
||||
const INTERACTIVE_SELECTOR = [
|
||||
'a', 'button', 'input', 'textarea', 'select', 'label',
|
||||
'.el-button', '.el-checkbox', '.el-radio', '.el-switch', '.el-select',
|
||||
@@ -20,77 +23,112 @@ const INTERACTIVE_SELECTOR = [
|
||||
].join(',')
|
||||
|
||||
let wrapper = null
|
||||
let pointerId = null
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let startScrollLeft = 0
|
||||
let dragging = false
|
||||
let rafId = null
|
||||
let pendingScrollLeft = 0
|
||||
|
||||
function isHorizontallyScrollable(el) {
|
||||
return el.scrollWidth > el.clientWidth + 1
|
||||
}
|
||||
|
||||
function handleMouseDown(e) {
|
||||
if (e.button !== 0) return
|
||||
// 每帧只应用一次滚动位置,提升平滑度
|
||||
function applyScroll() {
|
||||
if (!wrapper) return
|
||||
wrapper.scrollLeft = pendingScrollLeft
|
||||
rafId = null
|
||||
}
|
||||
|
||||
function handlePointerDown(e) {
|
||||
if (e.button !== 0 && e.pointerType === 'mouse') return
|
||||
if (e.target.closest(INTERACTIVE_SELECTOR)) return
|
||||
const wrap = e.target.closest('.el-table__body-wrapper')
|
||||
// 固定列(如 fixed="right" 的操作列)渲染在 .el-table__fixed-body-wrapper 中,悬浮覆盖在
|
||||
// 表体右侧,需一并识别;最终统一落到真正负责横向滚动的 .el-table__body-wrapper 上
|
||||
const anyWrapper = e.target.closest('.el-table__body-wrapper, .el-table__fixed-body-wrapper')
|
||||
if (!anyWrapper) return
|
||||
const table = anyWrapper.closest('.el-table')
|
||||
const wrap = table && table.querySelector('.el-table__body-wrapper')
|
||||
if (!wrap || !isHorizontallyScrollable(wrap)) return
|
||||
|
||||
wrapper = wrap
|
||||
pointerId = e.pointerId
|
||||
startX = e.clientX
|
||||
startY = e.clientY
|
||||
startScrollLeft = wrap.scrollLeft
|
||||
pendingScrollLeft = startScrollLeft
|
||||
dragging = false
|
||||
|
||||
// 关键:捕获指针,之后所有 pointermove/pointerup 都持续派发到容器,
|
||||
// 即使光标移出表体/窗口也不中断
|
||||
if (wrap.setPointerCapture && e.pointerId != null) {
|
||||
try { wrap.setPointerCapture(e.pointerId) } catch (err) { /* 忽略捕获失败 */ }
|
||||
}
|
||||
// 阻止默认行为,抑制原生图片/文本拖拽与选中
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
function handleMouseMove(e) {
|
||||
if (!wrapper) return
|
||||
function handlePointerMove(e) {
|
||||
if (!wrapper || e.pointerId !== pointerId) return
|
||||
e.preventDefault()
|
||||
const dx = e.clientX - startX
|
||||
const dy = e.clientY - startY
|
||||
|
||||
if (!dragging) {
|
||||
// 轻微位移视为点击,不触发滚动;位移必须以横向为主,避免与纵向拖拽(如行排序)冲突
|
||||
if (Math.abs(dx) <= DRAG_THRESHOLD || Math.abs(dx) <= Math.abs(dy)) return
|
||||
// 开始拖动:以当前位置为基准,避免起始跳变
|
||||
// 仅当纵向明显占优(超过横向 3 倍且超过 12px)才判定为纵向操作、不触发横向拖动,
|
||||
// 其余情况(含轻微斜向/弧线)都正常触发,避免"拖不动"
|
||||
if (Math.abs(dx) <= DRAG_THRESHOLD || (Math.abs(dy) > Math.abs(dx) * 3 && Math.abs(dy) > 12)) return
|
||||
dragging = true
|
||||
startX = e.clientX
|
||||
startY = e.clientY
|
||||
startScrollLeft = wrapper.scrollLeft
|
||||
wrapper.classList.add('is-dragging')
|
||||
// 整页锁定光标与文本选中,保证拖拽手感一致(光标不闪变、文字不误选)
|
||||
document.body.classList.add('el-table-dragging')
|
||||
}
|
||||
e.preventDefault()
|
||||
wrapper.scrollLeft = startScrollLeft - (e.clientX - startX)
|
||||
|
||||
// 记录目标位置,交由 rAF 统一应用
|
||||
pendingScrollLeft = startScrollLeft - (e.clientX - startX)
|
||||
if (rafId == null) rafId = requestAnimationFrame(applyScroll)
|
||||
}
|
||||
|
||||
function handleMouseUp() {
|
||||
function endDrag(blockClick) {
|
||||
if (!wrapper) return
|
||||
if (rafId != null) {
|
||||
cancelAnimationFrame(rafId)
|
||||
rafId = null
|
||||
}
|
||||
wrapper.classList.remove('is-dragging')
|
||||
if (dragging) {
|
||||
document.body.classList.remove('el-table-dragging')
|
||||
if (blockClick && dragging) {
|
||||
// 拖拽确实发生了滚动,屏蔽本次 click,避免误触发行点击、复选框等
|
||||
const blockClick = (ev) => {
|
||||
const suppress = (ev) => {
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
document.removeEventListener('click', blockClick, true)
|
||||
document.removeEventListener('click', suppress, true)
|
||||
}
|
||||
document.addEventListener('click', blockClick, true)
|
||||
document.addEventListener('click', suppress, true)
|
||||
}
|
||||
wrapper = null
|
||||
pointerId = null
|
||||
dragging = false
|
||||
}
|
||||
|
||||
// 鼠标拖出窗口外时清理拖拽状态,防止状态残留
|
||||
function handleMouseLeave() {
|
||||
if (!wrapper) return
|
||||
wrapper.classList.remove('is-dragging')
|
||||
wrapper = null
|
||||
dragging = false
|
||||
function handlePointerUp(e) {
|
||||
if (e.pointerId !== pointerId) return
|
||||
endDrag(true)
|
||||
}
|
||||
|
||||
function handlePointerCancel() {
|
||||
endDrag(false)
|
||||
}
|
||||
|
||||
const install = function() {
|
||||
if (install.installed) return
|
||||
install.installed = true
|
||||
document.addEventListener('mousedown', handleMouseDown, false)
|
||||
document.addEventListener('mousemove', handleMouseMove, false)
|
||||
document.addEventListener('mouseup', handleMouseUp, false)
|
||||
document.addEventListener('mouseleave', handleMouseLeave, false)
|
||||
document.addEventListener('pointerdown', handlePointerDown, true)
|
||||
document.addEventListener('pointermove', handlePointerMove, true)
|
||||
document.addEventListener('pointerup', handlePointerUp, true)
|
||||
document.addEventListener('pointercancel', handlePointerCancel, true)
|
||||
}
|
||||
|
||||
export default install
|
||||
|
||||
Reference in New Issue
Block a user