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

如何实现滚动逐行切换显示图片 保留parallax.js视差效果

逐行滚动切换图片实现方案

核心原则:完全保留原有parallax.js视差效果,不修改其初始化逻辑,仅通过容器控制、自动分行、滚动方向监听实现逐行切换的平滑交互。


实现逻辑

  • 自动识别网格布局下的图片行分组,无需手动给元素添加行标识,适配原有grid跨列规则
  • 容器固定为单行高度、溢出隐藏,初始仅展示第一行内容
  • 加动画锁防止快速滚动时的跳变bug,所有切换使用缓动动画实现平滑过渡
  • 视口尺寸变化时自动重新计算行分组,适配响应式场景

第一步:替换/追加CSS样式

保留原有parallax相关样式,补充动画和布局控制代码:

.parallax-window {
    min-height: 400px;
    background: transparent;
    /* 固定容器高度为单行高度,溢出隐藏 */
    height: 475px;
    overflow: hidden;
    position: relative;
}

.service-box-container{
    display: grid;
    grid-template-columns: repeat(6, 2fr);
    gap: 15px;
    /* 容器平移动画 */
    transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
    width: 100%;
}

.service-box-container .service-box{
    grid-column: span 2;
    /* 单元素进出场动画初始状态 */
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 0.5s ease, transform 0.5s ease;
}
.service-box-container .service-box:nth-child(5n+4), .service-box-container .service-box:nth-child(5n+5) {
    grid-column: span 3;
}
/* 清除figure默认边距,避免高度计算偏差 */
.service-box figure {
    margin: 0;
}
/* 激活行的元素显示状态 */
.service-box-container .service-box.active-row {
    opacity: 1;
    transform: translateY(0);
}

.service-box-container img{
  max-width: 100%;
  width: 100%;
  object-fit: cover;
  object-position: center;
  height: 460px;
  border-radius: 20px;
}

第二步:添加交互JS代码

将以下代码放在原有parallax初始化逻辑之后,不会干扰原有视差效果:

$(document).ready(function(){
    // 原有视差初始化代码完全保留,不做任何修改
    $('.parallax-window').parallax({});

    const $container = $('.service-box-container');
    const $boxes = $('.service-box');
    let currentRow = 0;
    let isAnimating = false;
    const rowHeight = 475; // 与容器高度保持一致
    let rows = [];

    // 自动按网格位置对元素分行
    function groupRows() {
        rows = [];
        let lastTop = -1;
        $boxes.removeClass('active-row');
        $boxes.each(function() {
            const top = Math.round($(this).position().top);
            if (top !== lastTop) {
                rows.push([]);
                lastTop = top;
            }
            rows[rows.length - 1].push($(this));
        });
        showRow(currentRow);
    }

    // 展示指定索引的行
    function showRow(rowIndex) {
        if (rowIndex < 0 || rowIndex >= rows.length || isAnimating) return;
        isAnimating = true;
        // 容器整体位移实现行切换
        $container.css('transform', `translateY(-${rowIndex * rowHeight}px)`);
        // 切换当前行元素激活状态
        $boxes.removeClass('active-row');
        rows[rowIndex].forEach($el => $el.addClass('active-row'));
        
        // 动画结束后解锁
        setTimeout(() => {
            isAnimating = false;
        }, 600);
    }

    // 防抖工具,避免窗口resize时频繁计算
    function debounce(func, wait) {
        let timeout;
        return function() {
            const context = this, args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(context, args), wait);
        };
    }

    // 初始化分行
    groupRows();
    // 窗口尺寸变化时重新计算分行
    $(window).on('resize', debounce(groupRows, 200));

    // 监听滚动方向触发行切换
    let lastScrollY = window.scrollY;
    $(window).on('scroll', function() {
        const currentScrollY = window.scrollY;
        const scrollDown = currentScrollY > lastScrollY;
        // 仅当容器在视口范围内时触发切换
        const rect = $container[0].getBoundingClientRect();
        const containerInView = rect.top < window.innerHeight && rect.bottom > 0;
        
        if (!containerInView || isAnimating) {
            lastScrollY = currentScrollY;
            return;
        }

        currentRow = scrollDown ? currentRow + 1 : currentRow - 1;
        showRow(currentRow);
        lastScrollY = currentScrollY;
    });
});

效果说明

  • 原有parallax.js背景视差效果完全不受影响
  • 页面初始加载仅展示第一行图片
  • 向下滚动时上一行平滑上移淡出,下一行同步淡入展示
  • 向上滚动时自动回退展示上一行内容
  • 快速滚动时不会出现行错乱、跳变问题
  • 窗口大小变化时自动适配网格布局,重新计算行位置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:03:34