JavaScript 图片预览效果 推荐 |
本文标签:JavaScript,图片预览 上次写的简便无刷新文件上传系统最初的目的就是用来实现这个图片预览效果 。 兼容:ie6/7/8, firefox 3.5.5 后台支持下还兼容:opera 10.10, safari 4.0.4, chrome 3.0 ![]() ps:兼容opera, safari和chrome需要后台支持,请下载实例测试 。
【基本原理】 图片预览主要包括两个部分:从file表单控件获取图像数据,根据数据显示预览图像 。 程序有以下几种预览方式: 程序定义时就自动根据浏览器设置MODE属性: ImagePreview.MODE = $$B.ie7 || $$B.ie8 ? "filter" : $$B.firefox ? "domfile" : $$B.opera || $$B.chrome || $$B.safari ? "remote" : "simple";
调用preview方法,就会执行预览程序: if ( this.file && false !== this.onCheck() ) { this._preview( this._getData() ); }
程序初始化时就会根据mode来设置_getData数据获取程序: mode的默认值是ImagePreview.MODE,也可以在可选参数中自定义 。 在_getDataFun里面,根据mode返回数据获取程序: 代码
switch (mode) { case "filter" : return this._filterData; case "domfile" : return this._domfileData; case "remote" : return this._remoteData; case "simple" : default : return this._simpleData; }
this.file.select(); try{ return document.selection.createRange().text; } finally { document.selection.empty(); } 一般用在ie7/8,在file控件select后再用selection对象获得文件本地路径 。 domfile数据获取程序: return this.file.files[0].getAsDataURL(); 用getAsDataURL从file控件获取数据,这个方法暂时只有ff3支持 。 远程数据获取程序: this._setUpload(); this._upload && this._upload.upload(); 用_upload上传文件对象把数据提交后台,根据返回的数据再显示 。 一般数据获取程序: return this.file.value; 最原始的方法,现在只有ie6还支持从file的value直接获取本地路径 。 获取的数据作为参数,在_preview预览程序中进行预览: if ( !!data && data !== this._data ) { this._data = data; this._show(); }
远程数据获取程序没有返回值,因为它需要等待返回数据,在_preview中会自动排除 。
程序初始化时就会根据mode来设置_show预览显示程序: this._show = opt.mode !== "filter" ? this._simpleShow : this._filterShow;
var preload = this._preload = new Image(), oThis = this; preload.onload = function(){ oThis._imgShow( oThis._data, this.width, this.height ); }; preload.onerror = function(){ oThis._error(); };
然后设置_preload的src预载图片,如果成功预载就会执行_imgShow显示预览 。 _imgShow需要三个参数,包括要预览图片的src值,图片原始宽度和图片原始高度 。 ![]() ![]() 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 ); style.width = Math.round( width * ratio ) + "px"; style.height = Math.round( height * ratio ) + "px";
最后设置img的src就实现预览了 。
remote模式会先提交file控件到后台,通过返回图片数据来显示图片 。 在_remoteData远程数据获取程序中,会调用_setUpload来设置上传文件对象 。 代码
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"); } });
后台最好先根据传递的参数缩小图片,尽量减少返回数据来提高预览速度 。
filter模式在_filterData程序中得到文件本地路径,但ie7/8都不允许直接使用本地路径显示图片 。 filter模式使用_filterShow方法来显示预览图片 。 代码
var preload = this._preload = document.createElement("div"); $$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] );
然后在_filterShow中预载图片: try{ preload.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = data; }catch(e){ this._error("filter error"); return; }
this.img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src=" + data + ")";
this._imgShow( ImagePreview.TRANSPARENT, preload.offsetWidth, preload.offsetHeight );
filter模式使用的是AlphaImageLoader滤镜 。 sizingMethod有三种方式: 对于预载图片对象_preload,要获取图片的原始尺寸,所以要用image方式 。 而src属性的路径还支持本地路径,是实现filter模式的关键所在 。
ff从3.0(或更早)开始,就不能通过value获取file的完整路径,也不支持直接用本地路径显示图片 。 这个File对象有三个获取文件数据的方法: File对象还有支持两个属性:fileName(文件名,不包括路径)和fileSize(文件大小) 。
上面已经多次提到Data URI,详细介绍请看秦歌的“Data URI 和 MHTML” 。 由于opera,safari和chrome需要remote模式的浏览器都支持Data URI,所以程序返回的是Data URI形式的数据 。 在filter模式需要一个透明图片来去掉img默认显示的小图标,一般的方法需要一个图片文件 。 data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw== 支持Data URI的情况下,只要把img的src设置为这个值就可以显示一个透明图片了 。 虽然ie6/7不支持Data URI,还可以用mhtml来代替 。 ![]() ![]() Content-Type: multipart/related; boundary="_CLOUDGAMER" --_CLOUDGAMER Content-Location:blankImage Content-Transfer-Encoding:base64 R0lGODlhAQABAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==
其中blankImage表示要获取的数据在文件对应的Content-Location 。 document.scripts[document.scripts.length - 1].getAttribute("src", 4)
结合Data URI 和 MHTML可以这样得到透明图片数据: ImagePreview.TRANSPARENT = $$B.ie7 || $$B.ie6 ? "mhtml:" + document.scripts[document.scripts.length - 1].getAttribute("src", 4) + "!blankImage" : "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
程序还有一个dispose方法用于销毁程序 。 说到移除元素,顺便说一下超空间(DOM hyperspace),这是从“ppk谈javascript”中看到的 。 <body></body> <script> var elm = document.createElement("div"); alert(elm.parentNode); document.body.removeChild(document.body.appendChild(elm)); alert(elm.parentNode); </script>
那么用innerHTML清除呢?再测试以下代码: <body><div id="test"></div></body> <script> var elm = document.getElementById("test"); document.body.innerHTML = ""; alert(elm.parentNode); </script>
那个碎片对象貌似没什么用(难道为了保证有parentNode?),那是不是innerHTML就一定比removeChild好呢? 代码
<body> <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>
个人推测,ie在使用innerHTML时,被移除的元素会变成一个个单独的元素,失去了彼此的联系 。 那么removeChild的好处是移除的元素能再次使用,兼容性好,不好的地方是ie会产生一个没用的碎片对象 。
一般来preview方法都是在onchange中调用,即选择文件后立即显示预览 。 在不需要程序时最好执行一次dispose方法来销毁程序,防止内存泄漏等 。 利用ImagePreview.TRANSPARENT可以显示透明图片,而不需另外隐藏或增加文件 。 第二个实例中的ResetFile是用来重置file控件的,详细参考这里file的reset 。 asp版本使用Persits.Jpeg组件缩放图片,测试请先安装该组件 。
实例化时,有两个必要参数,分别是file控件对象和img元素的预览显示对象: new ImagePreview( file, img );
还提供了以下方法:
代码
SPAN style="COLOR: rgb(0,0,255)">this.timeout = opt.timeout;var ImagePreview = function(file, img, options) { this.file = $$(file);//文件对象 this.img = $$(img);//预览图片对象 this._preload = null;//预载图片对象 this._data = "";//图像数据 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; //设置数据获取程序 this._getData = this._getDataFun(opt.mode); //设置预览显示程序 this._show = opt.mode !== "filter" ? this._simpleShow : this._filterShow; }; //根据浏览器获取模式 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 = { //设置默认属性 _setOptions: function(options) { this.options = {//默认值 mode: ImagePreview.MODE,//预览模式 ratio: 0,//自定义比例 maxWidth: 0,//缩略图宽度 maxHeight: 0,//缩略图高度 onCheck: function(){},//预览检测时执行 onShow: function(){},//预览图片时执行 onErr: function(){},//预览错误时执行 //以下在remote模式时有效 action: undefined,//设置action timeout: 0//设置超时(0为不设置) }; return $$.extend(this.options, options || {}); }, //开始预览 preview: function() { if ( this.file && false !== this.onCheck() ) { this._preview( this._getData() ); } }, //根据mode返回数据获取程序 _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; } }, //滤镜数据获取程序 _filterData: function() { this.file.select(); try{ return document.selection.createRange().text; } finally { document.selection.empty(); } }, //domfile数据获取程序 _domfileData: function() { return this.file.files[0].getAsDataURL(); }, //远程数据获取程序 _remoteData: function() { this._setUpload(); this._upload && this._upload.upload(); }, //一般数据获取程序 _simpleData: function() { return this.file.value; }, //设置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"); } }); } }, //预览程序 _preview: function(data) { //空值或相同的值不执行显示 if ( !!data && data !== this._data ) { this._data = data; this._show(); } }, //设置一般预载图片对象 _simplePreload: function() { if ( !this._preload ) { var preload = this._preload = new Image(), oThis = this; preload.onload = function(){ oThis._imgShow( oThis._data, this.width, this.height ); }; preload.onerror = function(){ oThis._error(); }; } }, //一般显示 _simpleShow: function() { this._simplePreload(); this._preload.src = this._data; }, //设置滤镜预载图片对象 _filterPreload: function() { if ( !this._preload ) { var preload = this._preload = document.createElement("div"); //隐藏并设置滤镜 $$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; try{ preload.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = data; }catch(e){ this._error("filter error"); return; } //设置滤镜并显示 this.img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src=" + data + ")"; this._imgShow( ImagePreview.TRANSPARENT, preload.offsetWidth, preload.offsetHeight ); }, //显示预览 _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 ); //设置预览尺寸 style.width = Math.round( width * ratio ) + "px"; style.height = Math.round( height * ratio ) + "px"; //设置src img.src = src; this.onShow(); }, //销毁程序 dispose: function() { //销毁上传文件对象 if ( this._upload ) { this._upload.dispose(); this._upload = null; } //销毁预载图片对象 if ( this._preload ) { var preload = this._preload, parent = preload.parentNode; this._preload = preload.onload = preload.onerror = null; parent && parent.removeChild(preload); } //销毁相关对象 this.file = this.img = null; }, //出错 _error: function(err) { this.onErr(err); } } 完整实例下载(asp与asp.net) this.ratio = opt.ratio; this.maxWidth = opt.maxWidth; this.maxHeight = opt.maxHeight; this.onCheck = opt.onCheck; this.onShow = opt.onShow; this.onErr = opt.onErr; //设置数据获取程序 this._getData = this._getDataFun(opt.mode); //设置预览显示程序 this._show = opt.mode !== "filter" ? this._simpleShow : this._filterShow; }; //根据浏览器获取模式 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 = { //设置默认属性 _setOptions: function(options) { this.options = {//默认值 mode: ImagePreview.MODE,//预览模式 ratio: 0,//自定义比例 maxWidth: 0,//缩略图宽度 maxHeight: 0,//缩略图高度 onCheck: function(){},//预览检测时执行 onShow: function(){},//预览图片时执行 onErr: function(){},//预览错误时执行 //以下在remote模式时有效 action: undefined,//设置action timeout: 0//设置超时(0为不设置) }; return $$.extend(this.options, options || {}); }, //开始预览 preview: function() { if ( this.file && false !== this.onCheck() ) { this._preview( this._getData() ); } }, //根据mode返回数据获取程序 _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; } }, //滤镜数据获取程序 _filterData: function() { this.file.select(); try{ return document.selection.createRange().text; } finally { document.selection.empty(); } }, //domfile数据获取程序 _domfileData: function() { return this.file.files[0].getAsDataURL(); }, //远程数据获取程序 _remoteData: function() { this._setUpload(); this._upload && this._upload.upload(); }, //一般数据获取程序 _simpleData: function() { return this.file.value; }, //设置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"); } }); } }, //预览程序 _preview: function(data) { //空值或相同的值不执行显示 if ( !!data && data !== this._data ) { this._data = data; this._show(); } }, //设置一般预载图片对象 _simplePreload: function() { if ( !this._preload ) { var preload = this._preload = new Image(), oThis = this; preload.onload = function(){ oThis._imgShow( oThis._data, this.width, this.height ); }; preload.onerror = function(){ oThis._error(); }; } }, //一般显示 _simpleShow: function() { this._simplePreload(); this._preload.src = this._data; }, //设置滤镜预载图片对象 _filterPreload: function() { if ( !this._preload ) { var preload = this._preload = document.createElement("div"); //隐藏并设置滤镜 $$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; try{ preload.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = data; }catch(e){ this._error("filter error"); return; } //设置滤镜并显示 this.img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src=" + data + ")"; this._imgShow( ImagePreview.TRANSPARENT, preload.offsetWidth, preload.offsetHeight ); }, //显示预览 _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 ); //设置预览尺寸 style.width = Math.round( width * ratio ) + "px"; style.height = Math.round( height * ratio ) + "px"; //设置src img.src = src; this.onShow(); }, //销毁程序 dispose: function() { //销毁上传文件对象 if ( this._upload ) { this._upload.dispose(); this._upload = null; } //销毁预载图片对象 if ( this._preload ) { var preload = this._preload, parent = preload.parentNode; this._preload = preload.onload = preload.onerror = null; parent && parent.removeChild(preload); } //销毁相关对象 this.file = this.img = null; }, //出错 _error: function(err) { this.onErr(err); } } 完整实例下载(asp与asp.net) |