可拖拽文本框缩放超出预期:首次正常后续缩放幅度异常放大失控
问题根本成因
- 核心bug是事件监听器无限制重复挂载。你把四个拖拽手柄的mousedown绑定逻辑,写在了外层
textBox的mousedown回调函数内部。每一次用户点击文本框区域触发外层mousedown,代码就会循环给所有手柄重复挂载相同的事件回调,全程没有做解绑或去重处理。
这种逻辑带来的直接效果就是:第一次操作时每个手柄上只有1个监听器,缩放幅度正常;第二次点击文本框后,每个手柄上已经堆叠了2个完全相同的缩放回调,拖拽时两个回调同时执行缩放计算,原本应该调整10px的宽度,实际会被改成20px,幅度直接翻倍;后续每多点一次,就多叠一层监听器,缩放倍率越来越高,最终完全失控。 - 辅助bug是事件冒泡未做拦截。点击手柄时,mousedown事件会从手柄冒泡到父级textBox,触发外层的拖拽移动逻辑,导致移动和缩放的mousemove监听器同时被挂载、同时执行,进一步放大计算偏差。
- 你怀疑的
getBoundingClientRect()使用问题本身不是根因,只是在重复绑定监听器的前提下,每次mousemove时拿到的rect是已经被其他同作用域监听器修改过的实时尺寸,不是用户拖拽前的基准尺寸,会让计算偏差被层层放大。 - 你提到的prevX/prevY重置逻辑本身没有错,错在每个重复挂载的监听器有自己独立的闭包作用域,各自维护自己的prevX/prevY值,没有同步机制,自然会算出完全失真的位移结果。
具体修复方案
- 把手柄的事件绑定逻辑从外层mousedown函数中移出,放在组件初始化阶段执行,只绑定一次就够了,不要每次触发mousedown就重复绑定。
- 点击手柄时,通过
e.stopPropagation()阻止事件冒泡,避免触发外层textBox的拖拽移动逻辑,让移动、缩放两个流程完全隔离。 - 调整逻辑判断顺序:当检测到是手柄触发的mousedown时,直接进入缩放流程,不要提前挂载外层的拖拽移动监听器。
- 缩放计算时,优先在拖拽启动时刻一次性获取文本框的初始位置、初始宽高作为计算基准,不要在每次mousemove时重新拿实时rect当计算基准,避免因为样式修改带来的尺寸抖动。
修复后的核心逻辑参考:
// 初始化阶段只绑定一次手柄事件,绝对不要写在mousedown回调内部 const handles = document.querySelectorAll('.handle'); handles.forEach(handle => { handle.addEventListener('mousedown', (e) => { e.stopPropagation(); // 阻断事件冒泡,不触发外层拖拽逻辑 isResizing = true; // 拖拽启动时一次性获取基准值,后续计算都基于这个基准 const startX = e.clientX; const startY = e.clientY; const startRect = textBox.getBoundingClientRect(); const mousemove = (e) => { const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; // 按不同手柄的位置计算宽高、坐标偏移 if (handle.classList.contains('se')) { textBox.style.width = startRect.width + deltaX + 'px'; textBox.style.height = startRect.height + deltaY + 'px'; } else if (handle.classList.contains('sw')) { textBox.style.width = startRect.width - deltaX + 'px'; textBox.style.left = startRect.left + deltaX + 'px'; textBox.style.height = startRect.height + deltaY + 'px'; } else if (handle.classList.contains('ne')) { textBox.style.width = startRect.width + deltaX + 'px'; textBox.style.height = startRect.height - deltaY + 'px'; textBox.style.top = startRect.top + deltaY + 'px'; } else { textBox.style.width = startRect.width - deltaX + 'px'; textBox.style.left = startRect.left + deltaX + 'px'; textBox.style.height = startRect.height - deltaY + 'px'; textBox.style.top = startRect.top + deltaY + 'px'; } } const mouseup = () => { window.removeEventListener('mousemove', mousemove); window.removeEventListener('mouseup', mouseup); isResizing = false; } window.addEventListener('mousemove', mousemove); window.addEventListener('mouseup', mouseup); }) })
内容的提问来源于stack exchange,提问作者DemoNemo5
相关产品推荐
相关产品推荐

