跳到主要內容
前端開發

jQuery 版本的捲動進場動畫:手動比對 offset 與 scrollTop

整理用 jQuery 監聽 resize 與 scroll 事件、手動計算元素位置與可視範圍邊界,判斷元素是否進入畫面並加上 animate.css 動畫 class 的做法,包含只觸發一次的進場動畫與會反覆進出的捲動動畫兩種版本。現在的做法是 IntersectionObserver,整理在新的一篇。

W

練習做活動頁(EDM)常常需要「捲到某個區塊時觸發進場動畫」的效果,每次都要重新想邏輯、翻舊 code,這篇整理一次常用的寫法。

監聽 resize 與 scroll

$(window)
  .resize(function () {
    $(window)
      .on('scroll', function () {
        // 捲動時要做的事...
      })
      .trigger('scroll');
  })
  .trigger('resize');

外層先綁 resize,裡面才綁 scroll,並且用 .trigger('resize').trigger('scroll') 手動觸發一次——這是為了確保頁面剛載入、使用者還沒有真的捲動或縮放視窗之前,也能先執行一次判斷邏輯,不然要等使用者第一次動手捲動或縮放,畫面才會有反應。

捲軸判斷:只觸發一次的進場動畫

var item = $(this);
var windowTop = $(window).scrollTop() + $(window).height();
var pageTop = item.offset().top;

if (windowTop >= pageTop) {
  if (item.hasClass('no-gutters')) {
    item.addClass('animate__animated animate__bounceInRight');
  } else {
    item.addClass('animate__animated animate__bounceInLeft');
  }
}

windowTop 是「目前捲動位置 + 可視窗口高度」,等於可視範圍的最底部在整個頁面上的絕對位置;pageTop 是目標元素頂部的絕對位置。當可視範圍的底部已經超過元素頂部,就代表元素進入畫面了,加上對應的 animate.css 動畫 class。因為只是 addClass,動畫只會播放一次,之後捲上捲下都不會重播。

捲動動畫:進入與消失都反應

$(select).each(function (i) {
  var bottomOfObject = $(this).offset().top + $(this).outerHeight() / 8;
  var bottomOfWindow = $(window).scrollTop() + $(window).height();

  bottomOfWindow > bottomOfObject
    ? $(this).addClass('active')
    : $(this).removeClass('active');
});

這版邏輯差不多,但用 addClassremoveClass 交替,元素進入畫面加上 active、離開畫面又拿掉,捲上捲下都會反應,適合搭配 CSS transition 做那種「捲到才淡入,捲走就淡出」的效果。

這個做法的代價

scroll 事件觸發得非常頻繁,使用者捲動的每一影格都可能觸發一次,裡面又跑了 .offset().scrollTop().height() 這些會強制瀏覽器重新計算版面(reflow)的操作,元素一多,捲動時很容易感覺到卡頓。實務上還需要另外加節流(throttle)或去抖(debounce)處理,這篇筆記裡的版本沒有做這件事。

現在還用得到嗎

這套「算位置、比大小、加 class」的邏輯本身沒有錯,但瀏覽器現在有專門為這個情境設計的 API——IntersectionObserver 不需要監聽 scroll 事件、不需要自己計算 offset,效能上也好上一截,因為它不是每一影格都在算,而是瀏覽器只在元素真正進出可視範圍的交界點才通知你。整理在《IntersectionObserver:取代手動計算捲動位置》。

參考資料

分享這篇