如果說 Web 2.0 革命有一個時髦詞的話,那就是 Asynchronous JavaScript and XML(Ajax)。應用程序的客戶端交互,如 Google Maps? 地圖服務和 Gmail? 網絡郵件服務,使得 Ajax 不僅是一個振奮人心的詞匯,而且具有實用價值。Ajax 技術包括超文本標記語言(HTML)、JavaScript 代碼、級聯樣式表(CSS)、XML 和異步 Web 請求,與我們所見的 Web V1.0 相比能創建更令人信服的 Web 交互。當然,這些技術自 Microsoft? Internet Explorer? V4 發布以來就存在了,但是直到最近才通過其他一些引人注目的應用顯示出其優勢。
實現 Ajax 很難嗎?Ajax 模型的每個成分都很容易學習。但關鍵在於將這些成分結合起來,融為一體。通常這是一個復合性的問題,因為客戶端編碼和服務器端編碼由不同的人來做。本文說明一個人如何在一兩個小時中就能編寫出小型的基於 Ajax 的幻燈片放映應用程序。
Ajax 幻燈片放映
個人圖像管理應用程序(如 Macintosh? 上的 Apple? iPhoto?)使得幻燈片浏覽廣為人知。在幻燈片浏覽中,圖像按照時間順序先後淡入淡出。此外,圖片還通過所謂的 “Ken Burns Effect” 進行移動和縮放。
在該例中,我讓浏覽器從服務器上下載一個圖像列表。然後使用動態 HTML(DHTML)把圖片列表組成一個幻燈片。我使用隨機的緩慢移動、縮放和漸變來改變圖片,實現了令人滿意的 Ken Burns Effect 版本,而不需要下載 Macromedia? Flash 或其他重量級的動畫工具。
體系結構
要了解 Ajax 有何不同,首先必須理解當前的 Web 編程模型。客戶機和服務器之間的簡單交互如 圖 1 所示。
圖 1. 客戶機-服務器交互的 Web V1.0 模型
Web 浏覽器或者客戶機 向 Web 服務器發出 GET 或 POST 請求。服務器格式化 HTML 響應。客戶機解析 HTML 並顯示給用戶。如果用戶單擊其他鏈接和按鈕,就向服務器發出另一個請求,用服務器返回的新頁面替換當前頁面。
新模型具有更多的異步特色,如 圖 2 所示。
圖 2. 客戶機-服務器交互的 Ajax 模型
在新的模型中,和以前一樣,服務器也返回 HTML 頁面。但是這個頁面中有一些 JavaScript 代碼。在需要的時候,這些代碼向服務器請求更多信息。這些請求可以是簡單的 GET 請求(Representational State Transfer (REST) 服務)或者 POST 請求(SOAP)。
然後,JavaScript 代碼解析響應(通常用 XML 編碼)並動態更新頁面以反映新的數據。除了 XML 外,還返回 JavaScript Serialized Object Notation(JSON)格式編碼的數據。浏覽器很容易理解這類數據,但其他類型的客戶機則不行。返回 XML 的意義在於浏覽器之外的其他客戶機也能解釋數據。選擇由您來決定並依賴於具體的應用程序。
提供圖片信息
開發 Ajax 幻燈片的第一步是結合 REST 數據服務。該例中使用 PHP 頁面返回所有可用的幻燈片圖像及其大小(寬和高)。所有圖像都放在 images 目錄中。文件名格式為 name_width_height.jpg,比如 oso1_768_700.jpg 表示該文件是我的狗 Oso 的照片,寬 768 像素,高 700 像素。我堅持使用這種命名方式,因為這樣就很容易確定圖片的寬和高,而不用費力去打開 Adobe? PhotoShop? 或 Macromedia Fireworks.
我使用 清單 1 所示的 PHP 服務器代碼來提供圖片列表。
清單 1. slides.php 服務器頁面
<?php header( "Content-type: text/xml" ); ?> <slides> <?php if ($handle = opendir('images')) { while (false !== ($file = readdir($handle))) { if ( preg_match( "/[.]jpg$/", $file ) ) { preg_match( "/_(\d+)_(\d+)[.]/", $file, $found ); ?> <slide src="images/<?php echo $file; ?>" width="<?php echo $found[1]; ?>" height="<?php echo $found[2]; ?>" /><?php echo( "\n" ); ?> <?php } } closedir($handle); } ?> </slides>
代碼很簡單。首先將內容類型設置為 XML.讓浏覽器將該文檔識別為 XML 並為其創建文檔對象模型(DOM)至關重要。代碼從 <slides> 標記開始,然後讀取圖片目錄並為遇到的每個圖片創建 <slide> 標記。最後腳本結束 <slides> 標記。
如果用 Mozilla? Firefox? 浏覽器打開(在我的機器上)本地主機 kenburns 目錄中的該頁面,就會看到 圖 3 所示的結果。
圖 3. slides.php 服務器腳本的輸出
一共三幅圖片:我的女兒和我的兩條狗。當然在這裡可以增加任何需要的細節或者多媒體,但我盡量保持例子的簡單性。
檢索 XML
下一步就是編寫一個 HTML 頁面(如 清單 2 所示)從服務器讀取數據並檢驗浏覽器和服務器之間使用的 Ajax 連接。這段 HTML 代碼包含內嵌的 JavaScript 代碼,檢索 XML 並打開一個警告窗口顯示服務器返回的文本。
清單 2. 簡單的 Ajax 讀取數據頁面
<html> <body> <script> function processReqChange() { if (req.readyState == 4 && req.status == 200 && req.responseXML != null) { alert( req.responseText ); } } function loadXMLDoc( url ) { req = false; if(window.XMLHttpRequest) { try { req = new XMLHttpRequest(); } catch(e) { req = false; } } else if(window.ActiveXObject) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { req = false; } } } if(req) { req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(""); } } loadXMLDoc( "http://localhost/kenburns/slides.php" ); </script> </body> </html>
代碼從指定的 URL 獲取 XML 內容,然後 loadXMLDoc 函數啟動 Ajax 請求。檢索頁面的請求異步發出並返回結果。請求完成後,對結果調用 processReqChange 函數。這裡用 processReqChange 函數在警告窗口中顯示 responseText 的函數值。在我的 Firefox 浏覽器中調用該頁面的結果如 圖 4 所示。
開局不錯。毫無疑問,我們從服務器取回了 XML 數據。但是有必要指出幾點。首先要注意 URL 使用了絕對路徑,包括域名等等。對於 Ajax 來說這是唯一有效的 URL 格式。編寫 Ajax JavaScript 代碼的服務器代碼總是創建有效的、完整格式的 URL.
這裡不那麼明顯的另一點是 Ajax 的安全保護措施。JavaScript 代碼不能請求任意的 URL.URL 的域名必須和該頁面相同。在這裡域名就是 localhost.但必須指出不能呈現 www.mycompany.com 的 HTML 但卻讓腳本從 data.mycompany.com 檢索數據。域必須完全相同,包括子域名。
有趣的另一點是 loadXMLDoc 中的代碼,似乎是費力地創建一個請求對象。為何這麼麻煩呢?Internet Explorer 7 的預覽版沒有內建 XMLHTTPRequest 對象類型。因此必須使用 Microsoft ActiveX? 控件。
最後在 processReqChange 函數中,可以看到我在查看 readyState 是否等於 4,status 是否設為 200.readyState 的值 4 表示事務已經完成。status 的值 200 表示頁面是有效的。如果沒有找到頁面,就可能會得到錯誤消息 404,就像您在浏覽器中看到的那樣。這裡沒有處理異常情況,因為這僅僅是一個例子,不過發布的 Ajax 代碼應該處理返回錯誤的請求。
動態創建 HTML
在說明如何創建幻燈片放映之前,首先擴展現在的例子,讓 processReqChange 函數用服務器返回的 XML 請求結果創建一個 HTML 表格。這樣做可以驗證兩件事:能夠讀取 XML 並能夠根據 XML 動態創建 HTML.
清單 3 顯示了修改後的代碼,它將從返回的 XML 創建表格。
清單 3. 改進的測試頁面
<html> <body> <table> <tbody id="dataTable"> </tbody> </table> <script> function processReqChange() { if (req.readyState == 4 && req.status == 200 && req.responseXML != null) { var dto = document.getElementById( 'dataTable' ); var items = []; var nl = req.responseXML.getElementsByTagName( 'slide' ); for( var i = 0; i < nl.length; i++ ) { var nli = nl.item( i ); var src = nli.getAttribute( 'src' ).toString(); var width = parseInt( nli.getAttribute( 'width' ).toString() ); var height = parseInt( nli.getAttribute( 'height' ).toString() ); var trNode = document.createElement( 'tr' ); var srcNode = document.createElement( 'td' ); srcNode.innerHTML = src; trNode.appendChild( srcNode ); var widthNode = document.createElement( 'td' ); widthNode.innerHTML = width.toString(); trNode.appendChild( widthNode ); var heightNode = document.createElement( 'td' ); heightNode.innerHTML = height.toString(); trNode.appendChild( heightNode ); dto.appendChild( trNode ); } load_slides( items ); start_slides(); } } function loadXMLDoc( url ) { req = false; if(window.XMLHttpRequest) { try { req = new XMLHttpRequest(); } catch(e) { req = false; } } else if(window.ActiveXObject) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { req = false; } } } if(req) { req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(""); } } loadXMLDoc( "http://localhost/kenburns/slides.php" ); </script> </body> </html>
在浏覽器中打開該頁面將顯示 圖 5 所示的結果。
修改後的 processReqChange 代碼現在查看 responseXML 對象而不是 responseText 文本。此外,它還使用 getElementsByTagName 訪問所有的 <slide> 標記。然後解析 src、width 和 height 屬性,並使用 document 對象的 createElement 方法創建行和單元格來存放數據。該方法使用的 createElement 遠比過去的老方法健壯,原來要建立一個包含表格內容的 HTML 字符串,然後用 innerHTML 將數據添加到已有的元素中。
創建幻燈片放映
現在已經有了能夠確定幻燈片中圖像的 Web 服務,還需要顯示這些幻燈片並執行 Ken-Burns-Effect 動畫的客戶端代碼。為此必須將執行三種基本功能的 JavaScript 對象結合起來:
封裝圖像提供基本的動畫引擎實現特效(比如移動、縮放和漸變)
封裝圖像
首先從圖像容器開始,我創建一個類 ImageInfo,如 清單 4 所示。
清單 4. ImageInfo.js
function ImageInfo( src, width, height, htmlObj ) { this.src = src; this.width = width; this.height = height; this.current_width = width; this.current_height = height; this.htmlObj = htmlObj; this.htmlObj.src = this.src; this.htmlObj.width = this.current_width; this.htmlObj.height = this.current_height; } ImageInfo.prototype.set_opacity = function( opacity ) { this.htmlObj.style.MozOpacity = opacity / 100; var f = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+opacity+')'; this.htmlObj.style.filter = f; } ImageInfo.prototype.set_position = function( x, y ) { this.htmlObj.style.left = x+'px'; this.htmlObj.style.top = y+'px'; } ImageInfo.prototype.set_size = function( w, h ) { this.current_width = w; this.current_height = h; this.htmlObj.width = this.current_width; this.htmlObj.height = this.current_height; } ImageInfo.prototype.get_image = function() { return this.htmlObj; } ImageInfo.prototype.hide = function() { this.htmlObj.style.visibility = 'hidden'; } ImageInfo.prototype.show = function() { this.htmlObj.style.visibility = 'visible'; }
幻燈片中每幅圖片都有一個對應的 ImageInfo 對象。該對象封裝了圖像信息:src、width 和 height.該對象還包含對在文檔中顯示圖像的 HTML <img> 標記的引用,以及移動圖像、設置透明度等的 helper 方法。注意,在 Firefox 和其他基於 Gecko? 的浏覽器中,MozOpacity 樣式用於設置不透明性。Internet Explorer 中則使用過濾器效果。
創建簡單的動畫引擎
下面我們來編寫一個簡單的動畫引擎。Animation.js 文件中的代碼如 清單 5 所示。
清單 5. Animation.js
function Animation( am, img, seconds, effects ) { this.img = img; this.animationManager = am; this.seconds = seconds; this.effects = effects; this.startMS = 0; } Animation.prototype.start = function() { this.animationManager.add( this ); this.startMS = 0; this.img.hide(); for( var e in this.effects ) { this.effects[e].apply( 0 ); } this.img.show(); } Animation.prototype.animate = function() { var d = new Date(); if ( this.startMS == 0 ) this.startMS = d.valueOf(); var p = (((d.valueOf()-this.startMS)/1000)/this.seconds)*100; for( var e in this.effects ) this.effects[e].apply( p ); } Animation.prototype.done = function() { var d = new Date(); return ( ( d.valueOf() - this.startMS ) / 1000 ) > this.seconds; } function AnimationManager( speed ) { this.animations = []; var self = this; window.setInterval( function() { self.idle(); }, speed ); } AnimationManager.prototype.add = function( anim ) { this.animations.push( anim ); } AnimationManager.prototype.idle = function() { if ( this.animations.length > 0 ) { this.animations[0].animate(); if ( this.animations[0].done() ) this.animations.shift(); if ( this.animations.length == 0 ) this.on_finished(); } } AnimationManager.prototype.on_finished = function() { }
清單 5 包含兩個類:Animation 和 AnimationManager.AnimationManager 類控制定時器並向其 Animation 對象列表中的第一項發送動畫消息。當 Animation 對象報告自己已經完成的時候,該類就轉向下一項,依此類推。
Animation 在一定的時間(按秒數指定)內對特定圖片應用一系列特效。Animation 對象需要計算完成度消息並將其發送給每種特效的 apply 方法。特效然後根據這個百分比計算應該如何處理圖像。比如,移動特效知道起點和終點,可以根據這個百分比計算應該將圖像放到何處。如果是 50%,圖像應該移到起點和終點之間。
作為我工作的一部分,同時也為了撰寫本文,我考察了大量的 JavaScript 動畫代碼。JavaScript 動畫經常因為不穩定而受到指責,因為所有 JavaScript 動畫都使用 window.setInterval 方法來完成。這是一個定時器方法,同時指定了回調時間間隔和回調函數。Web 上的大部分代碼都要求每次調用該函數時動畫移動一步。但這並不能真正工作,因為告訴浏覽器的間隔僅僅是一個建議。如果規定 20 毫秒,但實際上可能第一次在 25 毫秒時調用,下一次卻要等到一秒鐘以後。浏覽器是單線程的,因此不能依賴於定時器。
解決方案是使用 Date 對象的 valueOf 方法確定動畫開始了多長時間。這個時間差是用毫秒計的,用於確定當 setInterval 定時器離開時動畫應該執行百分之多少。該方法可以提供規定的任意長時間的平滑動畫。
執行特效
三個核心類的最後一個是 Ken Burns Effects.這些特效通過 Animation 對象應用於圖像,如 清單 6 所示。
清單 6. KenBurnsAnimations.js
function KenBurnsFader( img, windowSize ) { this.img = img; this.windowSize = windowSize; } KenBurnsFader.prototype.apply = function( percent ) { var opacity = 100; if ( percent <= this.windowSize ) opacity = ( percent / this.windowSize ) * 100; else if ( percent >= ( 100 - this.windowSize ) ) opacity = ( ( 100 - percent ) / this.windowSize ) * 100; this.img.set_opacity( opacity ); } function KenBurnsZoomer( img, start, end, cw, ch ) { this.start = start; this.end = end; this.img = img; var wr = this.img.width / cw; var nw = this.img.width * wr; var nh = this.img.height * wr; this.sw = ( nw * ( this.start / 100 ) ); this.ew = ( nw * ( this.end / 100 ) ); this.sh = ( nh * ( this.start / 100 ) ); this.eh = ( nh * ( this.end / 100 ) ); this.dw = ( this.ew - this.sw ) / 100; this.dh = ( this.eh - this.sh ) / 100; } KenBurnsZoomer.prototype.apply = function( percent ) { this.img.set_size( this.sw + ( this.dw * percent ), this.sh + ( this.dh * percent ) ); } function KenBurnsMover( img, sx, sy, ex, ey, cw, ch ) { this.img = img; this.sx = sx / 100; this.ex = ex / 100; this.sy = sy / 100; this.ey = ey / 100; this.cw = cw; this.ch = ch; this.wr = this.img.width / this.cw; } KenBurnsMover.prototype.apply = function( percent ) { var nw = this.img.current_width * this.wr; var nh = this.img.current_height * this.wr; var cntw = ( ( this.cw / 2 ) - ( nw / 2 ) ); var cnth = ( ( this.ch / 2 ) - ( nh / 2 ) ); var sx = ( nw * this.sx ); var ex = ( nw * this.ex ); var sy = ( nh * this.sy ); var ey = ( nh * this.ey ); var dx = ( ex - sx ) / 100; var dy = ( ey - sy ) / 100; var x = cntw + sx + ( dx * percent ); var y = cntw + sy + ( dy * percent ); this.img.set_position( x, y ); }
這三個類分別處理應用於圖像的不同特效。KenBurnsFader 類使用不透明度處理圖像的淡入淡出。KenBurnsZoomer 類處理圖像的縮放,從最初的大小到最終的大小。KenBurnsMover 類處理圖像的移動,從起點到終點(用圖像的百分比指定)。
經過一些試驗後,我發現最吸引人的移動特效是相對於窗口中心從一個角移動到另一個角。KenBurnsMover 類的 apply 方法包含一些復雜的數學運算,不僅相對於包含圖像的 <div> 標記的中心來移動,還要計算圖像和 <div> 標記的相對大小,這樣在小窗口中移動的距離就小,在大窗口中移動的距離就大。放大倍數根據窗口的高度確定。
實現非 Ajax DHTML
有了這些基礎類之後,就可以實現幻燈片的非 Ajax DHTML 版本來進行測試了,如 清單 7 所示。
清單 7. 非 Ajax 幻燈片放映
<html> <head> <style type="text/css"> body { background: black; margin: 0px; padding: 0px; } </style> <script src="KenBurnsAnimations.js"> </script> <script src="Animation.js"> </script> <script src="ImageInfo.js"> </script> <script> var g_animationManager = new AnimationManager( 50 ); var g_current_slide = 0; var g_slides = []; var g_directions = [ { sx: [ -30, 0 ], ex: [ 5, 40 ], sy: [ -30, 0 ], ey: [ 5, 40 ] }, // nw -> se { sx: [ 5, 40 ], ex: [ -30, 0 ], sy: [ 5, 40 ], ey: [ -30, 0 ] }, // ne -> sw { sx: [ 5, 40 ], ex: [ -30, 0 ], sy: [ 5, 40 ], ey: [ -30, 0 ] }, // se -> nw { sx: [ -30, 0 ], ex: [ 5, 40 ], sy: [ 5, 40 ], ey: [ -30, 0 ] } // sw -> ne ]; g_animationManager.on_finished = function() { g_current_slide++; if ( g_current_slide >= g_slides.length ) g_current_slide = 0; g_slides[ g_current_slide ].start(); } function rnd( start, end ) { return ( Math.random() * ( end - start ) ) + start; } function load_slides( images ) { var ic = document.getElementById( 'imgContainer' ); for( var i in images ) { var img = images[i]; var imgObj = document.createElement( 'img' ); imgObj.style.position = 'absolute'; imgObj.style.left = '0px'; imgObj.style.top = '0px'; imgObj.style.visibility = 'hidden'; ic.appendChild( imgObj ); var ii = new ImageInfo( img.src, img.width, img.height, imgObj ); var szoom = rnd( 50, 100 ); var ezoom = rnd( 70, 120 ); var d = parseInt( ( Math.random() * g_directions.length ).toString() ); var di = g_directions[ d ]; var sx = rnd( di.sx[0], di.sx[1] ); var sy = rnd( di.sy[0], di.sy[1] ); var ex = rnd( di.ex[0], di.ex[1] ); var ey = rnd( di.ey[0], di.ey[1] ); g_slides.push( new Animation( g_animationManager, ii, 10, [ new KenBurnsZoomer( ii, szoom, ezoom, ic.clientWidth, ic.clientHeight ), new KenBurnsMover( ii, sx, sy, ex, ey, ic.clientWidth, ic.clientHeight ), new KenBurnsFader( ii, 30 ) ] ) ); } } function start_slides() { g_slides[ g_current_slide ].start(); } </script> </head> <body> <div style="position:relative;width:100%;height:100%;overflow:hidden;" id="imgContainer"> </div> <script> var images = [ { src: 'images/megan1_875_700.jpg', width: 875, height: 700 }, { src: 'images/oso1_875_700.jpg', width: 875, height: 700 }, { src: 'images/oso2_873_700.jpg', width: 873, height: 700 } ]; load_slides( images ); start_slides(); </script> </body> </html>
不用電影是很難說明上述代碼在浏覽器中的運行結果的。因此我抓了一個快照,如 圖 6 所示。
該頁面首先通過 <script> 標記的 src 屬性引入基類。安裝這些類之後,增加兩個函數將整個機制組織到一起:load_slides 和 start_slides.load_slides 函數接收一個數組,包括圖像的 src、width 和 height,然後創建 <image> 標記和動畫。start_slides 函數從第一項開始啟動幻燈片放映。
附加在動畫管理器上的另一個方法 on_finished 在動畫完成時調用。我使用該通知移動到下一張幻燈片,如果已經完成所有幻燈片的動畫,則回到列表中的第一張。
再回到 load_slides,要注意它引用了一個名為 g_directions 的數組。該數組包含一些隨機范圍,幻燈片加載程序用它來規定圖片移動的起點和終點。最理想的效果是從一個角到另一個角。從注釋中可以看到,這些值規定幻燈片的移動范圍為東北、東南、西北和西南的任意組合。最後的 <script> 標記定義了一個圖像數組,然後使用 load_slides 和 start_slides 函數啟動幻燈片放映。
創建 Ajax 幻燈片放映
最後一步是創建 Ajax 版本的幻燈片放映。這意味著要使用從 slides.php 服務獲得的內容代替硬編碼的圖像列表。
Ajax 版本的幻燈片放映代碼如 清單 8 所示。
清單 8. Ajax 幻燈片代碼
<html> <head> <style type="text/css"> body { background: black; margin: 0px; padding: 0px; } </style> <script src="KenBurnsAnimations.js"> </script> <script src="Animation.js"> </script> <script src="ImageInfo.js"> </script> <script src="SlideShow.js"> </script> </head> <body> <div style="position:relative;width:100%;height:100%;overflow:hidden;" id="imgContainer"> </div> <script> function processReqChange() { if (req.readyState == 4 && req.status == 200 && req.responseXML != null) { var items = []; var nl = req.responseXML.getElementsByTagName( 'slide' ); for( var i = 0; i < nl.length; i++ ) { var nli = nl.item( i ); var src = nli.getAttribute( 'src' ).toString(); var width = parseInt( nli.getAttribute( 'width' ).toString() ); var height = parseInt( nli.getAttribute( 'height' ).toString() ); items.push( { src: src, width: width, height: height } ); } load_slides( items ); start_slides(); } } function loadXMLDoc( url ) { req = false; if(window.XMLHttpRequest) { try { req = new XMLHttpRequest(); } catch(e) { req = false; } } else if(window.ActiveXObject) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { req = false; } } } if(req) { req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(""); } } loadXMLDoc( "http://localhost/kenburns/slides.php" ); </script> </body> </html>
我把 start_slides 和 load_slides 函數移到了外部 JavaScript 文件 SlidesShow.js 中,以免該文件過大。代碼的其他部分和 清單 2 中的 Ajax 測試頁類似。只不過這些代碼沒有插入警告窗口,也沒有把數據插入一個表格,而是創建了一個幻燈片信息數組,然後調用 load_slides 和 start_slides.
如此而已!這樣就可以使用 Ken Burns Effect 動態地移動、縮放和漸變圖像的 Ajax 幻燈片。
結束語
本文中盡可能地使用了面向對象的 JavaScript 代碼。JavaScript 是一種完全面向對象的語言,雖然可能不使用 class 和 interface 關鍵字,但仍然可以保持代碼的清晰性和可維護性。如果可以的話,我建議您使用 Ajax 框架。這裡沒有使用框架是因為我想介紹一種輕型的 Ajax 解決方案。但現在的框架(有很多)更容易編寫更具可移植性的 Ajax 和 DHTML 代碼。
除了本文中介紹的之外,關於 Ajax 幻燈片我還有以下建議:
使用基於時間的動畫。用 setInterval 代碼實現基於步驟的動畫看起來有些抖動。
對可視化元素用 DHTML 建立代碼原型,然後再增加 Ajax 內容。這意味著可以離線編寫 DHTML 代碼。
將連接到服務器的 Ajax 代碼和呈現數據的 DHTML 用戶界面(UI)組件分開。這樣即便不使用 Ajax 獲取數據,也仍然能夠使用那些界面組件。
使用 createElement 和 appendChild 函數而不是 innerHTML 來改變頁面內容。
一定要針對所有希望支持的浏覽器檢查客戶端代碼。此外,還要記錄下您所遇到的兼容性問題以及解決這些問題的方法。盡量將固定的客戶端代碼封裝成可重用的 JavaScript helper 函數和類。
對於復雜的界面(包括多重動畫),編碼之前應首先使用情節串連板與客戶一起確定他們所需要的效果。情節串連板是代碼規范的動畫版本。JavaScript 動畫寫起來很快,因此在編碼之前明確目標是值得的,否則可能走不少冤枉路。
從職業的角度來看,僅關注數據庫和業務邏輯的 Web V1.0 時代的 “後端工程師”,在 Web V2.0 時代中作用是有限的。必須認識到並非所有對服務器的請求都要借助於 HTML.Ajax 和 DHTML 對那些願意花錢提高其技能的真正的工程師來說是現實的工具。前端不僅僅是設計人員的前端。
過去,通常需要 Flash 或者類似的應用程序才能實現本文這樣的動態幻燈片放映。現代化的浏覽器為 DHTML 提供了不透明性這類豐富的特效支持(Internet Explorer 甚至支持旋轉、模糊等),再加上 Ajax,僅僅在浏覽器中就能實現令人眩目的效果。這意味著客戶不再需要下載奇怪的擴展或者運行有可能不安全的應用程序。他們可能偶爾看到了您的網頁,令人震驚的圖像效果會讓他們經常來光顧。