邊做邊學jQuery系列09 - jQuery讓網頁動起來

課程錄製時間: 2009年
jQuery讓網頁動起來

【動畫效果】

網頁動畫效果可以吸引使用者注意,提供較佳的操作體驗。用白話來說,網頁要酷、要炫,適度的動畫效果是不可或缺的。

透過Javascript + CSS的結合,網頁元素就可以自動變化顯現結果,產生動畫,但相關程式設計細節頗為瑣碎。再一次,jQuery又讓複雜的事情變得簡單。

在jquery.js的核心程式裡,即內建了基本的動畫API,例如: 淡入淡出、放大縮小等等,而它們都基於同樣的原理 -- 在一定的時間長度內,將某個或多個CSS屬性值,由數值A遞增或遞減到數值B,這就是animate()函數的使命。原則上只要可以透過數字調整的CSS屬性都可用來產生動畫,例如: top、left、width、height、marin、opacity... 等等,而fadeIn()/fadeOut()說穿了是只調整opacity的animate()、slideDown()/slideUp()不過是純粹改變height的animate()。研究完animate(),就等於把動畫的核心原理看完了,因此就從animate()看起吧!

【animate函數】

animate()函數是所有動態特效的根本,完整animate函數的呼叫方式為: animate(params, duration, easing, callback)。

param是一個匿名物件,跟css(params)時概念相同,裡面是一堆CSS屬性設定,意指要將CSS屬性改變成這些指定值,而這些值都必須包含數值的部分,例如: width, opacity... 等等,jQuery才能逐步改變它的大小。若其中傳入text-decoration, border之類非數值型屬性,會產生Invalid Argument錯誤。另外,除了絕對值外,屬性的數字也可以寫成如: width:"+=200px"的相對表示法,用+=表示增寬200px。(-=為減少)

duration指在希望在多久時間內將CSS屬性調成指定數值。可以傳入字串: "slow"(600ms), "normal"(400ms)或"fast"(200ms)或是直接給數字,如: 1000代表1000ms(1秒)。

easing可用來設定數字變化過程的演算邏輯,內建有兩種: "linear"及"swing",但可以外掛其他easing演算法。網路上已有現成的 jQuery Easing Plugin ,提供如物件落地彈跳的效果。

callback可指定動畫完成後要執行的動作。實際測試後,不難發現animate()是以非同步方式執行的。也就是說,啟動一段五秒的動畫後,下一列的程式碼會立即被執行,並不是等到動畫結束才繼續。因此,若我們希望某段程式在動畫結束後才執行,可透過指定callback的方式達到目的。
例如:
$("#XX").animate({ width: "200px" }, 2000,
    function() { alert("Completed"); });
alert("After");

我們會先看到After,立即按下OK,約兩秒後看到Completed。有趣的是Completed出現的時間並不是After出現後固定的兩秒內,如果我們拖了三秒才按下After alert的OK鈕,則按下後動畫會直接跳到最後結果,並馬上彈出Completed。換句話說,雖然整個UI被alert給凍結了,動畫過程還是會在背景持續依預定節奏跑完。

另外,我們如果連續指定動畫特效在同一個元素上,則這些特效會依序被執行。例如有一個50px寬50px高的<div>:
$("div").animate({ width: "200px" }, 2000).animate({ height: "200px" }, 3000);
變寬及變高的特效會依序發生,也就是先以2秒時間寬度由50px變成200px,接下來3秒再換高度由50px變成200px。

有沒有可能讓二者同時進行呢? 改用animate( param, props )的呼叫方式即可。其中,param是CSS的屬性設定,而props則可以匿名物件的方式傳入duration、easing、complete屬性,再加上兩個進階屬性,queue與step。

queue是一個Boolean,設為false時,先後指定的動畫效果會一起呈現,例如:
$("div").animate({ width: "200px" }, { duration: 2000, queue: false }).animate({ height: "200px" }, 3000);

如此,整個動畫會在3秒內播完,而寬度在2秒內變成200px,第2-3秒間固定在200px;而高度則在3秒內逐步放大到200px。animate({ width: "200px", height: "200px" })的長寬變化必須同時開始同時結束,透過queue: false可以同步播放時間長度不同的動畫效果。

step是一個callback函數,可以讓我們自訂每一階段的元素變化,例如可用來衍生出顏色改變:

$("<div />").appendTo("body")
.css({ backgroundColor: "black", width: "200px", height: "20px" })
.animate({ bgClr: "255" }, 
  { 
    duration: 5000, step: function(now, fx) {
      var n = parseInt(now);
      $(this).css("background-color", 
          "rgb(" + (255-n) + ",0," + n + ")");
    }
  });

在上例中,bgClr是一個虛擬的CSS屬性,step callback會被連續呼叫,五秒內now的值會由0變大到255,我們把它也當成顏色逐漸變化的根據。注意,如果params裡有兩項CSS屬性,每一回合step會被呼叫兩次,有三項就會被呼叫三次,我們可透過類似fx.prop == "width"的方式判別該次step呼叫針對的是哪一項屬性。(註: 此處只是示範自訂step的用途,要做顏色動畫事實上已有現成的Plugin可用)

在特效播放過程中,jQuery允許我們用stop()函數踩剎車中止動畫播放。而stop支援兩個參數: stop(clearQueue, gotoEnd),第一個是clearQueue(Boolean),意指除了目前播放的特效外,後續等待播放的特效是否也一併取消(如先前 $("div").animate({ width: "200px" }, 2000).animate({ height: "200px" }, 3000); 的例子,若在第一段特效過程中stop()且clearQueue=true,則第二段特效也將一併取消)。第二個參數gotoEnd則是決定元素要留在目前播放中途的狀態或是直接跳到播放完成的狀態。

【常用特效函數】

講完了animate的原理,餘下的幾個函數就不再神祕,幾乎都是將特定參數的animate()包裝成常用的函數罷了,以下做個整理:

  • show() 將隱藏的元素立刻顯示出來,若元素已可見則沒有變化。
  • show(speed[, callback]) 在speed指定的時間內(即animate的duration,可以是"slow"、"normal"、"fast"或是數字代表微秒數,以下將不再重覆說明),將width, height由0變到原有尺寸,透明度也明完全透明變成不透明。callback即前述的complete函數,在特效結束後呼叫(稍後在其他函數中用到時便不再重覆說明)。
  • hide() 將顯示的元素立刻隱藏,已隱藏者不受影響。
  • hide(speed[, callback]) 在speed指定的時間內將width, height, opacity縮為0。
  • toggle() 切換元素的顯示狀態,將可見者隱藏,隱藏者顯示出來。
  • toggle(switch) switch為一Boolean變數,true時顯示,false時隱藏。
  • slideDown(speed[, callback]) 在speed指定時間內將height由0遞增至原有高度,形成向下拉開的視覺效果。
  • slideUp(speed[, callback]) 在speed指定時間內將height由原有高度遞減至0,形成向下收合的視覺效果。
  • slideToggle(speed[, callback) 切換元素的顯示狀態,將可見者隱藏,隱藏者顯示出來,但過程使用slideDown, slideUp效果。
  • fadeIn(speed[, callback]) 在speed指定時間內將opacity由透明逐漸變成不透明,形成漸漸浮現的視覺效果。
  • fadeOut(speed[, callback]) 在speed指定時間內將opacity由不透明逐漸變成透明,形成慢慢消失的視覺效果。
  • fadeTo(speed, opacity[, callback)  在speed指定時間內將opacity逐漸變成指定的透明度。

動畫特效是靠背後高速的連續運算,才能呈現出流暢、華麗的效果,針對一些運算能力較差的行動裝置,為了表現酷炫動畫的代價可超出其CPU可負荷的範圍,將導致播放不流暢或耗用過多寶貴的電力。jQuery 1.3+提供了jQuery.fx.off = true,設定後,可以停用全部的動畫過程,直接呈現最後的狀態。

【牛刀小試】

綜合以上學到的,我們來做一個移動滑鼠挑選照片,選取過程焦點照片自動突出的效果。這個在Sliverlight展示中常可看到,有了jQuery,我們也可以輕鬆做到。

程式碼維持jQuery一貫的簡潔風,唯一要補充說明的是,這裡用了stop()處理滑鼠快速移動時queue中留下的多餘延遲動作,為什麼要這麼做? 與其用文字說明抽象的情境,不如請大家試著將stop()移除,馬上就明瞭了。

$(function() {
	//加入10張照片
	for (var i=1; i<=10; i++) {
		$("#dvAlbum").append("<img class='imgItem' src='Pic" + 
			((i < 10) ? "0" : "") + i + ".jpg' />");
	}
	//加入onmouseover, onmouseout的動畫
	$(".imgItem").hover(function() {
		$(this)
		.css("border-color", "yellow")
		.stop(true, true).animate({ marginTop:"10px" }, 500, function() {
			$("#imgMain").attr("src", $(this).attr("src"));
		});
		
	}, function() {
		$(this)
		.css("border-color", "white")
		.stop(true, true).animate({ marginTop:"3px" }, 500);
	})	
	
});

【範例檔案下載】

歡迎推文分享:



 
RSS
【工商服務】
OrcsWeb: Windows Server Hosting
twMVC

關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。