Say it at the front
The file drag-and-drop upload function is now available everywhere, everyone should have used it, so how it is implemented have you ever understood? Today, let's implement this function together and encapsulate a drag-and-drop upload component.
Effect display
Functional implementation
Native JavaScrip implementation
First of all, we should write the page first:
<!DOCTYPE html>
<html lang="en">
<head>
<title>点击或拖拽上传并显示图片</title>
<style>
body {
display: flex;
flex-direction: column;
}
h1 {
text-align: center;
}
#drop-zone {
width: 300px;
height: 200px;
border: 2px dashed #ccc;
margin: 20px auto;
text-align: center;
line-height: 200px;
font-size: 18px;
}
#uploaded-image {
max-width: 300px;
max-height: 300px;
margin: 20px auto;
}
</style>
</head>
<body>
<h1>点击或拖拽上传并显示图片示例</h1>
<div id="drop-zone">点击或拖拽上传图片</div>
<img id="uploaded-image" src="" alt="上传的图片" />
<script>
</script>
</body>
</html>
Let's try to figure out what it looks like to drag and drop an image directly onto the page
We found that dragging an image directly onto the page will open the image on a new page, so we need to prevent this default behavior to allow placement, and the drag and drop behavior of the page will mainly trigger the following events:
Dragents
The dragenter event is triggered when a draggable element or selected text enters a valid placement target.
The target object is a range directly selected by the user (the element that is directly indicated by the user as the placement target), or <body> an element.
dragleave
The dragleave event is triggered when a dragged element or selected text leaves a valid placement target.
This event is non-cancellable.
dragover
The dragover event fires (every few hundred milliseconds) when a draggable element or selected text is dragged into a valid placement target.
This event fires on the placement target.
drop
The drag event is triggered every few hundred milliseconds when the user drags an element or selected text.
Effect realized
Get the upload box and preview box elements
const dropZone = document.getElementById("drop-zone");
const uploadedImage = document.getElementById("uploaded-image");
When the image enters the upload frame, add a gray layer to the upload frame
Listen for the dragenter event of the upload box and change the background color of the upload box when the image moves into the upload box.
dropZone.addEventListener("dragenter", function (event) {
dropZone.style.backgroundColor = "#f7f7f7";
});
Prevent browser default behaviors, such as opening files
Both the dragover and drop events trigger the browser to open a file, and we need to block its default behavior.
dropZone.addEventListener("dragover", function (event) {
event.preventDefault();
});
dropZone.addEventListener("drop", function (event) {
event.preventDefault();
});
Get the dragged and dropped file
Drag release will trigger the drop event, we only need to listen to the drop event to get the list of dragged files event.dataTransfer.files.
dropZone.addEventListener("drop", function (event) {
event.preventDefault();
dropZone.style.backgroundColor = "";
const file = event.dataTransfer.files[0];
handleFile(file);
});
Process the uploaded file and set a preview image for the image
To determine whether the uploaded file is an image, we only need to determine whether the type of the uploaded file starts with image, and if so, we can convert it to a dataUrl file and set the preview.
function handleFile(file) {
if (file && file.type.startsWith("image/")) {
const reader = new FileReader();
reader.onload = function (event) {
uploadedImage.src = event.target.result;
};
reader.readAsDataURL(file);
}
}
Encapsulated as a Vue component
Knowing that native JS implements a drag-and-drop upload function, we can easily wrap it into a Vue component.
Listen for drag events
In Vue, we can listen to the drag events of elements directly through the @dragover, @dragleave, and @drop.
<div
class="upload-area"
@dragover.prevent="handleDragOver"
@dragleave="handleDragLeave"
@drop.prevent="handleDrop"
@click="handleUploadClick"
>
<p v-if="!isDragging" class="tip-text">{{ tipText }}</p>
<p v-else>{{ tipConfirmText }}</p>
</div>
The logic for handling events is actually the same as in native JS.
methods: {
handleDragOver(event) {
event.preventDefault();
this.isDragging = true;
},
handleDragLeave() {
this.isDragging = false;
},
handleDrop(event) {
event.preventDefault();
this.isDragging = false;
const file = event.dataTransfer.files[0];
this.uploadFile(file);
}
}
Get the uploaded file and pass it to the parent component
After getting the uploaded file, we should not process the file directly in the component, just pass it to the parent component for processing.
uploadFile(file) {
this.$emit("uploadFile", file);
}
The full code of the component
The functional logic is basically the same as that of native JavaScrip, and I won't repeat the description here, just look at the code:
<template>
<div class="drag-upload">
<div
class="upload-area"
@dragover.prevent="handleDragOver"
@dragleave="handleDragLeave"
@drop.prevent="handleDrop"
@click="handleUploadClick"
>
<p v-if="!isDragging" class="tip-text">{{ tipText }}</p>
<p v-else>{{ tipConfirmText }}</p>
</div>
<input
type="file"
ref="fileInput"
style="display: none"
@change="handleFileSelected"
/>
</div>
</template>
<script>
export default {
name: "JDragUpload",
props: {
tipText: {
type: String,
default: "将文件拖放到此处或点击上传",
},
tipConfirmText: {
type: String,
default: "释放文件以上传",
},
},
data() {
return {
isDragging: false,
};
},
methods: {
handleDragOver(event) {
event.preventDefault();
this.isDragging = true;
},
handleDragLeave() {
this.isDragging = false;
},
handleDrop(event) {
event.preventDefault();
this.isDragging = false;
const file = event.dataTransfer.files[0];
this.uploadFile(file);
},
handleUploadClick() {
this.$refs.fileInput.click();
},
handleFileSelected() {
const file = this.$refs.fileInput.files[0];
this.uploadFile(file);
},
uploadFile(file) {
this.$emit("uploadFile", file);
},
},
};
</script>
<style lang="less" scoped>
.drag-upload {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
border: 2px dashed #ccc;
.upload-area {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
background-color: #f0f0f0;
cursor: pointer;
.tip-text {
text-align: center;
}
}
}
</style>
Original link: https://juejin.cn/post/7281581471898222655