在 swiper 启用 `freemode` 时,原生事件(如 `settranslate`)仅在拖拽开始或瞬时触发,无法持续响应滑动过程;本文提供基于 `setinterval` 的高频率轮询方案,确保 `setimagepositions()` 在整个拖拽与惯性滚动期间持续执行,实现平滑的视差、位移与旋转动画。
Swiper 的 freeMode: true 模式下,用户可自由拖拽并产生惯性滚动,但 Swiper 不提供持续触发的生命周期事件(如 onDrag, onScrolling, onFreeModeTransitionEnd 等在较新版本中仍不覆盖全程)。因此,依赖 setTranslate 事件只能捕获离散的 translate 变更点,导致 setImagePositions() 调用不连贯,动画卡顿或中断。
为解决这一限制,核心思路是:在每次 setTranslate 触发时,启动一个高频定时器,在滑动活跃期持续调用渲染逻辑,并在合理超时后自动清理。该方案模拟了“实时监听”,兼顾性能与响应性。
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');
// 计算元素相对于 Swiper 容器中心的归一化位置(-1 ~ 1)
const slideLeft = $slide.offset().left;
const slideWidth = $slide.width();
const swiperLeft = $('.swiper').offset().left;
const swiperWidth = $('.swiper').width();
const centerOffset = slideLeft + slideWidth / 2 - (swiperLeft + swiperWidth / 2);
const progressCenter = centerOffset / (swiperWidth + slideWidth); // 归一化 [-~0.5, ~0.5]
// 应用复合变换:垂直偏移 + 旋转(基于中心进度)
$inner.css(
'transform',
`translateY(${verticalAmount * progressCenter}%) rotate(${rotationAmount * progressCenter}deg)`
);
});
}
// 关键:监听 setTranslate,动态启停轮询
swiper.on('setTranslate', () => {
// 清理上一次可能残留的定时器
if (freeMoveInterval) {
clearInterval(freeMoveInterval);
}
// 启动高频轮询(10ms 间隔 ≈ 100fps,足够流畅)
let loopCount = 0;
const maxLoops = 300; // 总执行上限(约 3 秒),防内存泄漏
freeMoveInterval = setInterval(() => {
if (loopCount++ >= maxLoops) {
clearInterval(freeMoveInterval);
freeMoveInterval = null;
return;
}
setImagePositions();
}, 10);
});
// 初始化渲染(避免首屏空白)
setImagePositions();通过该方案,你的视差动画将真正“跟随手指移动”,从拖拽起始、滑动中到惯性结束全程响应,显著提升交互沉浸感与专业度。