Open lostvita opened 5 years ago
说明:请使用该Token:1562835370187访问本文Demo,或者点击访问。
本文通过实现(文本/文件)拖动预览功能全面、深入分析整个过程涉及的对象及其API,增强我们对文件类数据的理解和应用。介绍的内容包括:
DataTransfer
File
Blob
DataTransfer对象用来保存在一个拖放操作中被拖动的数据,它可以包含一个或多个数据项,每一个数据项又可以有一种或多种数据类型。
DataTransfer对象的属性分为标准属性和gecko属性。其中,标准属性是所有现代浏览器都支持的,gecko属性则只有gecko内核的浏览器才支持。相应地,DataTransfer的方法也有标准方法和gecko方法之分。
gecko
关于DataTransfer对象的Gecko属性和方法,移步这里了解。
用户在拖动元素或者文本时,每隔几百毫秒就会触发拖动事件。拖动事件类型有:
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对象上,则对整个文档页面的拖动事件生效;但在实际应用中,我们更多是劫持某个输入区域的拖放操作,譬如:
document
$inputBox.addEventListener'drop', evt) => { evt.preventDefault() console.log('drop') }, { capture: false, passive: false })
特别地,拖动事件有严格的触发次序: 假定有一个拖放区域,拖动外部元素/文本到该区域,可能触发的拖动事件次序有如下两种类型: 或是
DataTransfer对象提供了dropEffect和effectAllowed两个属性,允许我们自定义拖动过程中鼠标的类型,其中,dropEffect的值受effectAllowed制约,只能设置effectAllowed允许设置的值。
dropEffect
effectAllowed
effectAllowed只能在dragstart事件中设置,在其他事件中设置不会生效。不同取值代表的含义:
dragstart
document.addEventListener('dragstart', (e) => { e.dataTransfer.effectAllowed = 'none' }, { capture: false, passive: false })
如上设置后,文档上的元素/文本将被禁止拖放。但仍然可以触发除drop以外的一系列的拖动事件。
drop
dropEffect的取值受effectAllowed的制约,只允许被设置effectAllowed指定的值。可能的取值有:move、copy、link、none。dropEffect一般在dragenter和dragover事件中设置,在其他事件中设置也不会生效。
move
copy
link
none
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数据转换为对应类型的数据(字符串、文件等)的一系列方法。如下表:
dataTransferItem
在一个拖动操作中,可能包含了多项数据(比如同时拖动了多个文件),这个时候就需要一个列表去描述这一组拖动数据,这个列表,就是DataTransferItemList对象,这是一个类数组的对象(有一个length属性,只读,还有add()、remove()和clear()等API)。
DataTransferItemList
length
add()
remove()
clear()
对拖动数据进行劫持一般在dragstart和drop阶段处理,因为这两个阶段的事件都是一次性,不会多次触发,不会造成性能问题。例如,用setData设置固定的文案去覆盖被拖动的文本内容:
setData
document.addEventListener('dragstart', e => { e.dataTransfer.setData('text/plain', '期望覆盖的文案') }, { capture: false, passive: false })
在drop阶段我们可以截取到拖动数据,然后做二次处理。但对于不同类型的数据截取,会有一些差异。
对于字符串类型数据,最简单的是通过getData方法获取(该方法无法获取文件类数据)。例如,获取拖动的纯文本内容:
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对象获取,稍后再介绍。
DOM
DataTransfer对象有一个files属性,用于存储拖动文件的列表数据。数据项就是一个File对象数据,可以直接取出。
files
$Ele.addEventListener('drop', (e) => { e.preventDefault() const files = e.dataTransfer.files || [] if(files.length) { // do something... } }, false)
在前面dataTransfer对象的标准属性中介绍过,items属性的值是一个DataTransferItemList对象,通过for循环从里面逐个取出数据,并判断dataTransferItem的kind属性值作不同的操作:
dataTransfer
items
for
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里面主要使用的属性有:
getAsFile
image/png
Blob是一个不可变、表示原始数据的类文件对象,拥有type和size两个只读属性,可以通过FileReader和Response读取Blob里面的数据。譬如,一个图片文件,被FileReader以不同的数据格式读取:
type
size
FileReader
以Data URL格式读取
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数据一般用在图片预览上:
data:[MIME type];
以ArrayBuffer格式读取,返回一个ArrayBuffer对象 读取的ArrayBuffer数据一般用于图片的二次处理,譬如转换jepg格式图片为png。
ArrayBuffer
以Text格式读取,默认以utf-8的编码格式读取文本内容(图片这么读下去,会乱码的)
utf-8
现在,根据截取的数据,我们需要继续实现:如果是图片,则对其进行预览。预览的方式有Data URL和Blob URL两种。 Data URL的方式在File&Blob概述章节已经介绍了,使用FileReader.readAsDataURL()方法即可。
FileReader.readAsDataURL()
URL对象可以用来构造、解析和编码url,它提供了一个静态方法createObjectURL(),方法的参数仅限于File、Blob和MediaSource这三种类型的对象,用于将类文件对象转化成URL字符串(形如:blob:http://localhost:8080/c61a0313-2e45-4adc-a40e-fbbffd4c84ba),该字符串指向对应(文件)对象的引用。
URL
createObjectURL()
MediaSource
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
从规范和内存管理的角度讲,每一次执行createObjectURL,我们需要手动释放内存(但实际应用中影响并不大):
// 在文件加载就绪之后卸载URL实例的引用 img.onload = function() { URL.revokeObjectURL(this.src); }
base64
最后,
参考:
前言
本文通过实现(文本/文件)拖动预览功能全面、深入分析整个过程涉及的对象及其API,增强我们对文件类数据的理解和应用。介绍的内容包括:
DataTransfer
对象介绍及API应用File
和Blob
对象介绍DataTransfer对象
DataTransfer
对象用来保存在一个拖放操作中被拖动的数据,它可以包含一个或多个数据项,每一个数据项又可以有一种或多种数据类型。DataTransfer
对象的属性分为标准属性和gecko属性。其中,标准属性是所有现代浏览器都支持的,gecko
属性则只有gecko
内核的浏览器才支持。相应地,DataTransfer
的方法也有标准方法和gecko
方法之分。DataTransfer的标准属性
DataTransfer的标准方法
关于DataTransfer对象的Gecko属性和方法,移步这里了解。
拖动事件
用户在拖动元素或者文本时,每隔几百毫秒就会触发拖动事件。拖动事件类型有:
拖动事件是绑定在元素身上的,上述示例代码绑定在了
document
对象上,则对整个文档页面的拖动事件生效;但在实际应用中,我们更多是劫持某个输入区域的拖放操作,譬如:特别地,拖动事件有严格的触发次序:
假定有一个拖放区域,拖动外部元素/文本到该区域,可能触发的拖动事件次序有如下两种类型:
或是
![](https://user-gold-cdn.xitu.io/2019/7/16/16bfa5bc7b95cd29?w=590&h=143&f=png&s=9343)
拖动效果
DataTransfer
对象提供了dropEffect
和effectAllowed
两个属性,允许我们自定义拖动过程中鼠标的类型,其中,dropEffect
的值受effectAllowed
制约,只能设置effectAllowed
允许设置的值。effectAllowed
effectAllowed
只能在dragstart
事件中设置,在其他事件中设置不会生效。不同取值代表的含义:如上设置后,文档上的元素/文本将被禁止拖放。但仍然可以触发除
drop
以外的一系列的拖动事件。dropEffect
dropEffect
的取值受effectAllowed
的制约,只允许被设置effectAllowed
指定的值。可能的取值有:move
、copy
、link
、none
。dropEffect
一般在dragenter
和dragover
事件中设置,在其他事件中设置也不会生效。点击这里玩一玩:拖动操作鼠标类型demo
setDragImage
通过该API可以自定义拖动操作中鼠标的背景图片,这个方法必须在
dragstart
中调用:如下图,将文本拖动到输入框,在鼠标底下会紧跟一张背景图片:![](https://user-gold-cdn.xitu.io/2019/7/16/16bfa62899858ce3?w=635&h=311&f=png&s=60587)
dataTransferItem对象
在介绍截取拖动数据之前,有必要先了解下
dataTransferItem
对象。dataTransferItem
对象表示一个拖动数据项,比如拖动的文本、图片等数据。它有两个用于描述数据类型的只读属性,还有用于将dataTransferItem
数据转换为对应类型的数据(字符串、文件等)的一系列方法。如下表:在一个拖动操作中,可能包含了多项数据(比如同时拖动了多个文件),这个时候就需要一个列表去描述这一组拖动数据,这个列表,就是
DataTransferItemList
对象,这是一个类数组的对象(有一个length
属性,只读,还有add()
、remove()
和clear()
等API)。截取拖动数据
对拖动数据进行劫持一般在
dragstart
和drop
阶段处理,因为这两个阶段的事件都是一次性,不会多次触发,不会造成性能问题。例如,用setData
设置固定的文案去覆盖被拖动的文本内容:在
drop
阶段我们可以截取到拖动数据,然后做二次处理。但对于不同类型的数据截取,会有一些差异。字符串类型数据
对于字符串类型数据,最简单的是通过
getData
方法获取(该方法无法获取文件类数据)。例如,获取拖动的纯文本内容:这种方式获取的是拖动的纯文本内容:
当然,通过指定字符串的格式,还可以获取富文本内容:
这种方式获取的是拖动文案的
另外一种是通过
DOM
节点及其祖先节点内容:dataTransferItem
对象获取,稍后再介绍。文件类型数据
DataTransfer
对象有一个files
属性,用于存储拖动文件的列表数据。数据项就是一个File
对象数据,可以直接取出。从items属性中截取
在前面
dataTransfer
对象的标准属性中介绍过,items
属性的值是一个DataTransferItemList
对象,通过for
循环从里面逐个取出数据,并判断dataTransferItem
的kind
属性值作不同的操作:通过上面的方式截取到拖动数据之后,我们可以根据需要对文件数据进行预览或发送。
File&Blob概述
根据上文可知,通过
getAsFile
API我们获取到了拖动的文件,返回的是一个File
对象。里面包含了文件类型(image/png
等)、名字和大小等信息。File
对象是特殊类型的Blob
,继承了Blob
的属性和方法,File
里面主要使用的属性有:Blob
是一个不可变、表示原始数据的类文件对象,拥有type
和size
两个只读属性,可以通过FileReader和Response读取Blob
里面的数据。譬如,一个图片文件,被FileReader
以不同的数据格式读取:以Data URL格式读取
返回一串![](https://user-gold-cdn.xitu.io/2019/7/16/16bfaa66a64d7f81?w=1477&h=148&f=png&s=107689)
data:[MIME type];
开头的base64编码串,读取的data URL数据一般用在图片预览上:以
读取的
ArrayBuffer
格式读取,返回一个ArrayBuffer
对象ArrayBuffer
数据一般用于图片的二次处理,譬如转换jepg格式图片为png。以Text格式读取,默认以![](https://user-gold-cdn.xitu.io/2019/7/16/16bfaa7b8b0ea717?w=1362&h=261&f=png&s=199351)
utf-8
的编码格式读取文本内容(图片这么读下去,会乱码的)实现图片预览
现在,根据截取的数据,我们需要继续实现:如果是图片,则对其进行预览。预览的方式有Data URL和Blob URL两种。
Data URL的方式在File&Blob概述章节已经介绍了,使用
FileReader.readAsDataURL()
方法即可。createObjectURL
URL
对象可以用来构造、解析和编码url,它提供了一个静态方法createObjectURL()
,方法的参数仅限于File
、Blob
和MediaSource
这三种类型的对象,用于将类文件对象转化成URL字符串(形如:blob:http://localhost:8080/c61a0313-2e45-4adc-a40e-fbbffd4c84ba
),该字符串指向对应(文件)对象的引用。调用
createObjectURL()
方法有两个特点:在
document
卸载之前,URL
对象实例会一直保持源对象的引用;对同一个对象执行多次
createObjectURL
方法,会生成不同的URL对象实例。从规范和内存管理的角度讲,每一次执行
createObjectURL
,我们需要手动释放内存(但实际应用中影响并不大):Data URL与Blob URL的区别
base64
格式编码的图片内容,Blob URL表示的是(内存中/本地)图片文件的引用(这是本质区别);最后,
参考: