那麼這是怎麼實現的呢?本文將引用烏徒幫的跟隨屏幕滾動代碼,對此效果做詳解。
一、原始代碼
下面是烏徒幫的跟隨屏幕滾動代碼,它的作用域為烏徒幫網頁兩側的邊欄,以及雙擊屏幕後的右側隱藏欄。
. 代碼如下:
var $catalogueOffsetTop = $('aside#catalogue').offset().top;
var $archiveOffestTop = $('aside#archive').offset().top;
var $archiveOffestLeft = $('aside#archive').offset().left;
$(window).bind('scroll resize',function(){
// #right-area的跟隨屏幕滾動效果
if($('#right-area').height() <= $(window).height()){
$('#right-area').stop(true,true).animate({'top': $(document).scrollTop() + 'px'},800);
}else if($('#right-area').height() > $(window).height() && $('#right-area').height() < $(document).height()){
// 這段范圍內是最關鍵的,允許滑動
if(($(document).scrollTop() + $(window).height()) <= $('#right-area').height()){
$('#right-area').stop(true,true).css('top','0');
}else if(($(document).scrollTop() + $(window).height()) < $(document).height()){
$right_top = $(document).scrollTop() + $(window).height() - $('#right-area').height();
$('#right-area').stop(true,true).animate({'top': $right_top + 'px'},800);
}else{
$right_top = $(document).height() - $('#right-area').height();
$('#right-area').stop(true,true).css({'top': $right_top + 'px'});
//alert($(document).scrollTop() + $(window).height() - $(document).height());
}
}else if($('#right-area').height() >= $(document).height()){
$('#right-area').height($(document).height()).stop(true,true).css({'overflow':'hidden','overflow-y':'scroll'});
}
if($(document).scrollLeft() == 0){ // 只有在屏幕處於左側的時候才進行下面的跟隨滾動,同時需要注意下面的if($(window).width() > 1024),是為了防止在小屏幕下還發生這種變化
// aside#catalogue的上下滑動
if($('aside#catalogue').outerHeight() < $(window).height()){
if($(document).scrollTop() <= $catalogueOffsetTop){
$('aside#catalogue').css({'position':'static','top':$catalogueOffsetTop});
if($(window).width() > 1024)$('#main').css({'padding-left':'0'});
}else{
$('aside#catalogue').css({'position':'fixed','top':'0'});
if($(window).width() > 1024)$('#main').css({'padding-left':$('aside#catalogue').outerWidth() + 5 + 'px'});
}
}else if($('aside#catalogue').height() >= $(window).height() && $('aside#catalogue').outerHeight() < ($('footer').offset().top - $catalogueOffsetTop)){
if(($(document).scrollTop() + $(window).height()) <= ($('aside#catalogue').outerHeight() + $catalogueOffsetTop)){
$('aside#catalogue').css({'position':'static','top':$catalogueOffsetTop});
if($(window).width() > 1024)$('#main').css({'padding-left':'0'});
}else if(($(document).scrollTop() + $(window).height()) < $('footer').offset().top){
$catalogue_top = $(window).height() - $('aside#catalogue').outerHeight() - 20;
$('aside#catalogue').css({'position':'fixed','top': $catalogue_top + 'px'});
if($(window).width() > 1024)$('#main').css({'padding-left':$('aside#catalogue').outerWidth() + 5 + 'px'});
}else{
$catalogue_top = $(window).height() - $('aside#catalogue').outerHeight() - 20 - ($(document).height() - $('footer').offset().top);
$('aside#catalogue').css({'position':'fixed','top':$catalogue_top + 'px'});
if($(window).width() > 1024)$('#main').css({'padding-left':$('aside#catalogue').outerWidth() + 5 + 'px'});
}
}
// aside#archive的上下滑動
if($('aside#archive').outerHeight() < $(window).height()){
if($(document).scrollTop() <= $archiveOffestTop){
$('aside#archive').css({'position':'static','top':$archiveOffestTop,'left':$archiveOffestLeft + 'px'});
}else{
$('aside#archive').css({'position':'fixed','top':'0','left':$archiveOffestLeft + 'px'});
}
}else if($('aside#archive').height() >= $(window).height() && $('aside#archive').outerHeight() < ($('footer').offset().top - $archiveOffestTop)){
if(($(document).scrollTop() + $(window).height()) <= ($('aside#archive').outerHeight() + $archiveOffestTop)){
$('aside#archive').css({'position':'static','top':$archiveOffestTop,'left':$archiveOffestLeft + 'px'});
}else if(($(document).scrollTop() + $(window).height()) < $('footer').offset().top){
$catalogue_top = $(window).height() - $('aside#archive').outerHeight();
$('aside#archive').css({'position':'fixed','top': $catalogue_top + 'px','left':$archiveOffestLeft + 'px'});
}else{
$catalogue_top = $(window).height() - $('aside#archive').outerHeight() - ($(document).height() - $('footer').offset().top);
$('aside#archive').css({'position':'fixed','top':$catalogue_top + 'px','left':$archiveOffestLeft + 'px'});
}
}
}else{ // 如果屏幕不處於左側,就讓這兩個跟隨歸位
$('aside#catalogue').css({'position':'static','top':$catalogueOffsetTop});
$('#main').css({'padding-left':'0'});
$('aside#archive').css({'position':'static','top':$archiveOffestTop,'left':$archiveOffestLeft + 'px'});
}
}).scroll().resize();
網絡上有很多相關的代碼,更有7行代碼解決此問題的方法,甚至還有通用性的插件來實現此效果。然而它們都太過普遍化,對於不同的網站,特殊性不同,在一些細節上要做更多的考慮。
二、選擇用什麼方式跟隨屏幕滾動 方案有三種:
1、使用position:absolute;然後對top值進行動態賦值;
2、使用position:fixed;然後對top值進行動態賦值;
3、對padding-top或margin-top進行動態賦值;
前兩種都是用到了postion對元素的位置進行安排,和float一樣,position將元素從正常的文本流中拖出來。而padding或margin的方法則是控制元素的邊距來實現。到底哪一種好呢?
使用position:absolute;會出現滾動時發生抖動(火狐中不會) ,使用padding-top時會讓有背景的元素看上去難看,也會發生抖動,使用position:fixed不支持IE6,使用margin-top沒有嘗試過,應該會發生抖動。本段代碼選擇的是position:fixed,唯一不發生抖動的方案,但是在IE6下不會有該效果。
三、要考慮的情況 之所以烏徒幫要將本站的代碼拿出來講解,是因為網上的代碼沒有具體分析,很多問題都沒有考慮到。
1、要跟隨的元素的高度和屏幕的高度進行比較
網上所有的代碼考慮的是該區域的高度小於窗口高度的情況,因此代碼很簡單。當區域高度等於和大於窗口高度時,我們又會有新的考慮。
2、如果區域高度超出窗口,何時開始跟隨滾動?
這要看我們想給用戶展示什麼,如果是一個廣告,如果是一段文字,如果是一個列表。我的設計是,當屏幕往下滾動,但是還沒有將要顯示的元素全部顯示完整時,不進行任何效果,當屏幕滾動到元素的底部臨界處時,效果觸發,再往下滾動時,元素的底端和屏幕的底端對齊,元素的下部一直呈現在屏幕內。當然,不同的網頁,你的設計自然不同,你也可能設計為,向下滾動時先沒有效果,當滾動到某一個廣告之後,這個廣告和屏幕頂端對齊跟隨滾動。
圖一 跟隨屏幕滾動邏輯設計圖
從圖一中我們來看一下這一設計思路。圖中綠色部分為要跟隨滾動的區域,灰色部分為整個網頁,淺灰色部分為屏幕(能看到的區域)我們通過向下移動淺灰色的屏幕來模擬往下滾動滾動條。在①的階段為初始階段,這個時候網頁一切照初始運作,沒有任何動作。到②的階段,屏幕向下滾動到一個臨界點,即要跟隨滾動區域的最低端。第③階段是滾動過臨界點之後,元素開始跟隨屏幕滾動,我們可以看到,元素的底端和屏幕的底端對齊,元素的頂端已經看不到。第④個階段的屏幕滾動到底部,可以想象,網頁的底部是存在一些版權信息的,元素不能跟隨滾到底部把這些信息遮住,因此在紅線的地方就不再跟隨滾動。
這是屏幕向下滾動的示意圖,當屏幕向上滾動時,這是這個順序的逆向。但是還有另外一個考慮,當屏幕在向上滾動時實現和向下滾動初始狀的一種效果,即臨界點是此時④中的綠色區域頂端,向上滾動時屏幕頂端和元素頂端對齊。出於技術難度的考慮,烏徒幫沒有實現這一效果。
3、數和量的計算
在滾動時,我們要掌握好那些量是變化的,哪些又不變,在不變中找變,在變中找不變,總之要保持頭腦清晰,分清如何去計算各種高度關系。
在圖一中,我用了一條藍色豎線來輔助高度計算,用紅色的線指示屏幕和元素的位置,將藍色豎線分成了a、b、c、d、e、f六段。那麼他們之間有著哪些變化數量關系呢?(我們將綠色區域的元素定義為#myDiv,將版權信息在內的底部定義為#footer)
a+b+c+d+e+f=$(document).height();//文檔高度,固定值
. 代碼如下:
a= $('#myDiv').offset().top;//#myDiv頂部到文檔頂部的初始值,隨著滾動,$('#myDiv').offset().top將會變化
b=$('#myDiv').height();//元素的高度,固定值
a+b+c=$(window).scrollTop()=$(docment).scrollTop();//滾動條的位置,即文檔頂端到當前屏幕頂端的距離,不斷變化中
d=$(window).height();//屏幕的高度,固定值
f=$('#footer').height();//#footer的高度,固定值
a+b+c+d+e=$('#footer').offset().top=$(document).height()-$('#footer').height();//#footer頂部到文檔頂部的距離,固定值,不過需要注意的是,$('#footer').offset().top+$('#footer').height()並非一定等於$(document).height(),你要看#footer下面是否已經沒有了空白。
在整個變化過程中,變化的值只有$(window).scrollTop()=$(docment).scrollTop()和$('#myDiv').offset().top,因此我們要抓住這些值之間的加減數量關系,做好邏輯判斷和賦值。
4、值在什麼時候獲取
你可以看到,我在scroll事件之前事先獲取了
. 代碼如下:
var $catalogueOffsetTop = $('aside#catalogue').offset().top;
var $archiveOffestTop = $('aside#archive').offset().top;
var $archiveOffestLeft = $('aside#archive').offset().left;
正是由於他們在scroll事件發生時會發生變化,因此要提前存放在變量中。
四、特殊情況特殊考慮
在寫出這麼多代碼之前,我曾想過寫出一個可以通用的代碼,然而事情並非那麼簡單,在烏徒幫中,三個要滾動 的區域都具有特殊性,因此必須認真考慮他們的事件邏輯和仔細賦值。
1、元素是否自由隨意
由於烏徒幫雙擊屏幕滑向右側時出現的區域是自由的,頂部和底部沒有阻擋信息,因此我們的處理更方便一些,不用獲取頂部距離的初始值和考慮滾到底部時空出一段。但是仍然要考慮下面第2點,屏幕和元素高度的比較。
而對於邊側欄的滾到,我們要考慮邊側欄頂部到文檔頂部還有一段距離,底部還有版權信息。滾到的位置要通過上文獲得的值,再配合css中獲得的值進行精確計算。
2、判斷元素的高度和屏幕高度之間的關系
當元素高度小的時候,我們的處理比較簡單,只需要將元素頂端和屏幕頂端對齊,和上面第1點結合,也會出現不同的情況:如果元素頂部到文檔頂部還有一段距離的話,我們還不能屏幕一滾動就開始讓它和屏幕頂端對齊,而必須滾到它的頂端這個臨界點的時候才可以開始。
而當元素的高度大於屏幕的高度的時候,我們要進行更復雜的判斷,和第1點判斷何時開始跟隨滾動:只有當屏幕的底端和元素底端對齊時,元素開始跟隨屏幕滾動。
但是還有一種情況,即元素的高度超出了我們想要的高度,我們可以使用overflow來對元素進行處理,這時我們通過元素的高度和頁面中一些固定值的比較來處理這一環節。烏徒幫通過比較右側元素的高度和底部的關系來進行overflow的處理:
. 代碼如下:
......
}else if($('#right-area').height() >= ($('footer').offset().top + $('footer').height())){
$('#right-area').height($('footer').offset().top + $('footer').height()).stop(true,true).css({'overflow':'hidden','overflow-y':'scroll'});
}
3、自己網頁內特殊情況的變化
烏徒幫由於左右還可以滾動,因此產生了一系列問題,position:fixed時左右方向上元素的距離並沒有固定值,因此在進行左右滾動時,元素會遮住滾動完的屏幕,因此我又對$(document).scrollLeft()進行了判斷,進行了一些處理。
另外,烏徒幫還是一個自適應的網頁設計網站,在不同寬度的屏幕上顯示的效果也不同,js的特點是當屏幕發生變化時仍然起作用,因此,我也增加了屏幕寬度的判斷。
總結
在跟隨屏幕滾動這個問題上,原始的思路是很簡單的,即通過本文列舉的三種方案進行位置或距離的動態改變,然而,要在具體細節上把握好,必須對動態變化中的各個數值有所把握。於此同時,結合自己的網頁,對不同情況下的動態效果有一個好的設計和規劃,也是實現跟隨屏幕滾動的關鍵環節。