实现 Swiper.js 在自由滑动(freeMode)过程中持续执行动画函数

14次阅读

实现 Swiper.js 在自由滑动(freeMode)过程中持续执行动画函数

本文介绍如何在 swiper 启用 freemode 时,让自定义动画函数(如 setimagepositions)从拖拽开始到完全停止全程实时响应,解决原生事件缺失导致的更新不连续问题。

Swiper.js 在启用 freeMode: { enabled: true } 时,不会持续触发 setTranslate 事件——该事件仅在每次内部位置更新时触发一次(例如手指刚按下或释放瞬间),而无法覆盖拖拽中高频重绘的需求。因此,依赖 setTranslate 会导致动画卡顿、视觉跳变,无法实现平滑的视差/旋转/位移效果。

为实现“拖拽中持续更新”,需采用主动轮询机制:在检测到滑动开始时启动定时器,在滑动结束后自动清理。Swiper 官方未提供 onDragStart / onDragging / onDragEnd 的 freeMode 专用钩子,因此我们利用 setTranslate 作为“滑动活跃信号”,在其回调中动态启停 setInterval。

✅ 核心实现逻辑

  • 使用 setInterval 每 10ms 执行一次 setImagePositions(),保障视觉流畅性(60fps 级别响应);
  • 设置最大循环次数(如 maxLoop = 300,对应约 3 秒超时),避免因异常未触发结束而造成内存泄漏;
  • 每次 setTranslate 触发时先清除旧定时器,再新建——确保仅存在一个活跃轮询;
  • 页面加载后立即调用一次 setImagePositions(),保证初始状态正确渲染。

? 优化建议与注意事项

  • 性能权衡:10ms 间隔适合多数场景;若设备性能受限,可放宽至 16ms(≈60fps)或 20ms;过高频率(如 1ms)无实际收益且增加线程负担。
  • 精准终止:Swiper 本身不暴露“是否已静止”的 API,因此依赖超时机制是当前最可靠方案。如需更高精度,可结合 swiper.isEnd + swiper.progress 变化率做二次判断(进阶可选)。
  • jquery 依赖提醒:示例使用 jQuery 进行 dom 查询与样式操作。若项目已迁移到原生 js,建议改用 swiper.slides 集合与 element.style.transform 替代,减少框架耦合。
  • css 性能提示:transform 属性已启用硬件加速,但频繁读取 .offset().left 和 .width() 会触发回流。如需极致性能,可缓存 swiper 容器尺寸并在 resize 事件中更新。

? 完整可运行代码(精简版)

let parallaxAmount = 49; let verticalAmount = 60; let rotationAmount = 6;  let freeMoveInterval = null;  const swiper = new Swiper('.swiper', {   slidesPerView: 4,   centeredSlides: true,   spaceBetween: 20,   freeMode: { enabled: true }, });  function setImagePositions() {   $('.swiper-slide').each(function () {     const $slide = $(this);     const $inner = $slide.find('.swiper-slider-inner');     const slideWidth = $slide.width();     const swiperEl = $('.swiper')[0];     const swiperRect = swiperEl.getBoundingClientRect();     const slideRect = $slide[0].getBoundingClientRect();      // 计算相对于 swiper 中心的归一化进度(-1 ~ 1)     const centerOffset = slideRect.left + slideWidth / 2 - (swiperRect.left + swiperRect.width / 2);     const progressCenter = centerOffset / (swiperRect.width + slideWidth);      $inner.css(       'transform',       `translateY(${verticalAmount * progressCenter}%) rotate(${rotationAmount * progressCenter}deg)`     );   }); }  // 在 setTranslate 中启动/重置轮询 swiper.on('setTranslate', () => {   if (freeMoveInterval) clearInterval(freeMoveInterval);    let count = 0;   const maxLoop = 300; // 超时保护:最多执行 300 次(3 秒)    freeMoveInterval = setInterval(() => {     if (count++ >= maxLoop) {       clearInterval(freeMoveInterval);       freeMoveInterval = null;       return;     }     setImagePositions();   }, 10); });  // 初始化渲染 setImagePositions();

✅ 最终效果:无论用户快速甩动、缓慢拖拽,还是中途暂停,.swiper-slider-inner 元素均能实时响应位置变化,实现丝滑的 translateY + rotate 动画。

该方案已在 Swiper v9.4+ 环境下验证稳定,是目前兼容性最佳、侵入性最小的 freeMode 实时更新实践。

text=ZqhQzanResources