HTML5圖片拖拽預覽原理及實現

1、前言

這兩天剛好有一位同事問我怎樣作一個圖片預覽功能。做爲現代人的咱們首先想到的固然是HTML5啦,其實HTML5作圖片預覽已是一個老生常談的問題了。我在這裏就簡單說說其中相關的一些東西,固然會附上咱們的源碼。在 HTML5 以前咱們作圖片預覽主流作法有兩種,第一種是經過 Flash 插件來作預覽,第二種是 Ajax 實現的假預覽,也就是說選擇圖片文件後,圖片其實已經異步上傳到服務器,服務器處理後返回圖片路徑,前端獲得響應結果作出處理從而使圖片顯示在界面上。而有了 HTML5 以後就能夠強烈鄙視上面兩種作法了。javascript

2、FileReader

要作圖片預覽功能,就不得不介紹一下 FileReader,顧名思義,它是用來讀取文件的。固然新東西總會有一些頑固派排斥的,咱們先來看看其兼容性如何(這不是本文討論的重點)。css

PC端兼容列表html

移動端兼容列表 前端

兼容性的話你們根據本身的需求參考一下上面的對照表,咱們接着來看看 FileReader 的幾個經常使用屬性和經常使用方法java

屬性web

  1. FileReader.onload 讀取完成
  2. FileReader.result 讀取結果
  3. FileReader.error 讀取錯誤
  4. FileReader.readyState 前文檔的狀態

方法canvas

  1. FileReader.abort() 中斷讀取-無參數
  2. FileReader.readAsArrayBuffer(file) 將文件讀取爲ArrayBuffer 對象 參數:文件
  3. FileReader.readAsBinaryString(file) 將文件讀取爲二進制碼 - 參數:文件
  4. FileReader.readAsDataURL(file) 將文件讀取爲DataURL 參數:文件
  5. FileReader.readAsText(file) 將文件讀取爲文本 參數:文件

廢話很少說,咱們經過代碼來更直觀點認識上面的屬性和方法。迴歸到需求,作一個圖片預覽功能。首先理一理咱們須要有的東西,第一要素固然是文件(文件選擇器),第二固然是預覽(容器)。瀏覽器

html 代碼 (樣式我順手加上了)ruby

<!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

3、實現拖拽預覽

上面咱們已經把基礎功能給完成了,接下來咱們給該程序加個拓展--拖拽圖片到預覽框自動加載。 要完成該功能仍是得靠 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>
展開完整代碼

運行效果以下 

4、結語

基本上實現以及代碼的原理也就解釋到這了。其實前端作的圖片預覽功能大多數需求是用來上傳到服務器的。不得不提到的是這裏的拖拽預覽雖然看起來體驗不錯,可是要將該文件上傳就得作一些特殊處理。這個我就留到後面的博客再講了,有問題的朋友能夠直接留言。

限於筆者技術,文章觀點不免有不當之處,但願發現問題的朋友幫忙指正,筆者將會及時更新。也請轉載的朋友註明文章出處並附上原文連接,以便讀者能及時獲取到文章更新後的內容,以避免誤導讀者。筆者力求避免寫些晦澀難懂的文章(雖然也有人說這樣顯得高逼格,專業),儘可能使用簡單的用詞和例子來幫助理解。若是表達上有好的建議的話也但願朋友們在評論處指出。

本文爲做者原創,轉載請註明出處! Cboyce

相關文章
相關標籤/搜索