發佈框前端體驗優化

經過前端代碼,利用File API對圖文發佈操做進行優化,提升用戶體驗。

圖片描述
發佈框是web應用的一種常見圖文發佈功能,在微博、評論、論壇、博客或內容管理系統等產品中常用。作好發佈框的交互設計,能提升用戶的編輯效率,提升用戶體驗,給產品增長錦上添花的效果。javascript

需求背景:

在實際的項目中,遇到了如下需求,用戶(運營人員)能夠經過發佈框發佈話題相關內容,產品經理指望在此發佈框上實現如下功能:
一、用戶能夠拖動文件,當文件進入瀏覽器時提示用戶拖動文件到發佈框;
二、當拖動的文件(例如.exe)不符合要求時,給予拒絕提示,不能上傳;
三、當拖動文件(批量)爲圖片或文檔時,解析圖片和文字,預覽(或上傳),其他類型的文件拒絕;html

圖片描述
四、發佈框支持圖片複製、和QQ、PrintScreen鍵等工具的截圖後粘貼(或ctrl+v)。前端

圖片描述

技術點:

  1. 拖放功能(drag & drop)
  2. File API功能
  3. 複製粘貼事件

drag & drop

拖放是 HTML5 中常見的功能。即:把抓取的對象拖放到其餘位置(想一想一下兩個元素換位)。與他相關就是兩個動做——拖和放。因此,它涉及到兩個元素。一個是被拖的元素,稱爲拖放源;另外一個是要放的目標,稱爲拖放目標。所涉及到的事件就是兩類:drag(源)和drop(目標)。
與它相關的兩個事件(按觸發的前後順序,參照物是鼠標指針而非文件邊緣):java

拖拽源:

  1. dragstart:按下鼠標時觸發
  2. drag:按下鼠標持續時觸發 (執行屢次)
  3. dragend:鼠標放開時觸發

投放目標:

  1. dragenter:拖動目標且鼠標進入投放區時觸發
  2. dragover:拖動目標且鼠標移動在投放區時觸發(每隔 350 毫秒會觸發一次)
  3. dragleave:拖動對象且離開投放區時觸發
  4. drop:拖動對象且在投放區放開鼠標時觸發(須要在dragover上設置禁止默認事件,纔會有觸發,奇怪的設定)

兼容性

這裏咱們主要用到了投放目標的drop事件,它的兼容性以下圖所示:
圖片描述node

主要代碼:

<textarea  rows="" cols="" id="myTextarea"></textarea>
    <script type="text/javascript">
    //<!--
        let myTextarea = document.getElementById('myTextarea');
        document.addEventListener("dragenter", function(e) {
            // 被拖動物品進入頁面給予虛線邊提示
            e.preventDefault();
            myTextarea.style.border = "#666 1px dashed";
        }, false);
        document.addEventListener("dragleave", function(e) {
            // 被拖動物品離開頁面
            e.preventDefault();
            myTextarea.style.border = "none";
        }, false);

        myTextarea.addEventListener("dragover", function(e) {
            // 被拖動物品移動
            e.preventDefault();
        }, false);

        myTextarea.addEventListener('drop', function(e){
             // 被拖動物品放置
             e.preventDefault();
             let upfile = e.dataTransfer.files;
             console.log(upfile);
         },false)
    //-->
    </script>

 遇到問題

dragenter(dragleave)的事件觸發相似於mouseover(mouseout),當在子節點內外的拖動時,會觸發子節點的drage事件並向上冒泡,引發屢次觸發當前節點的drag事件。舉例來講,咱們只在document上綁定dragenter事件,可是任何進出頁面子標籤的拖動,都會再次觸發document的dragenter事件。能夠經過是否包含和relatedTarget來解決。web

圖片描述
兩種方向的拖動都會觸發目標節點的dragenter事件,這不是咱們想要的結果!segmentfault

//判斷兩個a中是否包含b
function contains(a,b){
    return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16);
}
NodeB.addEventListener("dragenter", (e) => {
    event.preventDefault();
    let related = e.relatedTarget || e.fromElement;
    if ((related != NodeB) && !NodeB.contains(related)) {
        //do something
    }
}, false);
NodeB.addEventListener("dragleave", (e) => {
    e.preventDefault();
    let related = e.relatedTarget || e.toElement;
    if ((related != NodeB) && !NodeB.contains(related)) {
        //do something
    }
}, false);

event對象有一個屬性叫relatedTarget,這個屬性就是用來判斷enter和leave事件目標節點的相關節點的屬性。簡單的來講就是當觸發enter事件時,relatedTarget屬性表明的就是鼠標剛剛離開的那個節點,當觸發leave事件時它表明的是鼠標移向的那個對象。因爲IE不支持這個屬性,不過它有代替的屬性,分別是 fromElement和toElement, node.contains()返回的是一個布爾值,來表示傳入的節點是否爲該節點的後代節點。利用這兩個特性,就能夠解決這個問題。後端

DataTransfer對象

任何拖動事件,event參數中都會一個DataTransfer屬性,它有一些經常使用屬性和方法:
一、DataTransfer.effectAllowed和dropEffect,用來設置拖和放的鼠標指針類型,用處不大,具體效果可點擊此處查看
二、DataTransfer.files,拖拽的本地文件列表。若是拖動操做不涉及拖動文件,則此屬性爲空列表。
三、DataTransfer.items,只讀,提供DataTransferItemList對象,該對象是全部拖動數據的列表,包含DataTransfer.files。數組

讀取文件:

// 圖片校驗  
checkFile(file){
    const isJPG = /jpg|jpeg|png/.test(file.type.toLowerCase());
    const isFile = /jpg|jpeg|png/.test(file.name.toLowerCase());
    if (!isJPG || !isFile) {
    console.log('只能夠上傳jpg、png的圖片。')
    }
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isLt2M) {
    console.log('圖片尺寸不容許超過2MB!')
    }
    return isJPG && isFile && isLt2M;
  }
let content:string, pic:[]
textareaDropfn(e){
    e.preventDefault();
    let fileList = e.dataTransfer.files;
    let contentText:string;
    for (let i = 0; i < fileList.length; i++) {
      const el = fileList[i];
      if(el.type == 'text/plain' || el.type == 'text/html'){
        // 文本文件
        let reader = new FileReader();
        let that = this;
        reader.onload = (function(file) {
          return function(e) {
            that.weiboContent += this.result;
          };
        })(el);
        //讀取文本內容
        reader.readAsText(el, "gbk");
      } else if(this.checkFile(el)) {
//讀取圖片
        this.pic.push(el);
      }
    }
    return false;
  }

 文件操做:

DataTransfer.files對象包含了咱們拖動的File對象,是個數組對象,包含如下屬性:瀏覽器

  • name:文件名,該屬性只讀。
  • size:文件大小,單位爲字節,該屬性只讀。
  • type:文件的 MIME 類型,若是分辨不出類型,則爲空字符串,該屬性只讀。
  • lastModified:文件的上次修改時間,格式爲時間戳。
  • lastModifiedDate:文件的上次修改時間,格式爲 Date 對象實例

咱們須要name、type、size屬性來校驗格式和大小是否知足要求。HTML5給咱們提供了FileReader API 用於讀取文件,即把文件內容讀入內存。它的參數是 File 對象或 Blob 對象。

什麼是File

在前端開發中,最多見的file就是表單上傳的文件,它是一個file對象,而FileList對象則是這些file對象的集合列表,表明所選擇的全部文件。file對象繼承於Blob對象,該對象表示二進制原始數據,提供slice方法(能夠用來文件分片),能夠訪問到字節內部的原始數據塊。總之,file對象包含與FlieList對象,而file對象繼承於Blob對象!他們的關係以下圖:
圖片描述

對於不一樣類型的文件,FileReader 提供不一樣的方法讀取文件。

readAsText(Blob|File, opt_encoding):返回文本字符串。默認狀況下,文本編碼格式是 UTF-8,能夠經過可選的格式參數,指定其餘編碼格式的文本。用此方法咱們能夠讀取文件內容。
readAsDataURL(Blob|File):返回一個基於 Base64 編碼的 data-uri 對象。用此方法咱們能夠作圖片預覽。

圖片本地預覽

咱們知道,img的src屬性或background的url屬性,能夠經過被賦值爲圖片網絡地址或base64的方式顯示圖片。在文件上傳中,咱們通常會先將本地文件上傳到服務器,上傳成功後,由後臺返回圖片的網絡地址再在前端顯示。經過FileReader的readAsDataURL方法,咱們能夠不通過後臺,直接將本地圖片顯示在頁面上。這樣作能夠減小先後端頻繁的交互過程,減小服務器端無用的圖片資源,代碼以下:

let input  = document.getElementById("file");
input.onchange = function(){
    let file = this.files[0];
        if(!!file){
            let reader = new FileReader();
            // 圖片文件轉換爲base64
            reader.readAsDataURL(file);
            reader.onload = function(){
                // 顯示圖片
                document.getElementById("file_img").src = this.result;
        }
    }
}

還有,URL對象也提供了一個把File類型的文件,轉化爲url(數據URL)的方法。
傳入一個 File 對象或者 Blob 對象,能生成一個連接:

let objecturl =  window.URL.createObjectURL(file|blob);

 這個 URL 能夠放置於任何一般能夠放置 URL 的地方,也可用此方法作圖片預覽。

複製粘貼事件

在發佈框裏支持粘貼圖片,可省去用戶截圖保存、再刪除的麻煩。copy、cut、paste這三個事件是一個類型的事件。咱們指望得到剪貼板(clipboard)裏面的圖片,能夠用給頁面中的元素綁定paste事件的方法,當用戶鼠標在該元素上或者該元素處於focus狀態,右鍵粘貼或者ctrl+v的操做都會觸發。
粘貼圖片咱們須要解決下面幾個問題
一、監聽用戶的粘貼操做
二、獲取到剪切板上的數據
三、將獲取到的數據渲染到網頁中

myTextarea.addEventListener("paste", function (e){
   let items = e.clipboardData && e.clipboardData.items || [];
});

 clipboardData對象是一個DataTransfer類型的對象,DataTransfer 是拖動產生的一個對象,但實際上粘貼事件也是它。items的DataTransferItem有兩個屬性kind和type,咱們能夠經過循環取出粘貼板上的數據,而後經過kind來判斷是文件(file)仍是字符串(string),若是kind是file,能夠用getAsFile方法獲取到文件。type屬性則包含的是具體體的數據類型即MIME-Type。

textareaPaste(e){
    let cbd = e.clipboardData;
    for(let i = 0; i < cbd.items.length; i++) {
      let item = cbd.items[i];
      if(item.kind == "file"){
        var blob = item.getAsFile();
        if (blob.size === 0) {
          return;
        }
        this.postImg(blob);
      }
    }
  }

獲取到file文件以後,就能夠進行一些列操做了。

 小結

結合拖放事件API,DataTransfer對象和文件讀取對象FileList等方面的知識,能夠實現拖拽上傳圖文並預覽效果。其實它們能實現的功能遠不止這些,好比拖動排序、大文件分片段點上傳,或者結合後臺處理Word文檔等操做。因爲技術的發展,這些桌面上的功能,均可以在前端實現,咱們須要有一個探索的心。

做者:TNFE 大鵬哥

相關文章
相關標籤/搜索