說明:請使用該Token:1562835370187訪問本文Demo,或者點擊訪問。javascript
本文經過實現(文本/文件)拖動預覽功能全面、深刻分析整個過程涉及的對象及其API,加強咱們對文件類數據的理解和應用。介紹的內容包括:html
DataTransfer
對象介紹及API應用File
和Blob
對象介紹DataTransfer
對象用來保存在一個拖放操做中被拖動的數據,它能夠包含一個或多個數據項,每個數據項又能夠有一種或多種數據類型。java
DataTransfer
對象的屬性分爲標準屬性和gecko屬性。其中,標準屬性是全部現代瀏覽器都支持的,gecko
屬性則只有gecko
內核的瀏覽器才支持。相應地,DataTransfer
的方法也有標準方法和gecko
方法之分。git
屬性名 | 取值 | 備註 |
---|---|---|
dropEffect | none, copy, link, move | 獲取/設置當前的拖放操做類型 |
effectAllowed | copyLink, copyMove, link, linkMove, move, all or uninitialized(默認值) | 獲取/設置容許的全部拖動操做類型 |
files | Arrray | 被拖動的文件信息列表 |
items | DataTransferItemList對象 | 只讀,被拖動的數據列表 |
types | Arrray | 只讀,在dragStart事件中設置(經過setData API)的數據格式的列表 |
方法名 | 參數 | 備註 |
---|---|---|
clearData([format]) | forma: [可選]數據類型 | 清空全部指定類型的拖動數據 |
getData(format) | format: [必須]數據類型 | 獲取指定類型的拖動數據,無數據則返回空字符串 |
setData(format, data) | format: [必須]數據類型,data: [必須]添加到拖動對象中的數據 | 設置給定類型的數據。若是該類型的數據不存在,則添加在列表末尾;若是存在,則替換。 |
setDragImage(img, xOffset, yOffset) | img: [必須]圖像元素;xOffset: [必須]X軸偏移值;yOffset: [必須]Y軸偏移值 | 自定義拖動圖像 |
關於DataTransfer對象的Gecko屬性和方法,移步這裏瞭解。angularjs
用戶在拖動元素或者文本時,每隔幾百毫秒就會觸發拖動事件。拖動事件類型有:github
document.addEventListener('dragstart', evt => {
console.log('dragstart');
});
document.addEventListener('dragenter', evt => {
console.log('dragenter');
});
document.addEventListener('dragleave', evt => {
console.log('dragleave');
});
document.addEventListener('dragover', evt => {
event.preventDefault();
console.log('dragover');
});
document.addEventListener('drop', evt => {
console.log('drop');
});
document.addEventListener('dragend', evt => {
console.log('dragend');
});
複製代碼
拖動事件是綁定在元素身上的,上述示例代碼綁定在了document
對象上,則對整個文檔頁面的拖動事件生效;但在實際應用中,咱們更可能是劫持某個輸入區域的拖放操做,譬如:web
$inputBox.addEventListener'drop', evt) => {
evt.preventDefault()
console.log('drop')
}, {
capture: false,
passive: false
})
複製代碼
特別地,拖動事件有嚴格的觸發次序: 數組
假定有一個拖放區域,拖動外部元素/文本到該區域,可能觸發的拖動事件次序有以下兩種類型: 或是DataTransfer
對象提供了dropEffect
和effectAllowed
兩個屬性,容許咱們自定義拖動過程當中鼠標的類型,其中,dropEffect
的值受effectAllowed
制約,只能設置effectAllowed
容許設置的值。瀏覽器
effectAllowed
只能在dragstart
事件中設置,在其餘事件中設置不會生效。不一樣取值表明的含義:異步
document.addEventListener('dragstart', (e) => {
e.dataTransfer.effectAllowed = 'none'
}, {
capture: false,
passive: false
})
複製代碼
如上設置後,文檔上的元素/文本將被禁止拖放。但仍然能夠觸發除drop
之外的一系列的拖動事件。
dropEffect
的取值受effectAllowed
的制約,只容許被設置effectAllowed
指定的值。可能的取值有:move
、copy
、link
、none
。dropEffect
通常在dragenter
和dragover
事件中設置,在其餘事件中設置也不會生效。
$Ele.addEventListener('dragover', (e) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move|move|copy|link'
}, {
capture: false,
passive: false
})
複製代碼
點擊這裏玩一玩:拖動操做鼠標類型demo
經過該API能夠自定義拖動操做中鼠標的背景圖片,這個方法必須在dragstart
中調用:
const img = new Image()
img.src = './bg.png'
document.addEventListener('dragstart', (e) => {
e.dataTransfer.setDragImage(img, 5, 5)
}, {
capture: false,
passive: false
})
複製代碼
以下圖,將文本拖動到輸入框,在鼠標底下會緊跟一張背景圖片:
在介紹截取拖動數據以前,有必要先了解下dataTransferItem
對象。dataTransferItem
對象表示一個拖動數據項,好比拖動的文本、圖片等數據。它有兩個用於描述數據類型的只讀屬性,還有用於將dataTransferItem
數據轉換爲對應類型的數據(字符串、文件等)的一系列方法。以下表:
屬性/方法 | 參數 | 描述 |
---|---|---|
kind | 只讀 | 拖動項數據的性質:string/file |
type | 只讀 | 拖動項數據的MIME類型:image/png等 |
getAsFile() | / | 以文件的形式讀取拖動項數據,返回一個File對象,非file性質的數據則返回null。 |
getAsString(call) | call:(str) => str | 以字符串的形式讀取拖動數據,在回調函數中獲取字符串數據。 |
在一個拖動操做中,可能包含了多項數據(好比同時拖動了多個文件),這個時候就須要一個列表去描述這一組拖動數據,這個列表,就是DataTransferItemList
對象,這是一個類數組的對象(有一個length
屬性,只讀,還有add()
、remove()
和clear()
等API)。
對拖動數據進行劫持通常在dragstart
和drop
階段處理,由於這兩個階段的事件都是一次性,不會屢次觸發,不會形成性能問題。例如,用setData
設置固定的文案去覆蓋被拖動的文本內容:
document.addEventListener('dragstart', e => {
e.dataTransfer.setData('text/plain', '指望覆蓋的文案')
}, {
capture: false,
passive: false
})
複製代碼
在drop
階段咱們能夠截取到拖動數據,而後作二次處理。但對於不一樣類型的數據截取,會有一些差別。
對於字符串類型數據,最簡單的是經過getData
方法獲取(該方法沒法獲取文件類數據)。例如,獲取拖動的純文本內容:
$Ele.addEventListener('drop', (e) => {
e.preventDefault()
console.log(e.dataTransfer.getData('text'))
}, {
capture: false,
passive: false
})
複製代碼
這種方式獲取的是拖動的純文本內容:
固然,經過指定字符串的格式,還能夠獲取富文本內容:console.log(e.dataTransfer.getData('text/plain'))
複製代碼
這種方式獲取的是拖動文案的DOM
節點及其祖先節點內容:
dataTransferItem
對象獲取,稍後再介紹。
DataTransfer
對象有一個files
屬性,用於存儲拖動文件的列表數據。數據項就是一個File
對象數據,能夠直接取出。
$Ele.addEventListener('drop', (e) => {
e.preventDefault()
const files = e.dataTransfer.files || []
if(files.length) {
// do something...
}
}, false)
複製代碼
在前面dataTransfer
對象的標準屬性中介紹過,items
屬性的值是一個DataTransferItemList
對象,經過for
循環從裏面逐個取出數據,並判斷dataTransferItem
的kind
屬性值做不一樣的操做:
const transferItems = e.dataTransfer.items
for(let i = 0; i < transferItems.length; i++) {
const item = transferItems[i]
if(item.kind === 'string') { // 處理字符串數據
item.getAsString(str => console.log(str))
} else if (item.kind === 'file') { // 處理文件數據
const file = item.getAsFile()
// ...
}
}
複製代碼
經過上面的方式截取到拖動數據以後,咱們能夠根據須要對文件數據進行預覽或發送。
根據上文可知,經過getAsFile
API咱們獲取到了拖動的文件,返回的是一個File
對象。裏面包含了文件類型(image/png
等)、名字和大小等信息。File
對象是特殊類型的Blob
,繼承了Blob
的屬性和方法,File
裏面主要使用的屬性有:
屬性 | 備註 |
---|---|
name | 文件名字 |
size | 文件大小 |
type | 文件MIME類型 |
lastModified | 文件的最後修改時間 |
Blob
是一個不可變、表示原始數據的類文件對象,擁有type
和size
兩個只讀屬性,能夠經過FileReader和Response讀取Blob
裏面的數據。譬如,一個圖片文件,被FileReader
以不一樣的數據格式讀取:
const reader = new FileReader()
reader.addEventListener("load", () => {
console.log(reader.result)
}, false)
reader.readAsDataURL(file)
// reader.readAsArrayBuffer(file)
// reader.readAsText(file[, encoding])
複製代碼
返回一串data:[MIME type];
開頭的base64編碼串,讀取的data URL數據通常用在圖片預覽上:
ArrayBuffer
格式讀取,返回一個ArrayBuffer
對象
讀取的ArrayBuffer
數據通常用於圖片的二次處理,譬如轉換jepg格式圖片爲png。utf-8
的編碼格式讀取文本內容(圖片這麼讀下去,會亂碼的)
如今,根據截取的數據,咱們須要繼續實現:若是是圖片,則對其進行預覽。預覽的方式有Data URL和Blob URL兩種。
Data URL的方式在 File&Blob概述章節已經介紹了,使用FileReader.readAsDataURL()
方法便可。
URL
對象能夠用來構造、解析和編碼url,它提供了一個靜態方法createObjectURL()
,方法的參數僅限於File
、Blob
和MediaSource
這三種類型的對象,用於將類文件對象轉化成URL字符串(形如:blob:http://localhost:8080/c61a0313-2e45-4adc-a40e-fbbffd4c84ba
),該字符串指向對應(文件)對象的引用。
// 前面咱們經過截取拿到了file對象數據
const { name, size } = file
const url = URL.createObjectURL(file)
this.previewObj = {
name,
size,
src: url
}
複製代碼
調用createObjectURL()
方法有兩個特色:
document
卸載以前,URL
對象實例會一直保持源對象的引用;createObjectURL
方法,會生成不一樣的URL對象實例。從規範和內存管理的角度講,每一次執行createObjectURL
,咱們須要手動釋放內存(但實際應用中影響並不大):
// 在文件加載就緒以後卸載URL實例的引用
img.onload = function() {
URL.revokeObjectURL(this.src);
}
複製代碼
base64
格式編碼的圖片內容,Blob URL表示的是(內存中/本地)圖片文件的引用(這是本質區別);最後,
參考: