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');
});
這版邏輯差不多,但用 addClass/removeClass 交替,元素進入畫面加上 active、離開畫面又拿掉,捲上捲下都會反應,適合搭配 CSS transition 做那種「捲到才淡入,捲走就淡出」的效果。
這個做法的代價
scroll 事件觸發得非常頻繁,使用者捲動的每一影格都可能觸發一次,裡面又跑了 .offset()、.scrollTop()、.height() 這些會強制瀏覽器重新計算版面(reflow)的操作,元素一多,捲動時很容易感覺到卡頓。實務上還需要另外加節流(throttle)或去抖(debounce)處理,這篇筆記裡的版本沒有做這件事。
現在還用得到嗎
這套「算位置、比大小、加 class」的邏輯本身沒有錯,但瀏覽器現在有專門為這個情境設計的 API——IntersectionObserver 不需要監聽 scroll 事件、不需要自己計算 offset,效能上也好上一截,因為它不是每一影格都在算,而是瀏覽器只在元素真正進出可視範圍的交界點才通知你。整理在《IntersectionObserver:取代手動計算捲動位置》。