javascript
JavaScript 图片上传预览效果
圖片上傳預(yù)覽是一種在圖片上傳之前對圖片進行本地預(yù)覽的技術(shù)。
使用戶選擇圖片后能立即查看圖片,而不需上傳服務(wù)器,提高用戶體驗。
但隨著瀏覽器安全性的提高,要實現(xiàn)圖片上傳預(yù)覽也越來越困難。
不過群眾的智慧是無限的,網(wǎng)上已經(jīng)有很多變通或先進的方法來實現(xiàn)。
例如ie7/ie8的濾鏡預(yù)覽法,firefox 3的getAsDataURL方法。
但在opera、safari和chrome還是沒有辦法實現(xiàn)本地預(yù)覽,只能通過后臺來支持預(yù)覽。
在研究了各種預(yù)覽方法后,作為總結(jié),寫了這個程序,跟大家一起分享。
上次寫的簡便無刷新文件上傳系統(tǒng)最初的目的就是用來實現(xiàn)這個圖片預(yù)覽效果的。
兼容:ie6/7/8, firefox 3.5.5
后臺支持下還兼容:opera 10.10, safari 4.0.4, chrome 3.0
效果預(yù)覽
| ||||
ps:兼容opera, safari和chrome需要后臺支持,請下載實例測試。
程序說明
【基本原理】
圖片預(yù)覽主要包括兩個部分:從file表單控件獲取圖像數(shù)據(jù),根據(jù)數(shù)據(jù)顯示預(yù)覽圖像。
程序的file和img屬性就是用來保存file控件和顯示預(yù)覽圖像的容器的,而img還必須是img元素。
程序有以下幾種預(yù)覽方式:
simple模式:直接從file的value獲取圖片路徑來顯示預(yù)覽,適用于ie6;
filter模式:通過selection獲取file的圖片路徑,再用濾鏡來顯示預(yù)覽,適用于ie7/8;
domfile模式:調(diào)用file的getAsDataURL方法獲取Data URI數(shù)據(jù)來顯示預(yù)覽,適用于ff3;
remote模式:最后的辦法,把file提交后臺處理后返回圖片數(shù)據(jù)來顯示預(yù)覽,全適用。
程序定義時就自動根據(jù)瀏覽器設(shè)置MODE屬性:
????$$B.firefox???"domfile"?:
????$$B.opera?||?$$B.chrome?||?$$B.safari???"remote"?:?"simple";
如果用能力檢測會比較麻煩,所以只用了瀏覽器檢測。
由于瀏覽器對應(yīng)的默認模式是不會變的,這個值會保存到函數(shù)屬性中作為公用屬性。
ps:ie6也可以用filter模式,不過它有更好的simple模式。
【獲取數(shù)據(jù)】
調(diào)用preview方法,就會執(zhí)行預(yù)覽程序:
if?(?this.file?&&?false?!==?this.onCheck()?)?{????this._preview(?this._getData()?);
}
?
在通過檢測后,再調(diào)用_getData獲取數(shù)據(jù),并作為_preview的參數(shù)進入下一步。
程序初始化時就會根據(jù)mode來設(shè)置_getData數(shù)據(jù)獲取程序:
mode的默認值是ImagePreview.MODE,也可以在可選參數(shù)中自定義。
由于兼容性問題,一般應(yīng)保留默認值,除非是使用全兼容的remote模式。
在_getDataFun里面,根據(jù)mode返回數(shù)據(jù)獲取程序:
代碼 switch?(mode)?{????case?"filter"?:
????????return?this._filterData;
????case?"domfile"?:
????????return?this._domfileData;
????case?"remote"?:
????????return?this._remoteData;
????case?"simple"?:
????default?:
????????return?this._simpleData;
}
?
不同的模式有不同的數(shù)據(jù)獲取程序:
濾鏡數(shù)據(jù)獲取程序:
try{
????return?document.selection.createRange().text;
}?finally?{?document.selection.empty();?}
一般用在ie7/8,在file控件select后再通過selection對象獲得文件本地路徑。
此時file控件不能隱藏,否則不能被select,不過一般能選擇文件就肯定能被select了。
確實要隱藏也可以在獲取數(shù)據(jù)之后再隱藏。
domfile數(shù)據(jù)獲取程序:
return?this.file.files[0].getAsDataURL();用getAsDataURL從file控件獲取數(shù)據(jù),這個方法暫時只有ff3支持。
遠程數(shù)據(jù)獲取程序:
this._setUpload();this._upload?&&?this._upload.upload();
用_upload上傳文件對象把數(shù)據(jù)提交后臺,根據(jù)返回的數(shù)據(jù)再顯示。
這個方法不屬于本地預(yù)覽,是沒有辦法中的辦法。
一般數(shù)據(jù)獲取程序:
return?this.file.value;最原始的方法,現(xiàn)在只有ie6還支持從file的value直接獲取本地路徑。
獲取數(shù)據(jù)后,作為_preview預(yù)覽程序的參數(shù),再進行處理:
if?(?!!data?&&?data?!==?this._data?)?{????this._data?=?data;?this._show();
}
首先排除空值或相同值的情況,再執(zhí)行_show程序進行顯示預(yù)覽,其中_data屬性用來保存當前的圖片數(shù)據(jù)。
圖片使用Data URI數(shù)據(jù)時可能會設(shè)置一個很大的src值,在ie8獲取很大的src值會出現(xiàn)“無效指針”的錯誤。
使用_data屬性保存這個值可以避免從src取值而觸發(fā)這個錯誤。
遠程數(shù)據(jù)獲取程序沒有返回值,因為它需要等待返回數(shù)據(jù),在_preview中會自動排除。
【顯示預(yù)覽】
程序初始化時就會根據(jù)mode來設(shè)置_show預(yù)覽顯示程序:
this._show?=?opt.mode?!==?"filter"???this._simpleShow?:?this._filterShow;
除了filter模式,都是使用_simpleShow顯示程序來顯示預(yù)覽圖片的。
里面會先調(diào)用_simplePreload方法設(shè)置一般預(yù)載圖片對象:
????var?preload?=?this._preload?=?new?Image(),?oThis?=?this,
????????onload?=?function(){?oThis._imgShow(?oThis._data,?this.width,?this.height?);?};
????this._onload?=?function(){?this.onload?=?null;?onload.call(this);?}
????preload.onload?=?$$B.ie???this._onload?:?onload;
????preload.onerror?=?function(){?oThis._error();?};
}?else?if?(?$$B.ie?)?{
????this._preload.onload?=?this._onload;
}
預(yù)載圖片對象保存在_preload屬性中,主要用來判斷圖像能否加載成功并獲取圖片原始尺寸。
要實現(xiàn)這些功能使用Image對象就足夠了。
在onload中執(zhí)行_imgShow顯示預(yù)覽,在onerror中進行出錯處理。
ps:ff、chrome和safari的圖片對象還有naturalHeight和naturalWidth屬性可以獲取圖片的原始尺寸,即使圖片尺寸已經(jīng)修改過。
這里要注意ie6/7的gif圖片載入bug,測試以下代碼:
<script>
img.onload?=?function(){?div.innerHTML?+=?this.complete?+?",?";?};
img.src?=?"http://tuan.pcpop.com/image/my/loading.gif";
</script>
一般圖片執(zhí)行一次onload后并不會重復(fù)執(zhí)行,但ie6/7的gif每次循環(huán)播放都會執(zhí)行一次onload。
ps:ie8在非標準(怪辟)模式下也有相同的問題。
可以在onload的時候,判斷complete是否為false來判斷是否重復(fù)加載。
ps:除了ie,其他瀏覽器在onload時complete就已經(jīng)為true了。
問題是選擇另一個圖片時這個complete仍然是true,這樣就沒有意義了。
所以只好在onload里面重置onload為null,并在每次選擇文件重設(shè)onload了。
然后設(shè)置_preload的src預(yù)載圖片,如果成功預(yù)載就會執(zhí)行_imgShow顯示預(yù)覽。
要注意src的設(shè)置要在onload/onerror的設(shè)置之后,否則設(shè)置之前就加載完成的話就觸發(fā)不了事件了。
_imgShow需要三個參數(shù),包括要預(yù)覽圖片的src值,圖片原始寬度和圖片原始高度。
在_imgShow里面首先設(shè)置預(yù)覽圖片的尺寸:
????ratio?=?Math.max(?0,?this.ratio?)?||?Math.min(?1,
????????????Math.max(?0,?this.maxWidth?)?/?width??||?1,
????????????Math.max(?0,?this.maxHeight?)?/?height?||?1
????????);
????
style.width?=?Math.round(?width?*?ratio?)?+?"px";
style.height?=?Math.round(?height?*?ratio?)?+?"px";
這里的關(guān)鍵是獲取ratio比例值,如果自定義的ratio大于0就直接使用自定義的比例,否則就根據(jù)參數(shù)自動計算。
自動計算首先要確保maxWidth最大寬度和maxHeight最大高度大于等于0。
然后分別跟原始寬高做“/”運算得到比例,如果比例為0表示不限制,那么比例就自動改為1。
最后取比較小的比例來計算,程序設(shè)定了比例最大值為1,這樣就不會自動放大圖片了。
當然比例的計算可以根據(jù)需要自行修改。
ps:style的優(yōu)先級比屬性(width/height)高,所以要用style設(shè)置。
最后設(shè)置img的src就可以實現(xiàn)預(yù)覽了。
【remote模式】
remote模式會先提交file控件到后臺,通過返回的數(shù)據(jù)來顯示圖片。
它跟其他模式最大的區(qū)別就是獲取數(shù)據(jù)的部分。
在_remoteData遠程數(shù)據(jù)獲取程序中,會調(diào)用_setUpload來設(shè)置上傳文件對象。
如果設(shè)置了action,并存在QuickUpload函數(shù),就會實例化一個上傳文件對象保存到_upload中:
this._upload?=?new?QuickUpload(this.file,?{
????onReady:?function(){
????????this.action?=?oThis.action;?this.timeout?=?oThis.timeout;
????????var?parameter?=?this.parameter;
????????parameter.ratio?=?oThis.ratio;
????????parameter.width?=?oThis.maxWidth;
????????parameter.height?=?oThis.maxHeight;
????},
????onFinish:?function(iframe){
????????try{
????????????oThis._preview(?iframe.contentWindow.document.body.innerHTML?);
????????}catch(e){?oThis._error("remote?error");?}
????},
????onTimeout:?function(){?oThis._error("timeout?error");?}
});
這里使用的QuickUpload就是簡便無刷新文件上傳程序。
在onReady中設(shè)置參數(shù),在onFinish中處理返回數(shù)據(jù),onTimeout進行出錯處理。
返回的數(shù)據(jù)可以是圖片的地址或?qū)?yīng)的Data URI數(shù)據(jù),然后給_preview處理。
當然針對不同的后臺輸出,數(shù)據(jù)處理的方式也不同,可以按照需要修改。
后臺最好先根據(jù)傳遞的參數(shù)縮小圖片,盡量減少返回數(shù)據(jù)來提高預(yù)覽速度。
【filter模式】
filter模式在_filterData程序中得到文件本地路徑,但ie7/8都不允許直接使用本地路徑顯示圖片。
不過還是可以通過濾鏡,用本地路徑來做預(yù)覽圖片效果。
filter模式使用_filterShow方法來顯示預(yù)覽圖片。
里面先調(diào)用_filterPreload方法來設(shè)置濾鏡預(yù)載圖片對象。
跟一般預(yù)載圖片對象不同,濾鏡預(yù)載對象是用濾鏡來顯示圖片,所以并不一定要圖像元素。
程序就使用了div元素作為濾鏡預(yù)載對象:
$$D.setStyle(?preload,?{
????width:?"1px",?height:?"1px",
????visibility:?"hidden",?position:?"absolute",?left:?"-9999px",?top:?"-9999px",
????filter:?"progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='image')"
});
var?body?=?document.body;?body.insertBefore(?preload,?body.childNodes[0]?);
在樣式設(shè)置中隱藏元素并添加濾鏡,要使濾鏡生效width和height必須設(shè)置一個值。
由于要獲取尺寸,只能用visibility來隱藏并插入body,關(guān)于AlphaImageLoader濾鏡后面再介紹。
然后在_filterShow中預(yù)載圖片:?
try{????preload.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src?=?data;
}catch(e){?this._error("filter?error");?return;?}
成功的話,再給img載入圖片:
注意,如果路徑中有“)”,“%”這類字符的話,直接拼接到濾鏡字符串中會出現(xiàn)類似sql注入的問題。
程序會先對這些敏感字符進行escape編碼:
為什么要做兩次escape編碼呢?測試時發(fā)現(xiàn)“%”只轉(zhuǎn)一次的話,遇到“%40”之類的字符時還是會出問題。
所以我推測,字符在使用前會進行兩次unescape解碼,于是對應(yīng)的做兩次escape編碼果然沒問題了。
雖然預(yù)載對象是直接設(shè)置濾鏡的src屬性,但也有“%”的拼接字問題,所以也要escape編碼。
ps:雖然單引號和雙引號這里并不是必要,還是一起替換掉安心點。
還要注意預(yù)覽對象不要用filters.item的方式設(shè)置濾鏡。
因為元素插入文檔之前,并不能通過filters.item獲取濾鏡對象,但能通過style預(yù)先設(shè)置filter。
所以在不確定元素位置的情況下,只能用style來設(shè)置filter了。
最后調(diào)用_imgShow設(shè)置尺寸:
this._imgShow(?ImagePreview.TRANSPARENT,?preload.offsetWidth,?preload.offsetHeight?);
由于img是一個圖片對象,默認會顯示一個小圖標,為了去掉這個小圖標,可以讓它顯示一個透明圖片。
程序傳遞了ImagePreview.TRANSPARENT來設(shè)置透明圖片,具體數(shù)據(jù)在Data URI 和 MHTML再說明。
ps:當然也可以在filter模式用div做預(yù)覽圖片對象就沒有小圖標了,但這樣兼容起來會麻煩很多。
【AlphaImageLoader濾鏡】
filter模式使用的是AlphaImageLoader濾鏡。
它的作用是在對象容器邊界內(nèi),在對象的背景和內(nèi)容之間顯示一張圖片。
如果載入的是png圖片,其透明度會被支持,因此它更多地用來解決png的兼容問題。
詳細參考msdn的AlphaImageLoader Filter和“Microsoft.AlphaImageLoader濾鏡講解”。
它包括三個屬性:enabled(濾鏡是否激活),sizingMethod(圖像顯示方式)和src(圖像路徑)。
程序主要使用后面兩個屬性。
sizingMethod有三種方式:
crop:剪切圖片以適應(yīng)對象尺寸;
image:默認值。增大或減小對象的尺寸邊界以適應(yīng)圖片的尺寸;
scale:縮放圖片以適應(yīng)對象的尺寸邊界。
預(yù)載圖片對象_preload,需要獲取圖片的原始尺寸,所以要用image方式。
而預(yù)覽圖片對象img,則要按設(shè)定尺寸顯示圖片,所以要用scale方式。
而src屬性設(shè)置的路徑還支持本地路徑,是實現(xiàn)filter模式的關(guān)鍵所在。
還好濾鏡并沒有像file控件那樣提高安全性,否則ie7/8就沒有辦法實現(xiàn)本地預(yù)覽了。
【nsIDOMFile接口】
ff從3.0(或許更早)開始,就不能通過file控件的value屬性獲取文件本地路徑,也不支持直接用本地路徑顯示圖片。
不過欣喜的是,它同時也提供了nsIDOMFile接口,能更好地獲取文件數(shù)據(jù)。
在ff的file控件有一個FileList對象,包含了帶nsIDOMFile接口的File對象。
ps:FileList對象貌似是一個NodeList集合,但目前只能用第一個,可能是為了將來實現(xiàn)一個file控件選擇多個文件的功能預(yù)留的。
這個File對象有三個獲取文件數(shù)據(jù)的方法:
getAsText:獲取文件的文本數(shù)據(jù),可以通過參數(shù)設(shè)置編碼;
getAsDataURL:獲取文件的Data URI(URL?)數(shù)據(jù);
getAsBinary:獲取文件的二進制數(shù)據(jù)。
其中g(shù)etAsDataURL獲得的Data URI數(shù)據(jù)可以用于顯示圖片,_domfileData中就是用它來獲取數(shù)據(jù)的。
File對象還支持兩個屬性:fileName(文件名,不包括路徑)和fileSize(文件大小)。
相關(guān)具體說明參考mozilla的File和nsIDOMFile。
【Data URI 和 MHTML】
上面已經(jīng)多次提到Data URI,詳細介紹請看秦歌的“Data URI 和 MHTML”。
Data URI的主要作用是以字符代替數(shù)據(jù),從而把文件“嵌”在代碼里。
除了ie,其他瀏覽器基本都很好的支持了Data URI。
ie8也有限度地支持,詳細參考msdn的data Protocol。
由于opera,safari和chrome需要remote模式的瀏覽器都支持Data URI,所以程序返回的是Data URI形式的數(shù)據(jù)。
相比返回路徑的方法,返回Data URI不需要創(chuàng)建文件,還少一次HTTP請求。
ps:ie8只支持32k的Data URI數(shù)據(jù),在ie8使用時要注意數(shù)據(jù)大小。
在filter模式需要一個透明圖片來去掉img默認顯示的小圖標,一般的方法需要一個圖片文件。
為了“省下”這個文件,可以使用Data URI來做一個1*1的透明圖片:
支持Data URI的情況下,只要把img的src設(shè)置為這個值就可以顯示一個透明圖片了。
雖然ie6/7不支持Data URI,但還有mhtml可以使。
在ImagePreviewd.js開頭有一段注釋了的代碼:
--_CLOUDGAMER
Content-Location:blankImage
Content-Transfer-Encoding:base64
R0lGODlhAQABAJEAAAAAAP///wAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==
其中boundary的值是分隔符標識,說明用于分隔數(shù)據(jù)段的字符。
Content-Location說明關(guān)聯(lián)引用位置,可以用作數(shù)據(jù)段的標識。
Content-Transfer-Encoding就是字符編碼形式。
后面的代碼就是1*1的透明圖片的base64編碼數(shù)據(jù)。
然后在代碼中這樣調(diào)用(例如設(shè)置img元素的src屬性):
mhtml:文件完整路徑!blankImage
就可以鏈接到一個透明圖片了。
接著就要解決如何獲得script(js文件)的完整路徑(包含http開頭的路徑)的問題了。
首先要在腳本運行時獲取,當前運行的script肯定是document.scripts的最后一個:
ps:ff不支持document.scripts,可以用getElementsByTagName("script")來兼容。
接著可以利用getAttribute從src獲取script的完整路徑:
document.scripts[document.scripts.length?-?1].getAttribute("src",?4)
ie6/7的getAttribute支持第二個參數(shù),設(shè)為4表示返回完整路徑的url地址,詳細參考msdn的getAttribute Method。
結(jié)合Data URI 和 MHTML可以這樣得到透明圖片數(shù)據(jù):?
ImagePreview.TRANSPARENT?=?$$B.ie7?||?$$B.ie6??????"mhtml:"?+?document.scripts[document.scripts.length?-?1].getAttribute("src",?4)?+?"!blankImage"?:
????"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
使用時要注意:
腳本必須單獨另存為一個文件,作為mhtml需要的文件路徑。
要自動獲取完整路徑需要用script標簽鏈接文件。
【超空間】
程序還有一個dispose方法用于銷毀程序。
包括這幾個部分:
_upload上傳文件對象:它本身已經(jīng)有一個dispose方法來銷毀程序;
_preload預(yù)載圖片對象:先清除它的onload/onerror事件再移除元素;
file和img屬性:直接設(shè)為null,由于不是程序創(chuàng)建的元素,留給使用者來移除。
說到移除元素,順便說一下超空間(DOM hyperspace),這是從“ppk談javascript”中看到的。
大概指的是當元素不在dom里面,而js又有關(guān)聯(lián)時,元素并不會消失,而是保存在一個稱為“超空間”的地方。
詳細參考書的DOM 超空間部分。
書中還說可以根據(jù)是否有parentNode來判斷元素是否在超空間,但測試以下代碼:
<script>
var?elm?=?document.createElement("div");
alert(elm.parentNode);
document.body.removeChild(document.body.appendChild(elm));
alert(elm.parentNode);
</script>
第一次parentNode都是null,沒有問題,按理第二次也應(yīng)該是null,但ie卻是一個object。
經(jīng)測試,這個object的nodeType是11,也就是一個碎片對象(FRAGMENT)。
而且各個被removeChild移除的元素的parentNode都不相同,即會生成不同的碎片對象。
這種情況算不算在“超空間”呢,不過書中也只是說“一般來說”,也不用太考究。
那么用innerHTML清除呢?再測試以下代碼:
<body><div?id="test"></div></body><script>
var?elm?=?document.getElementById("test");
document.body.innerHTML?=?"";
alert(elm.parentNode);
</script>
結(jié)果在ie也是null了,看來removeChild和innerHTML在清除元素時產(chǎn)生了不同的結(jié)果。
那個碎片對象貌似沒什么用(難道為了保證有parentNode?),那是不是innerHTML就一定比removeChild好呢?
再測試以下代碼:
<style>div{border:1px?solid?#000;?height:20px;}</style>
<span><div?id="test1">test1</div></span>
<span><div?id="test2">test2</div></span>
</body>
<script>
var?div1?=?document.getElementById("test1"),?parent1?=?div1.parentNode;
parent1.removeChild(div1);
alert(div1.tagName?+?":"?+?div1.innerHTML);
parent1.appendChild(div1);
var?div2?=?document.getElementById("test2"),?parent2?=?div2.parentNode;
parent2.innerHTML?=?"";
alert(div2.tagName?+?":"?+?div2.innerHTML);
parent2.appendChild(div2);
</script>
當使用removeChild時,移除元素的結(jié)構(gòu)并沒有發(fā)生變化,各個瀏覽器的效果都一樣。
而使用innerHTML清除時,其他瀏覽器的效果跟removeChild一樣,但在ie被移除的元素就只剩下一個“外殼”了。
個人推測,ie在使用innerHTML時,被移除的元素會變成一個個單獨的元素,失去了彼此的聯(lián)系。
形象點說就是removeChild是直接掰斷樹枝,還能繼續(xù)嫁接使用,而innerHTML是把需要的樹葉節(jié)點取下來,再把樹枝燒掉。
ps:僅僅是推測,誰有官方資料請告訴我。
那么removeChild的好處是移除的元素能再次使用,兼容性好,不好的地方是ie會產(chǎn)生一個沒用的碎片對象。
而innerHTML的好處是不會產(chǎn)生多余的碎片對象,方便高效,但在ie被移除的元素基本不能再用,有兼容性問題。
那就可以根據(jù)需要使用不同的方法了,至于防止內(nèi)存泄漏用那個好,感覺是innerHTML,但沒有更深入研究的話還說不清楚。
使用技巧
一般來preview方法都是在onchange中調(diào)用,即選擇文件后立即顯示預(yù)覽。
在不需要程序時最好執(zhí)行一次dispose方法來銷毀程序,防止內(nèi)存泄漏等。
利用ImagePreview.TRANSPARENT可以顯示透明圖片,而不需另外隱藏或增加文件。
第二個實例中的ResetFile是用來重置file控件的,詳細參考這里file的reset。
而file控件樣式設(shè)置詳細參考這里的file樣式。
asp版本使用Persits.Jpeg組件縮放圖片,測試請先安裝該組件。
使用說明
實例化時,有兩個必要參數(shù),分別是file控件對象和img元素的預(yù)覽顯示對象:
new?ImagePreview(?file,?img?);
可選參數(shù)用來設(shè)置系統(tǒng)的默認屬性,包括:
屬性:??? 默認值//說明
mode:??ImagePreview.MODE,//預(yù)覽模式
ratio:??0,//自定義比例
maxWidth:?0,//縮略圖寬度
maxHeight:?0,//縮略圖高度
onCheck:?function(){},//預(yù)覽檢測時執(zhí)行
onShow:??function(){},//預(yù)覽圖片時執(zhí)行
onErr:??function(){},//預(yù)覽錯誤時執(zhí)行
以下在remote模式時有效
action:??undefined,//設(shè)置action
timeout:?0//設(shè)置超時(0為不設(shè)置)
如果要使用remote模式必須設(shè)置一個action。
還提供了以下方法:
preview:執(zhí)行預(yù)覽操作;
dispose:銷毀程序。
程序源碼
????
????this.file?=?$$(file);//文件對象
????this.img?=?$$(img);//預(yù)覽圖片對象
????
????this._preload?=?null;//預(yù)載圖片對象
????this._data?=?"";//圖像數(shù)據(jù)
????this._upload?=?null;//remote模式使用的上傳文件對象
????
????var?opt?=?this._setOptions(options);
????
????this.action?=?opt.action;
????this.timeout?=?opt.timeout;
????this.ratio?=?opt.ratio;
????this.maxWidth?=?opt.maxWidth;
????this.maxHeight?=?opt.maxHeight;
????
????this.onCheck?=?opt.onCheck;
????this.onShow?=?opt.onShow;
????this.onErr?=?opt.onErr;
????
????//設(shè)置數(shù)據(jù)獲取程序
????this._getData?=?this._getDataFun(opt.mode);
????//設(shè)置預(yù)覽顯示程序
????this._show?=?opt.mode?!==?"filter"???this._simpleShow?:?this._filterShow;
};
//根據(jù)瀏覽器獲取模式
ImagePreview.MODE?=?$$B.ie7?||?$$B.ie8???"filter"?:
????$$B.firefox???"domfile"?:
????$$B.opera?||?$$B.chrome?||?$$B.safari???"remote"?:?"simple";
//透明圖片
ImagePreview.TRANSPARENT?=?$$B.ie7?||?$$B.ie6??
????"mhtml:"?+?document.scripts[document.scripts.length?-?1].getAttribute("src",?4)?+?"!blankImage"?:
????"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
ImagePreview.prototype?=?{
??//設(shè)置默認屬性
??_setOptions:?function(options)?{
????this.options?=?{//默認值
????????mode:????????ImagePreview.MODE,//預(yù)覽模式
????????ratio:????????0,//自定義比例
????????maxWidth:????0,//縮略圖寬度
????????maxHeight:????0,//縮略圖高度
????????onCheck:????function(){},//預(yù)覽檢測時執(zhí)行
????????onShow:????????function(){},//預(yù)覽圖片時執(zhí)行
????????onErr:????????function(){},//預(yù)覽錯誤時執(zhí)行
????????//以下在remote模式時有效
????????action:????????undefined,//設(shè)置action
????????timeout:????0//設(shè)置超時(0為不設(shè)置)
????};
????return?$$.extend(this.options,?options?||?{});
??},
??//開始預(yù)覽
??preview:?function()?{
????if?(?this.file?&&?false?!==?this.onCheck()?)?{
????????this._preview(?this._getData()?);
????}
??},
??
??//根據(jù)mode返回數(shù)據(jù)獲取程序
??_getDataFun:?function(mode)?{
????switch?(mode)?{
????????case?"filter"?:
????????????return?this._filterData;
????????case?"domfile"?:
????????????return?this._domfileData;
????????case?"remote"?:
????????????return?this._remoteData;
????????case?"simple"?:
????????default?:
????????????return?this._simpleData;
????}
??},
??//濾鏡數(shù)據(jù)獲取程序
??_filterData:?function()?{
????this.file.select();
????try{
????????return?document.selection.createRange().text;
????}?finally?{?document.selection.empty();?}
??},
??//domfile數(shù)據(jù)獲取程序
??_domfileData:?function()?{
????return?this.file.files[0].getAsDataURL();
??},
??//遠程數(shù)據(jù)獲取程序
??_remoteData:?function()?{
????this._setUpload();
????this._upload?&&?this._upload.upload();
??},
??//一般數(shù)據(jù)獲取程序
??_simpleData:?function()?{
????return?this.file.value;
??},
??
??//設(shè)置remote模式的上傳文件對象
??_setUpload:?function()?{
????if?(?!this._upload?&&?this.action?!==?undefined?&&?typeof?QuickUpload?===?"function"?)?{
????????var?oThis?=?this;
????????this._upload?=?new?QuickUpload(this.file,?{
????????????onReady:?function(){
????????????????this.action?=?oThis.action;?this.timeout?=?oThis.timeout;
????????????????var?parameter?=?this.parameter;
????????????????parameter.ratio?=?oThis.ratio;
????????????????parameter.width?=?oThis.maxWidth;
????????????????parameter.height?=?oThis.maxHeight;
????????????},
????????????onFinish:?function(iframe){
????????????????try{
????????????????????oThis._preview(?iframe.contentWindow.document.body.innerHTML?);
????????????????}catch(e){?oThis._error("remote?error");?}
????????????},
????????????onTimeout:?function(){?oThis._error("timeout?error");?}
????????});
????}
??},
??
??//預(yù)覽程序
??_preview:?function(data)?{
????//空值或相同的值不執(zhí)行顯示
????if?(?!!data?&&?data?!==?this._data?)?{
????????this._data?=?data;?this._show();
????}
??},
??
??//設(shè)置一般預(yù)載圖片對象
??_simplePreload:?function()?{
????if?(?!this._preload?)?{
????????var?preload?=?this._preload?=?new?Image(),?oThis?=?this,
????????????onload?=?function(){?oThis._imgShow(?oThis._data,?this.width,?this.height?);?};
????????this._onload?=?function(){?this.onload?=?null;?onload.call(this);?}
????????preload.onload?=?$$B.ie???this._onload?:?onload;
????????preload.onerror?=?function(){?oThis._error();?};
????}?else?if?(?$$B.ie?)?{
????????this._preload.onload?=?this._onload;
????}
??},
??//一般顯示
??_simpleShow:?function()?{
????this._simplePreload();
????this._preload.src?=?this._data;
??},
??
??//設(shè)置濾鏡預(yù)載圖片對象
??_filterPreload:?function()?{
????if?(?!this._preload?)?{
????????var?preload?=?this._preload?=?document.createElement("div");
????????//隱藏并設(shè)置濾鏡
????????$$D.setStyle(?preload,?{
????????????width:?"1px",?height:?"1px",
????????????visibility:?"hidden",?position:?"absolute",?left:?"-9999px",?top:?"-9999px",
????????????filter:?"progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='image')"
????????});
????????//插入body
????????var?body?=?document.body;?body.insertBefore(?preload,?body.childNodes[0]?);
????}
??},
??//濾鏡顯示
??_filterShow:?function()?{
????this._filterPreload();
????var?preload?=?this._preload,
????????data?=?this._data.replace(/[)'"%]/g,?function(s){?return?escape(escape(s));?});
????try{
????????preload.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src?=?data;
????}catch(e){?this._error("filter?error");?return;?}
????//設(shè)置濾鏡并顯示
????this.img.style.filter?=?"progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src=\""?+?data?+?"\")";
????this._imgShow(?ImagePreview.TRANSPARENT,?preload.offsetWidth,?preload.offsetHeight?);
??},
??
??//顯示預(yù)覽
??_imgShow:?function(src,?width,?height)?{
????var?img?=?this.img,?style?=?img.style,
????????ratio?=?Math.max(?0,?this.ratio?)?||?Math.min(?1,
????????????????Math.max(?0,?this.maxWidth?)?/?width??||?1,
????????????????Math.max(?0,?this.maxHeight?)?/?height?||?1
????????????);
????//設(shè)置預(yù)覽尺寸
????style.width?=?Math.round(?width?*?ratio?)?+?"px";
????style.height?=?Math.round(?height?*?ratio?)?+?"px";
????//設(shè)置src
????img.src?=?src;
????this.onShow();
??},
??
??//銷毀程序
??dispose:?function()?{
????//銷毀上傳文件對象
????if?(?this._upload?)?{
????????this._upload.dispose();?this._upload?=?null;
????}
????//銷毀預(yù)載圖片對象
????if?(?this._preload?)?{
????????var?preload?=?this._preload,?parent?=?preload.parentNode;
????????this._preload?=?preload.onload?=?preload.onerror?=?null;
????????parent?&&?parent.removeChild(preload);
????}
????//銷毀相關(guān)對象
????this.file?=?this.img?=?null;
??},
??//出錯
??_error:?function(err)?{
????this.onErr(err);
??}
}
?
完整實例下載(asp.net版)
完整實例下載(asp版)
由felsenlee提供的jsp版:完整實例下載(jsp版)
轉(zhuǎn)載于:https://www.cnblogs.com/cloudgamer/archive/2009/12/22/ImagePreview.html
總結(jié)
以上是生活随笔為你收集整理的JavaScript 图片上传预览效果的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux运维系统工程师系列---13
- 下一篇: CSS选择器的权重与优先规则