JavaScript滑动关闭功能与CSS动画兼容问题求助
首先,问题根源是滑动结束时你清空了transform,导致抽屉瞬间回到完全打开的位置,再触发关闭动画,自然会有跳变。另外,keyframes动画是硬编码的起始/结束状态,不会识别元素当前的位置,而transition是基于元素当前状态的过渡,更适合这种场景。之前用transition时开合有问题,是因为初始状态的设置没做好,调整一下就能解决。
下面是修复后的完整代码:
HTML(无需修改)
<div class="container"> <div class="drawer"> <button class="close">close</button> </div> </div> <button class="open">open</button>
CSS 修改:用transition替代keyframes,调整状态控制
.container { position: fixed; inset: 0; pointer-events: none; height: 100vh; display: flex; justify-content: flex-end; align-items: center; } .drawer { pointer-events: all; display: block; height: 100%; width: 20rem; background: skyblue; transform: translateX(100%); /* 默认处于关闭位置 */ transition: transform 0.3s ease; /* 统一过渡效果 */ } .drawer[data-open] { transform: translateX(0); /* 打开状态 */ } /* 移除原有的keyframes相关代码 */
JS 修改:调整触摸逻辑,保留滑动位置,避免清空transform
const open = document.querySelector(".open"); const close = document.querySelector(".close"); const drawer = document.querySelector(".drawer"); open.addEventListener("click", () => { drawer.setAttribute("data-open", ""); }); close.addEventListener("click", () => { drawer.removeAttribute("data-open"); }); let xTouchStart; let xCurrentTranslate = 0; const slideThreshold = 10; const closeThreshold = 100; const handleTouchMove = (e) => { const touch = e.touches[0]; const deltaX = touch.screenX - xTouchStart; // 只允许向右滑动(关闭方向),超过阈值后跟随手指移动 if (deltaX > slideThreshold) { // 限制最大滑动距离不超过抽屉宽度,避免滑出过多 xCurrentTranslate = Math.min(deltaX - slideThreshold, drawer.offsetWidth); drawer.style.transition = "none"; // 滑动时取消过渡,保证跟随流畅 drawer.style.transform = `translateX(${xCurrentTranslate}px)`; } }; const handleTouchEnd = (e) => { const touch = e.changedTouches[0]; const deltaX = touch.screenX - xTouchStart; drawer.style.transition = ""; // 恢复过渡效果 if (deltaX > closeThreshold) { // 滑动距离达标,触发关闭,从当前位置平滑过渡到关闭状态 drawer.removeAttribute("data-open"); } else { // 滑动距离不够,回弹到打开状态 drawer.style.transform = ""; } // 清理触摸事件监听 drawer.removeEventListener("touchmove", handleTouchMove); drawer.removeEventListener("touchend", handleTouchEnd); drawer.removeEventListener("touchcancel", handleTouchEnd); }; drawer.addEventListener("touchstart", (e) => { const touch = e.touches[0]; xTouchStart = touch.screenX; // 记录触摸开始时的当前transform值,避免滑动初始位置偏差 const computedStyle = window.getComputedStyle(drawer); const matrix = new DOMMatrix(computedStyle.transform); xCurrentTranslate = matrix.e; drawer.addEventListener("touchmove", handleTouchMove); drawer.addEventListener("touchend", handleTouchEnd); drawer.addEventListener("touchcancel", handleTouchEnd); });
修复说明:
- CSS部分:把抽屉默认状态设为
translateX(100%)(完全关闭),data-open时切换到translateX(0)(完全打开),用统一的transition控制过渡,解决了之前用transition时开合异常的问题。 - JS部分:
- 滑动时不再清空transform,而是计算实时滑动偏移量,让抽屉跟随手指移动。
- 滑动结束时,根据滑动距离判断是触发关闭(从当前位置平滑过渡到关闭状态)还是回弹到打开位置。
- 触摸开始时记录当前的transform值,避免滑动初始位置出现跳变。
这样修改后,滑动关闭时会从当前滑动的位置平滑过渡到关闭状态,不会再出现复位的问题,同时抽屉的开合过渡也能正常工作。
内容的提问来源于stack exchange,提问作者Oscar
相关产品推荐
相关产品推荐

