一、前言
這兩天恰好有一位同事問我怎樣做一個圖片預覽功能。作為現代人的我們首先想到的當然是HTML5啦,其實HTML5做圖片預覽已經是一個老生常談的問題了。我在這里就簡單說說其中相關的一些東西,當然會附上我們的源碼。在 HTML5 之前我們做圖片預覽主流做法有兩種,第一種是通過 Flash 插件來做預覽,第二種是 Ajax 實現的假預覽,也就是說選擇圖片文件后,圖片其實已經異步上傳到服務器,服務器處理后返回圖片路徑,前端得到響應結果做出處理從而使圖片顯示在界面上。而有了 HTML5 之后就可以強烈鄙視上面兩種做法了。
二、FileReader
要做圖片預覽功能,就不得不介紹一下 FileReader,顧名思義,它是用來讀取文件的。當然新東西總會有一些頑固派排斥的,我們先來看看其兼容性如何(這不是本文討論的重點)。
PC端兼容列表
移動端兼容列表
兼容性的話大家根據自己的需求參考一下上面的對照表,我們接着來看看 FileReader 的幾個常用屬性和常用方法
屬性
- FileReader.onload 讀取完成
- FileReader.result 讀取結果
- FileReader.error 讀取錯誤
- FileReader.readyState 前文檔的狀態
方法
- FileReader.abort() 中斷讀取-無參數
- FileReader.readAsArrayBuffer(file) 將文件讀取為ArrayBuffer 對象 參數:文件
- FileReader.readAsBinaryString(file) 將文件讀取為二進制碼 - 參數:文件
- FileReader.readAsDataURL(file) 將文件讀取為DataURL 參數:文件
- FileReader.readAsText(file) 將文件讀取為文本 參數:文件
廢話不多說,我們通過代碼來更直觀點認識上面的屬性和方法。回歸到需求,做一個圖片預覽功能。首先理一理我們需要有的東西,第一要素當然是文件(文件選擇器),第二當然是預覽(容器)。
html 代碼 (樣式我順手加上了)
<!DOCTYPE html> <html> <head> <title>Cboyce-HTML5圖片預覽</title> <style type="text/css"> /*主容器*/ .container{ width: 90%; margin-top: 20px; } /*圖片預覽容器*/ .container .img-prev-container{ width: 200px; height: 100px; margin:10px auto; border:1px solid #ccc; } /*預覽圖片樣式*/ .container .img-prev-container img{ width: 100%; height: 100%; } </style> </head> <body> <div class="container"> <div class="img-prev-container"> </div> <input type="file" value="請選擇圖片" id="fileSelecter" /> </div> </body> </html>
接下來該 FileReader 出場了
window.onload = function(){ //觸發 change 事件 GetDomById('fileSelecter').onchange = function(event){ //獲取文件對象 var file = event.target.files[0]; //創建reader對象 var reader = new FileReader(); //讀取完成后觸發 reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; console.log(_img_src) //添加預覽圖片到容器框 var img = document.createElement('img'); img.setAttribute('src',_img_src); GetDomById('img-perv-div').appendChild(img); } //獲取到數據的url 圖片將轉成 base64 格式 reader.readAsDataURL(file); } } //簡化 document.getElementById() 函數 function GetDomById(id){ return document.getElementById(id); }
細節注意:這里的圖片格式默認轉為 base64
補充說明:
event.target 屬性,其特點在我們的代碼中其實不忙看出來 "捕獲當前事件作用的對象",通俗點來講就是,誰觸發了該事件,我就能通過該事件的 target 拿到誰。
其實上述代碼還有一個小 bug "換圖變成多圖"。 請看下圖
修復:改造 onload
reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; //預覽圖的容器 var _img_container = GetDomById('img-perv-div') //添加預覽圖片到容器框 var _imgs = _img_container.getElementsByTagName('img'); //容器中沒有則創建,有則修改 src 屬性 if(!_imgs.lenght){ _imgs[0] = document.createElement('img'); _imgs[0].setAttribute('src',_img_src); _img_container.appendChild(_imgs[0]); }else{ _imgs[0].setAttribute('src',_img_src); } }
解決bug
三、實現拖拽預覽
上面我們已經把基礎功能給完成了,接下來我們給該程序加個拓展--拖拽圖片到預覽框自動加載。 要完成該功能還是得靠 HTML5 的 Drag 和 drop。如果你還搞不清楚我們要做什么,那我們先來看下最終效果。
在代碼開始之前我們先來了解兩個實現該功能最為關鍵的事件。 1. dragover 拖拽一個對象到目標對象上面觸發該事件 2. drop 拖放事件結束時觸發。通俗來講就是當我們拖拽一個對象到目標對象上后放開(松開鼠標左鍵)該對象的時候觸發
接下來我們來看下代碼,這里也對之前的代碼做出了一些改造
window.onload = function(){ //預覽圖的容器 var _img_container = getDomById('img-perv-div') //創建reader對象 var reader = new FileReader(); //觸發 change 事件 getDomById('fileSelecter').onchange = function(event){ //獲取文件對象 var file = event.target.files[0]; //讀取完成后觸發 reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; //添加預覽圖片到容器框 showPrevImg(_img_container,_img_src); } //獲取到數據的url 圖片將轉成 base64 格式 reader.readAsDataURL(file); } //添加拖放支持 _img_container.addEventListener('dragover',function(ev){ ev.preventDefault();//阻止默認事件。比如說Chrome是直接將圖片用瀏覽器打開 },false) _img_container.addEventListener('drop',function(ev){ ev.preventDefault(); reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; //圖片預覽處理 showPrevImg(_img_container,_img_src); } reader.readAsDataURL(ev.dataTransfer.files[0]) },false) } //簡化 document.getElementById() 函數 function getDomById(id){ return document.getElementById(id); } //圖片預覽處理函數 function showPrevImg(_img_container,_img_src){ //添加預覽圖片到容器框 var _imgs = _img_container.getElementsByTagName('img'); //容器中沒有則創建,有則修改 src 屬性 if(!_imgs.lenght){ _imgs[0] = document.createElement('img'); _imgs[0].setAttribute('src',_img_src); _img_container.appendChild(_imgs[0]); }else{ _imgs[0].setAttribute('src',_img_src); } }
代碼分析
addEventListener('dragover',function(ev){ ev.preventDefault(); },false)
這段代碼重點在於 ev.preventDefault();
阻止默認行為,如果我們不阻止其默認行為將會產生下面的后果
接下來要做的就是拖放結束展示圖片預覽效果
_img_container.addEventListener('drop',function(ev){ ev.preventDefault(); reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; //添加預覽圖片到容器框 showPrevImg(_img_container,_img_src); } reader.readAsDataURL(ev.dataTransfer.files[0]) },false)
這里用到 event.dataTransfer 我們補充一下,我們先來看下他的定義
dataTransfer 拖曳數據傳遞對象,其提供了對於預定義的剪貼板格式的訪問,以便在拖曳操作中使用
通俗來講就是,我們在拖曳操作中可以使用它來操作我們拖曳的對象。比如拖圖片,通過它能拿到我們所拖曳的圖片對象
最后,強迫症犯了,稍微寫了點樣式美化了一下完整代碼如下

<!DOCTYPE html> <html> <head> <title>Cboyce-HTML5圖片預覽</title> <style type="text/css"> body{ font-family: '微軟雅黑'; } /*主容器*/ .container{ width: 90%; margin-top: 20px; } /*每一個圖片預覽項容器*/ .img-prev-item{ width: 200px; height: 200px; display: inline-block; border:1px solid #ccc; text-align: center; border-radius: 3px; } /*圖片預覽容器*/ .container .img-prev-container{ width: 200px; height: 156px; margin: 0 auto; border-bottom: 1px solid #ccc; vertical-align: middle; display: table-cell; padding: 2px; color: #838383; text-align: center } /*預覽圖片樣式*/ .container .img-prev-container img{ width: 100%; height: auto; max-height: 100%; } /*label*/ .selfile{ background-color: #0095ff; color: white; padding: 6px 58px; border-radius: 5px; } /*工具條 div*/ .tool{ padding-top: 9px; } /*隱藏文件選擇器*/ #fileSelecter{ display: none; } </style> </head> <body> <div class="container"> <div class="img-prev-item"> <div class="img-prev-container" id="img-perv-div"> 請選擇圖片或者<br />將圖片拖拽至此 </div> <div class="tool"> <label for="fileSelecter" class="selfile">請選擇圖片</label> <input type="file" value="請選擇圖片" id="fileSelecter" /> </div> </div> </div> <script type="text/javascript"> window.onload = function(){ //預覽圖的容器 var _img_container = getDomById('img-perv-div') //創建reader對象 var reader = new FileReader(); //觸發 change 事件 getDomById('fileSelecter').onchange = function(event){ //獲取文件對象 var file = event.target.files[0]; //讀取完成后觸發 reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; //添加預覽圖片到容器框 showPrevImg(_img_container,_img_src); } //獲取到數據的url 圖片將轉成 base64 格式 reader.readAsDataURL(file); } //添加拖放支持 _img_container.addEventListener('dragover',function(ev){ //ev.stopPropagation(); ev.preventDefault();//阻止默認事件。比如說Chrome是直接將圖片用瀏覽器打開 console.log('dragover') },false) // _img_container.addEventListener('dragend',function(ev){ // ev.stopPropagation(); // ev.preventDefault(); // console.log('dragend') // },false) _img_container.addEventListener('drop',function(ev){ //ev.stopPropagation(); ev.preventDefault(); console.log('drop') //console.log(ev.dataTransfer.files[0]) reader.onload = function(ev){ //獲取圖片的url var _img_src = ev.target.result; //添加預覽圖片到容器框 showPrevImg(_img_container,_img_src); } reader.readAsDataURL(ev.dataTransfer.files[0]) },false) } //簡化 document.getElementById() 函數 function getDomById(id){ return document.getElementById(id); } function showPrevImg(_img_container,_img_src){ _img_container.innerHTML=""; //添加預覽圖片到容器框 var _imgs = _img_container.getElementsByTagName('img'); //容器中沒有則創建,有則修改 src 屬性 if(!_imgs.lenght){ _imgs[0] = document.createElement('img'); _imgs[0].setAttribute('src',_img_src); _img_container.appendChild(_imgs[0]); }else{ _imgs[0].setAttribute('src',_img_src); } } //接下來要做的就是拖放結束展示圖片預覽效果 </script> </body> </html>
運行效果如下
四、結語
基本上實現以及代碼的原理也就解釋到這了。其實前端做的圖片預覽功能大多數需求是用來上傳到服務器的。不得不提到的是這里的拖拽預覽雖然看起來體驗不錯,但是要將該文件上傳就得做一些特殊處理。這個我就留到后面的博客再講了,有問題的朋友可以直接留言。
限於筆者技術,文章觀點難免有不當之處,希望發現問題的朋友幫忙指正,筆者將會及時更新。也請轉載的朋友注明文章出處並附上原文鏈接,以便讀者能及時獲取到文章更新后的內容,以免誤導讀者。筆者力求避免寫些晦澀難懂的文章(雖然也有人說這樣顯得高逼格,專業),盡量使用簡單的用詞和例子來幫助理解。如果表達上有好的建議的話也希望朋友們在評論處指出。
本文為作者原創,轉載請注明出處! Cboyce