打造自己的jQuery Plugin+圖片等比例縮小
最近的專案客戶要求一個功能。
例如一個200 * 100 的方框中,要擺入一張圖片,來源圖片的尺寸比例
都不一致,但都要塞入這個框框而且不能變形,所以需要等比例的顯示縮圖。
今天把我研究來的心得寫出來,順便研究了一下自訂的jQuery Plugin怎麼做。
如果有看過黑大的邊做邊學jQuery系列,應該對自訂jQuery Plugin不陌生。
裡面有影片又有教學,認真地看一次加上實作,馬上就能學會製作簡單的plugin
參考連結在這:邊做邊學 jQuery 系列 13- 打造自己的jQuery Plugin
基本上只要把下面這個框架Copy起來,裡面再加上自己想實作的code
(function($) {
$.fn.extend({
method1: function(options) {
//...code...
},
method2: function(options) {
//...code...
}
});
})(jQuery);
其實還滿簡單的。
那開始寫我的等比例縮圖的程式
其實想起來不難,當 圖片的寬 超過我要的框框寬度時,
將寬設為框框寬度,將高設為 框框寬度* ( 圖片高度 /圖片寬度 )
當圖片高度高過框框高度時
將高設為框框高度,並將寬度設為 框框高度 * ( 圖片寬度 /圖片高度 )
舉個例:今天我的框框最大寬高是 200 * 100,載入的圖片是 600 * 300
寬 600 > 200 => 寬設為 200,高設為 200 * ( 300/ 600 ) = 100
高 300 > 100 => 高設為 100,寬設為 100 * ( 600/ 300 ) = 200
只是些簡單的數學,但經我嘗試之後,麻煩的是在
因為當圖片尚未載入完成時,讀取圖片的高度容易發生錯誤。
例如當一張圖片只讀取一半時就去執行改變寬高的動作,寬度因為只要出現一點點
就可以讀到完整的寬度,但高度卻只讀到一半的高度,導致運算結果會有誤。
所以要在圖片完全載入之後,才能去改變圖片大小。但是IE跟Firefox對圖片完成載入
的事件支援又不一樣,光這個問題就讓我傷腦筋好久。先試寫了一版算是比較不會
有問題的Plugin來用用看。
先看function的部分
//當瀏覽器為IE時,當圖片readyState狀態為complete時才執行動作
function IEBrower(event) {
if (this.readyState == "complete") {
ChangeImg(this, event);
}
}
//當瀏覽器為Firefox時,當圖片complete狀態為true時才執行動作
function FFBrower(event) {
if (this.complete == true) {
ChangeImg(this,event);
}
}
function ChangeImg(e,event) {
var HeightWidth = e.offsetHeight / e.offsetWidth; //設置高寬比
var WidthHeight = e.offsetWidth / e.offsetHeight; //設置寬高比
//如同上方說明的邏輯
if (e.offsetWidth > event.data.MaxWidth) {
e.width = event.data.MaxWidth;
e.height = event.data.MaxWidth * HeightWidth;
}
if (e.offsetHeight > event.data.MaxHeight) {
e.height = event.data.MaxHeight;
e.width = event.data.MaxHeight * WidthHeight;
}
}
再來看Plugin的部分
(function ($) {
//抓瀏覽器的名稱
var appname = navigator.appName.toLowerCase();
function changeImgSize(target, options) {
//處理參數
var settings = { MaxWidth: 100, MaxHeight: 100 };
$.extend(settings, options);
if (appname.indexOf("netscape") == -1) {
//ie
$(target).bind("readystatechange"
, { MaxWidth: settings.MaxWidth, MaxHeight: settings.MaxHeight }
, IEBrower);
} else {
//firefox
$(target).bind("load"
, { MaxWidth: settings.MaxWidth, MaxHeight: settings.MaxHeight }
, FFBrower);
}
}
$.fn.extend({
reSize: function (options) {
return this.each(function () {
changeImgSize(this, options);
});
}
});
})(jQuery);
7、8行我覺得很好用,可先設計這個plugin的預設值,當使用這個plugin時,
如果未傳入參數,則使用預設值,若有傳入則會將傳入的參數蓋過預設值。
因此我框框預設值是100*100。如果我只傳入寬度MaxWidth=200,那框框的最大
寬高就變成200*100。
10~20行是判斷哪種瀏覽器,如果是ie就在圖片上綁readystatechange事件
如果是firefox則綁load事件。
23~29行就是擴充jQuery的方法,方法名叫reSize,並用可以串接的方式去寫。
整個plugin的邏輯是,將傳進來的img去執行changeImgSize(),判斷瀏覽器並
綁上事件,當圖片載入狀態完成時,去執行ChangeImg() 等比例縮圖。
寫完之後,只要將js檔載入,就可以在頁面上使用自訂的plugin了。
<script type="text/javascript">
$(function () {
$("img").reSize({ MaxWidth: 200, MaxHeight: 200 });
});
</script>
頁面放八張圖,尺寸及長寬都不一
<table style="border: 1px solid green; background-color: #313131;" class="MyPlugin">
<tr>
<td>
<img id="img1" src="/images/1.jpg" />
</td>
<td>
<img id="img2" src="/images/2.jpg" />
</td>
<td>
<img id="img3" src="/images/3.jpg" />
</td>
<td>
<img id="img4" src="/images/4.jpg" />
</td>
</tr>
<tr>
<td>
<img id="img5" src="/images/5.jpg" />
</td>
<td>
<img id="img6" src="/images/6.jpg" />
</td>
<td>
<img id="img7" src="/images/7.jpg" />
</td>
<td>
<img id="img8" src="/images/8.jpg" />
</td>
</tr>
</table>
結果
可以看到圖片都很安分的擺在框框裡面。更改成長方形的框並改一下版面配置
依舊有用,並且IE跟FireFox都可用。
備註:
不過不是每次reload時都會正確縮放,我猜想應該是事件綁的時間太晚了吧。
我將原本沒有寫成plugin的code寫上
<script type="text/javascript">
var appname = navigator.appName.toLowerCase();
function Wa_SetImgAutoSize(e) {
var img = e; //獲取圖片
var MaxWidth = 190; //設置圖片寬度界限
var MaxHeight = 190; //設置圖片高度界限
var HeightWidth = img.offsetHeight / img.offsetWidth; //設置高寬比
var WidthHeight = img.offsetWidth / img.offsetHeight; //設置寬高比
if (appname.indexOf("netscape") == -1) {
//ie
if (img.readyState != "complete")
return false;
} else {
//firefox
if (img.complete != true)
return false;
}
if (img.offsetWidth > MaxWidth) {
img.width = MaxWidth;
img.height = MaxWidth * HeightWidth;
}
if (img.offsetHeight > MaxHeight) {
img.height = MaxHeight;
img.width = MaxHeight * WidthHeight;
}
}
</script>
<table style="border: 1px solid green; background-color: #313131;" class="MyPlugin">
<tr>
<td>
<img id="img1" src="/images/1.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
<td>
<img id="img2" src="/images/2.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
<td>
<img id="img3" src="/images/3.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
</tr>
<tr>
<td>
<img id="img4" src="/images/4.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
<td>
<img id="img5" src="/images/5.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
<td>
<img id="img6" src="/images/6.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
</tr>
<tr>
<td>
<img id="img7" src="/images/7.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
<td>
<img id="img8" src="/images/8.jpg" onload="Wa_SetImgAutoSize(this)" />
</td>
<td></td>
</tr>
</table>
由於上面這個寫法是直接在img上綁onload事件,當完成時執行修改大小的function
因此幾乎百分之百可以執行成功。但因為我不想一個tag一個tag去綁事件,並且想寫成
plugin的方式,目前還沒有想到好的解法。
剛剛問了同事,他說不要把方法寫在$(function(){…})裡面,直接寫在img的最下面,如:
經過測試之後,果然成功了。
附上我自製的Plugin,有興趣的可以玩玩看。MyPlugin.zip (只有js檔,沒有照片喔..)