You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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部分:
    1. 滑动时不再清空transform,而是计算实时滑动偏移量,让抽屉跟随手指移动。
    2. 滑动结束时,根据滑动距离判断是触发关闭(从当前位置平滑过渡到关闭状态)还是回弹到打开位置。
    3. 触摸开始时记录当前的transform值,避免滑动初始位置出现跳变。

这样修改后,滑动关闭时会从当前滑动的位置平滑过渡到关闭状态,不会再出现复位的问题,同时抽屉的开合过渡也能正常工作。

内容的提问来源于stack exchange,提问作者Oscar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 18:53:15