文件拖拽上传功能已经烂大街了,你还不会吗?

简介: 文件拖拽上传功能已经烂大街了,你还不会吗?

说在前面

🖼文件拖拽上传功能现在已经随处可见,大家应该都用过了吧,那么它具体是怎么实现的大家有去了解过吗?今天我们一起来实现一下这个功能,并封装一个拖拽上传组件吧。

效果展示

体验地址:http://jyeontu.xyz/jvuewheel/#/JDragUploadView

功能实现

原生JavaScrip实现

首先我们应该先将页面写好:

<!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>

我们试下直接拖拽图片到页面上是什么效果

我们发现直接将图片拖拽到页面上会在新页面打开图片,那么我们需要阻止这一个默认行为以允许放置,页面的拖拽行为主要会触发以下几个事件:

dragenter

dragenter 事件在可拖动的元素或者被选择的文本进入一个有效的放置目标时触发。

目标对象是用户直接选择的范围(由用户直接指示作为放置目标的元素),或者 <body> 元素。

dragleave

dragleave 事件在拖动的元素或选中的文本离开一个有效的放置目标时被触发。

此事件不可取消。

dragover

dragover 事件在可拖动的元素或者被选择的文本被拖进一个有效的放置目标时(每几百毫秒)触发。

该事件在放置目标上触发。

drop

drag 事件在用户拖动元素或选择的文本时,每隔几百毫秒就会被触发一次。

效果实现
获取上传框和预览框元素
const dropZone = document.getElementById("drop-zone");
const uploadedImage = document.getElementById("uploaded-image");
图片进入上传框时,为上传框加一层灰色蒙层

监听上传框的dragenter事件,在图片移动进入上传框时改变上传框的背景颜色。

dropZone.addEventListener("dragenter", function (event) {
    dropZone.style.backgroundColor = "#f7f7f7";
});
图片离开上传框时,去除上传框的灰色蒙层

监听上传框的dragleave事件,在图片移出上传框时去除上传框的背景颜色。

dropZone.addEventListener("dragleave", function (event) {
    dropZone.style.backgroundColor = "";
});
阻止浏览器默认行为,例如打开文件

dragoverdrop事件都会触发浏览器打开文件,我们需要阻止其默认行为。

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});
dropZone.addEventListener("drop", function (event) {
    event.preventDefault();
});
获取拖拽上传的文件

拖拽释放会触发drop事件,我们只需要监听drop事件,获取到拖拽的文件列表event.dataTransfer.files即可。

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();
    dropZone.style.backgroundColor = "";
    const file = event.dataTransfer.files[0];
    handleFile(file);
});
处理上传文件,为图片时设置预览图

判断上传的文件是否为图片,我们只需要判断上传文件的type是否为image开头,是的话我们可以将其转为dataUrl文件并设置预览。

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);
    }
  }

封装为vue组件

知道了原生js实现一个拖拽上传功能之后,我们也能很容易得将其封装成一个vue组件。

监听拖拽事件

在vue中,我们直接通过@dragover@dragleave@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>

事件的处理逻辑和原生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);
    }
}
获取到上传文件并传递给父组件

获取到上传的文件后,我们不应该组件内直接处理文件了,将其传递给父组件进行处理即可。

uploadFile(file) {
    this.$emit("uploadFile", file);
}
组件完整代码

功能逻辑与原生JavaScrip基本相同,这里也不重复描述了,直接看代码吧:

<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>

源码

组件体验及文档地址:http://jyeontu.xyz/jvuewheel/#/JDragUploadView

Gitee源码:https://gitee.com/zheng_yongtao/jyeontu-component-warehouse/tree/master/JYeontuComponentWarehouse/packages/JDragUpload

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

目录
相关文章
|
7月前
|
数据可视化 数据挖掘 数据管理
当钢铁企业装上数据眼,从此告别PPT
当钢铁企业装上数据眼,从此告别PPT
|
设计模式 负载均衡 算法
请查收!一份2023年程序员不得不看的自救提升指南(彩色终极版)
今天摸鱼的时候看到有人36岁在深圳开起了出租车的新闻,而且对方毕业于华南师范大学,曾在大厂当过主管,因为疫情而毕业,至今2年都没能回到主业,因为上有父母,下有孩子,需要养家糊口,不愿跑美团,认为没面子,所以开起了出租车。 这话不得不再次刷新了我的三观,原来开出租车还能瞧不起跑外卖的?不得不想起很多程序员35岁之后只能跑外卖,同样是35岁危机,居然还有鄙视链....
141 0
|
程序员 Android开发 iOS开发
程序员五一修图小贴士
程序员五一修图小贴士
162 0
程序员五一修图小贴士
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(四)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(四)
77 0
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(四)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(三)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(三)
81 0
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(三)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(五)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(五)
81 0
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(五)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(一)
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(一)
106 0
程序人生 - 写完这篇文章后,我被保险公司追杀了几十条街(一)
|
Java Go Spring
Springboot 居然可以设置动态的Banner!!(悄悄滴~)|牛气冲天新年征文
正题开始啦~ 牛年啦~ 咋们也研究下怎么给我们滴 IDEA 添加点 过年滴气氛,像这样 Spring boot 源码 之 Banner 源码看起来 <( ̄︶ ̄)↗[GO!] Springboot 启动时,会先去创建SpringApplication 对象 。 SpringApplication 构造器 指定banner的模式 Banner 函数式接口 这里有三种模式 run 方法 创建banner对象 进入上图红框中,可以看到这里如果不是 OFF 模式的话会去创建 SpringApplicationBannerPrinter 对象 可以看到该类中还定义了 图片的类型(居然还支持 gif
290 0
|
Java 关系型数据库 MySQL
刚入职没多久,连夜手写了一个代码生成器,项目开发速度瞬间屌炸了!(一)
各位网友,大家好,我是阿粉! 最近刚入职一个新团队,还没来得及熟悉业务,甲方爸爸就要求项目要在2个月内完成开发并上线! 本想着往后推迟1个月在交付,但是甲方爸爸不同意,只能赶鸭子上架了! 然后根据业务需求,设计出了大概30多张表,如果这30多张表,全靠开发人员手写 crud,开发所需的时间肯定会大大的延长,甚至可能直接会影响交付时间! 于是就想着,能不能通过代码生成器一键搞定全部的 crud,开发团队只需要根据业务需求编写逻辑代码就可以?
刚入职没多久,连夜手写了一个代码生成器,项目开发速度瞬间屌炸了!(一)

热门文章

最新文章