經過前端代碼,利用File API對圖文發佈操做進行優化,提升用戶體驗。
發佈框是web應用的一種常見圖文發佈功能,在微博、評論、論壇、博客或內容管理系統等產品中常用。作好發佈框的交互設計,能提升用戶的編輯效率,提升用戶體驗,給產品增長錦上添花的效果。javascript
在實際的項目中,遇到了如下需求,用戶(運營人員)能夠經過發佈框發佈話題相關內容,產品經理指望在此發佈框上實現如下功能:
一、用戶能夠拖動文件,當文件進入瀏覽器時提示用戶拖動文件到發佈框;
二、當拖動的文件(例如.exe)不符合要求時,給予拒絕提示,不能上傳;
三、當拖動文件(批量)爲圖片或文檔時,解析圖片和文字,預覽(或上傳),其他類型的文件拒絕;html
四、發佈框支持圖片複製、和QQ、PrintScreen鍵等工具的截圖後粘貼(或ctrl+v)。前端
拖放是 HTML5 中常見的功能。即:把抓取的對象拖放到其餘位置(想一想一下兩個元素換位)。與他相關就是兩個動做——拖和放。因此,它涉及到兩個元素。一個是被拖的元素,稱爲拖放源;另外一個是要放的目標,稱爲拖放目標。所涉及到的事件就是兩類:drag(源)和drop(目標)。
與它相關的兩個事件(按觸發的前後順序,參照物是鼠標指針而非文件邊緣):java
這裏咱們主要用到了投放目標的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()返回的是一個布爾值,來表示傳入的節點是否爲該節點的後代節點。利用這兩個特性,就能夠解決這個問題。後端
任何拖動事件,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、type、size屬性來校驗格式和大小是否知足要求。HTML5給咱們提供了FileReader API 用於讀取文件,即把文件內容讀入內存。它的參數是 File 對象或 Blob 對象。
在前端開發中,最多見的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 大鵬哥