实现文件的拖拽功能主要用到HTML5提供的两个API Drag和Drop。我们先来了解一下API的作用:
拖放事件
关于拖放事件有些是在被拖动元素上触发的,而有些则是在放置目标上触发的
当我们拖动某个元素时,会依次触发:
- ondragstart
- ondrag
- ondragend
这三个事件都是在被拖动元素上触发的。当拖动开始时会先触发dragstart事件,然后在拖动的过程中会持续触发drag事件,当拖动停止时(无论被拖动元素是否放到了有效的放置目标)都会触发dragend事件,这三个事件类似鼠标的移动事件mousestart,mousemove,mouseend
当某个元素被拖动到放置目标上,会依次触发:
- dragenter
- dragover
- dragleave 或 drop
这三个事件都是在放置目标上触发的。当元素进入放置目标时会触发dragenter事件,当元素在放置目标上移动时会持续触发dragover事件,当元素移出放置目标时会触发dragleave事件,当元素被放到了放置目标中会触发drop事件而不是dragleave事件,这几个事件(除drop)也类似鼠标的移动事件mouseenter,mouseover,mouseleave
阻止默认行为。虽然所有的元素都支持drop事件,但是这些元素默认是不允许放置的,这个时候当我们在放置目标上松开鼠标是不会触发drop事件的,我们可以通过event.preventDefault()来阻止默认的行为,如下:
droptarget.ondragenter = event => {
event.preventDefault()
}
droptarget.ondragover = event => {
event.preventDefault()
}
复制代码
另外在一些浏览器中,当我们移动图片到放置目标上,松开的时候会打开这张图片,如果移动的是超链接,则会打开这个页面。我们有时候需要阻止这种默认的行为,可以这样做
droptarget.ondrop = event => {
event.preventDefault()
}
dataTransfer对象
dataTransfer对象用来在拖动的过程中从被拖动元素向放置目标传递数据,这个对象有两个方法setData和getData
setData有两个参数,第一个是MIME类型,第二个则是我们要保存的值
event.dataTransfer.setData('text/plain', 'msg')
event.dataTransfer.setData('text/uri-list', 'http://baidu.com')
复制代码
getData只有一个参数,就是setData中我们传的第一个参数
event.dataTransfer.getData('text/plain')
event.dataTransfer.setData('text/uri-list')
复制代码
setData我们一般在dragstart中去使用,而getData只能在drop事件中去使用,这个务必记住
你拖动一个图片到目标区域,那目标区域怎么获取这个图片的信息呢?就靠它!它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。
SparkDM5
上传图片用图片文件的对象hash哈希值判断图片是否一样,避免重复提交相同的图片到服务器中,这里我们采用SparkMD5来生成文件hash值。
示例
页面:
Document
图片展示区:
将图片拖拽至此处上传
js
const img_arr = [];
// 文件上传
function uploadFile() {
$("#file").click();
}
function upload(files) {
const MAX_COUNT = 3;
const IMG_COUNT = MAX_COUNT - img_arr.length;
const additional_arr = [];
const IMG_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
if (Array.prototype.some.call(files, function(file) {return !IMG_TYPES.includes(file.type)})) {
layer.msg("图片格式不正确!!!")
$("#file").val('');
return;
}
if (files.length > IMG_COUNT) {
layer.msg(`最多只能上传${MAX_COUNT}张图片!`)
$("#file").val('');
return;
}
Array.prototype.forEach.call(files, function(file, i, arr) {
const fileReader = new FileReader();
const sparkMD5 = new SparkMD5();
fileReader.readAsDataURL(file);
fileReader.onload = (event) => {
const binary = event.target.result;
// 生成图片hash
const hash = sparkMD5.appendBinary(binary).end();
// 判断是否存在相同的图片
if (!isExists(hash)) {
additional_arr.push({
hash,
src: binary,
file
});
} else {
// 中断文件流
fileReader.abort();
layer.msg("请不要上传相同的图片!!!");
}
};
fileReader.onloadend = (event) => {
// 读取完成
if (additional_arr.length === arr.length) {
img_arr.push(...additional_arr);
show(additional_arr); // 展示在展览区
$("#file").val('');
}
}
})
}
// 判断是否有相同的图片
function isExists(hash) {
return img_arr.some(function(item) {
return item.hash === hash;
})
}
// 文件change
function fileChange(that, event) {
const files = event.target.files;
upload(files);
}
/** 控制删除icon */
function imgMouseEnter(that) {
$(that).find("span.del-icon").css('display', 'block');
};
function imgMouseLeave(that) {
$(that).find("span.del-icon").css('display', 'none');
}
/** 展示图片 */
function show(additional_arr) {
if (!additional_arr.length) {
return;
}
const img_html = additional_arr.reduce(function(init, item) {
init += `
`
return init;
}, '');
$(".show-area").append(img_html);
}
// 删除
function deleteImg(that) {
const hash = $(that).attr("hash");
const $imgBox = $(that).parents(".img-box");
$imgBox.remove();
img_arr.splice(img_arr.findIndex(function(item) {
return item.hash === hash;
}), 1);
}
// 获取元素位置
function getElementLeft(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
function getElementTop(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null){
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
/** =================== drag ================= */
function imgDrag(that, event) {
var e = event || window.event;
e.preventDefault()
e.stopPropagation()
}
function dragUpload(that, event) {
var e = event || window.event;
e.preventDefault()
e.stopPropagation()
const files = e.dataTransfer.files;
upload(files);
}
// 拖拽删除
function imgDragEnd(that, event) {
const hash = $(that).attr("hash");
const $showArea = $(".show-area");
const $imgBox = $(that).parents(".img-box");
const width = $showArea[0].clientWidth;
const height = $showArea[0].clientHeight;
const origin = {
x: getElementLeft($showArea[0]),
y: getElementTop($showArea[0])
};
const target = {
x: event.clientX,
y: event.clientY
}
if (target.x - origin.x > width || target.x < origin.x || target.y - origin.y > height || target.y < origin.y) {
$imgBox.remove();
img_arr.splice(img_arr.findIndex(function(item) {
return item.hash === hash;
}), 1);
}
$showArea.css("border", "1px dashed #c3c3c3");
}
function imgDragOver(that, event) {
const $showArea = $(that).parents(".show-area");
$showArea.css("border", "1px solid #0065ff");
}
css
html, body {
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#drag-show {
padding-top: 40px;
line-height: 60px;
text-align: center;
}
.show-area {
display: inline-block;
height: 60px;
line-height: 60px;
width: 180px;
border: 1px dashed #c3c3c3;
border-radius: 5px;
vertical-align: middle;
}
.title {
display: inline-block;
line-height: 60px;
color: #555;
font-size: 20px;
vertical-align: middle;
}
.img-box {
position: relative;
display: inline-block;
width: 50px;
height: 50px;
line-height: 50px;
margin-right: 8px;
vertical-align: middle;
cursor: pointer;
}
.img-span {
display: inline-block;
width: 100%;
height: 100%;
vertical-align: middle;
overflow: hidden;
}
.img-item {
display: inline-block;
width: 100%;
vertical-align: middle;
}
.del-icon {
position: absolute;
top: -7px;
right: -7px;
width: 14px;
height: 14px;
background-image: url('./del.png');
background-size: 100% 100%;
cursor: pointer;
}
#drag-area {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
left: 50%;
width: 300px;
height: 200px;
margin-left: -150px;
margin-top: 20px;
color: #555;
font-size: 20px;
border-radius: 12px;
border: 1px dashed #c3c3c3;
}
.upload-box {
display: inline-block;
width: auto;
height: auto;
}
#upload {
display: inline-block;
width: 50px;
height: 50px;
padding: 8px;
margin-top: 10px;
box-sizing: border-box;
border-radius: 5px;
border: 1px dashed #c3c3c3;
background-size: 100% 100%;
background-image: url('./plus.png');
cursor: pointer;
}
#upload:hover {
background-color: #fafafa;
}
效果: