说明:请使用该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两种。
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表示的是(内存中/本地)图片文件的引用(这是本质区别);最后,
参考: