laitimes

The drag-and-drop upload function is already rotten, don't you know it yet?

author:The front end is small

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

The drag-and-drop upload function is already rotten, don't you know it yet?
The drag-and-drop upload function is already rotten, don't you know it yet?

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>
           
The drag-and-drop upload function is already rotten, don't you know it yet?

Let's try to figure out what it looks like to drag and drop an image directly onto the page

The drag-and-drop upload function is already rotten, don't you know it yet?

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>
           
The drag-and-drop upload function is already rotten, don't you know it yet?
Original link: https://juejin.cn/post/7281581471898222655