返回

解决Particles.js滚动时背景不持续显示的问题

javascript

解决 Particles.js 在页面滚动时不持续显示的问题

使用 Particles.js 作为网页背景,滚动页面时粒子效果却停留在初始屏幕范围,不跟随滚动,底部留白,这实在让人头疼。 这篇文章就来解决这个问题。

问题原因分析

从给出的 CSS 代码 #particles { position: absolute; width: 100%; height: 100%; ... } 来看,问题的根源在于 height: 100%position: absolute 的组合。

  • position: absolute:让 #particles 脱离了文档流,它的尺寸不再跟随内容变化,而是相对于最近的已定位祖先元素(如果没找到,则相对于初始包含块,通常是 viewport)。
  • height: 100%: 这里的 100% 指的是 viewport(视口)的高度,也就是初始屏幕可见区域的高度,并非整个网页内容的高度。

因此,当页面内容超出初始视口高度时, #particles 的高度并不会增加,导致粒子效果无法覆盖滚动后的内容区域。

解决方案

下面针对问题原因提供几个解决方法。

1. 使用 position: fixed

最简单直接的办法是改用 position: fixed

  • 原理: position: fixed 使元素相对于视口固定,这意味着即使滚动页面,元素的位置也会保持不变。 让 #particles 元素始终覆盖整个视口,完美解决。

  • 代码示例:

    #particles {
        position: fixed; /* 关键修改 */
        width: 100%;
        height: 100%;
        z-index: -1;
        background-size: cover;
        background-position: 50% 50%;
        background-repeat: no-repeat;
    }
    
  • 额外建议 : 这个方案基本能搞定大部分场景,简单易行,推荐优先尝试。

2. 使用 JavaScript 动态计算高度

如果非要用 position: absolute;, 可以利用JavaScript计算出网页的真实高度,然后动态地设置给 #particles 元素。

  • 原理: 获取整个网页内容(包括超出视口的部分)的高度, 然后把这个高度赋值给#particles

  • 代码示例 (jQuery):

    $(document).ready(function() {
        function setParticlesHeight() {
            var bodyHeight = $(document).height(); //获取文档的真实高度
            $('#particles').height(bodyHeight);
        }
    
        setParticlesHeight(); // 页面加载时设置一次
    
        $(window).resize(setParticlesHeight); // 窗口大小改变时重新设置
        $(window).scroll(setParticlesHeight); // 页面滚动时设置高度
    });
    

代码示例 (原生JavaScript):

document.addEventListener('DOMContentLoaded', function() {
    function setParticlesHeight() {
       let bodyHeight = document.body.scrollHeight;
       let particles = document.getElementById('particles');
       particles.style.height = bodyHeight + "px";
   }
  
  setParticlesHeight();

  window.addEventListener('resize', setParticlesHeight);
  window.addEventListener('scroll', setParticlesHeight);
});
 ```

*   **额外建议:**  需要留意页面内容动态变化的情况,例如 AJAX 加载新内容、折叠/展开内容等。如果有这种情况,需要在相关事件发生时,也调用 `setParticlesHeight` 函数更新 `#particles` 的高度。 还要考虑到性能问题,`scroll` 事件触发频率较高, 过度频繁的操作会卡顿,所以上面的代码不一定是最优解。

#### 3. 进阶优化:节流(Throttle)与防抖(Debounce)

上面的JS方法在滚动时频繁更新`#particles`的高度, 有优化空间。 可以使用节流(Throttle)或防抖(Debounce)来限制更新频率。

*   **节流 (Throttle):**   规定在一定时间内,只执行一次函数。

*   **防抖 (Debounce):**   在事件停止触发后,延迟一段时间再执行函数。

*  **使用场景举例:**  对于滚动事件, 更适合用节流,因为我们希望在滚动过程中也能看到粒子效果的变化,只是不需要那么频繁地更新。

*   **代码示例(节流 - 使用 Lodash 库):** 

 ```javascript
  $(document).ready(function() {
      function setParticlesHeight() {
          var bodyHeight = $(document).height();
          $('#particles').height(bodyHeight);
      }

     // 使用 Lodash 的 throttle 函数,每 200 毫秒最多执行一次 setParticlesHeight
     var throttledSetParticlesHeight = _.throttle(setParticlesHeight, 200);

      setParticlesHeight();

     $(window).resize(throttledSetParticlesHeight);
      $(window).scroll(throttledSetParticlesHeight); //页面滚动的时候调用节流后的方法
  });
 ```
*  **原生实现throttle (不用库)** :
 ```javascript
   function throttle(func, limit) {
       let inThrottle;
       return function() {
           const args = arguments;
           const context = this;
           if (!inThrottle) {
               func.apply(context, args);
               inThrottle = true;
               setTimeout(() => inThrottle = false, limit);
           }
       }
   }

    function setParticlesHeight() {
         let bodyHeight = document.body.scrollHeight;
         let particles = document.getElementById('particles');
         particles.style.height = bodyHeight + "px";
     }

   const throttledSetParticlesHeight = throttle(setParticlesHeight, 200);

    document.addEventListener('DOMContentLoaded', function() {

       throttledSetParticlesHeight();

       window.addEventListener('resize', throttledSetParticlesHeight);
       window.addEventListener('scroll', throttledSetParticlesHeight);
     });

 ```
* **防抖的例子就不赘述了, 搜索 "javascript debounce" 就能找到很多示例** 
#### 4. 使用 viewport 单位 (vh) 结合 `min-height`

另一种思路是不去精确计算文档高度, 而是让`#particles`元素至少填满一个视口, 并允许它随着内容增加而扩展。

* **原理:** 
- 使用`min-height: 100vh;`确保 `#particles` 的高度至少与视口一样高。
- 使用`position: absolute;`使其相对与设置了`position`为非`static`的父元素。

*   **代码示例:** 

 ```html
 <div style="position: relative;">
   <div id="particles"></div>
   <!-- 页面的其他内容 -->
 </div>

 ```

 ```css
 #particles {
     position: absolute;
     width: 100%;
     min-height: 100vh; /* 关键修改 */
     z-index: -1;
     background-size: cover;
     background-position: 50% 50%;
     background-repeat: no-repeat;
 }

 ```

* **额外建议:** 
- 确保`#particles`的直接或间接父元素设置了 `position: relative;`(或其他非 `static` 值),以便让 `#particles` 相对于它定位。 这一步很重要!
- 如果希望背景在内容较少时也能够撑满整个视口,并且随着内容增长还能保持,这个方案相当不错。

这几个方法根据具体的需求选择就行,通常方法1(`position: fixed;`)最为方便。其他方法可以在特定条件下带来更优化的效果。