基于Vue3的全屏拖拽上传组件

本文主要介绍了基于Vue3的全屏拖拽上传组件,分享给大家,具体如下:

知识点

  • 浏览器拖拽 api
  • fetch 请求
  • vue3

说来话长,长话短说,关于 html5 的拖拽 api 也只是做过一些拖拽排序的例子.其实思路上与其他拖拽上传组件基本一样,都是指定一个区域可拖拽,然后读取文件在上传
先说说拖拽 api,这个是 html5 新增的一个 api,给一个元素设置 draggable = true 属性时,该元素就会支持拖拽
拖拽元素事件如下

1. ondrag 当拖动元素的时候运行脚本
2. ondragstart 当拖动操作开始时候运行脚本
3. ondragend 当拖动操作结束的时候运行脚本

目标元素的事件如下:
1. ondragover 当元素被拖动至有效拖放目标上方时执行脚本
2. ondragenter 当元素被拖动至有效拖动目标时执行脚本
3. ondragleave 当元素离开至有效拖放目标是运行脚本
4. ondrop 当被拖动元素正在被放下的时候运行脚本

比如我们想监听 body 的拖拽:

const ele = document.querySelector('body')
ele.addEventListener('dragenter', (e) => {
  // do something
})

而当我们想要阻止默认事件的时候我们可以用 e.preventDefault()

组件

先看一下效果,此时我这里是设置的仅能上传 png 与 jpg

使用:

    <upload
      accept=".jpg,.png,.ico" // 设置文件类型
      @onChange="change" // 文件上传事件
      action="http://localhost:3001/upload" // 上传地址
      :header="header" // 上传的header
      autoUpload // 是否自动上传
      name="file"// 上传的字段名
      @onSuccess="onSuccess"  // 上传成功回调
    ></upload>

最开始的时候我想获取拖拽元素的时候莫名发现尽管加了监听事件,可还是会打开新的窗口去预览文件,所以我们第一步就是先把默认事件都给禁用掉

// 禁用默认拖拽事件
function disableDefaultEvents() {
  const doc = document.documentElement
  doc.addEventListener('dragleave', (e) => e.preventDefault()) //拖离
  doc.addEventListener('drop', (e) => e.preventDefault()) //拖后放
  doc.addEventListener('dragenter', (e) => e.preventDefault()) //拖进
  doc.addEventListener('dragover', (e) => e.preventDefault()) //拖来拖去
}

直接获取根元素,阻止拖拽的默认事件

第二步就是我们给 body 或是其他元素加上我们想要监听的事件,这里有一个注意的是 body 的高度一定是窗口的高度,这样才会全屏拖拽,在拖离的时候我们还要判断一下文件是否被拖出区域

这里一共有这么判断,

e.target.nodeName === 'HTML',这个用来判断根元素是不是 html
e.target === e.explicitOriginalTarget 这个是火狐特有的一个 api,判断这两个触发事件的目标是否一致
(!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
e.clientY >= window.innerHeight))

这个是用来判断鼠标当前的位置的,是否还在区域内

// 初始化拖入事件
function init() {
    // 获取body元素
  const ele = document.querySelector('body')
  // 添加事件
  //拖后放
  ele.addEventListener('dragenter', () => {
    show.value = true
  })
  // 这里判断鼠标拖离
  ele.addEventListener('dragleave', (e) => {
    if (
      e.target.nodeName === 'HTML' ||
      e.target === e.explicitOriginalTarget ||
      (!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
          e.clientY >= window.innerHeight))
    ) {
      show.value = false
    }
  })
  //拖进
  ele.addEventListener('drop', (e) => {
    show.value = false
    e.preventDefault()
    onDrop(e) // 拖入处理文件的方法
  })
}

第三步是处理拖入的文件,此时 accept 是我们定义的文件类型,此时我们用e.dataTransfer.files这个属性可以获得拖入的文件,
然后我们把拖入的文件用 filter 做一个过滤,只保留我们需要的文件类型

checkType(file,accept)就是用来判断文件类型的,这一个函数是借鉴了 element ui 里面的上传组件的筛选,当时我也是写蒙了我 😂

// 检查文件类型
function checkType(file, accept = '') {
  const { type, name } = file
  if (accept.length === 0) return true
  const extension = name.indexOf('.') > -1 ? `.${name.split('.').pop()}` : ''
  const baseType = type.replace(/\/.*$/, '')
  return accept
    .split(',')
    .map((type) => type.trim())
    .filter((type) => type)
    .some((acceptedType) => {
      if (/\..+$/.test(acceptedType)) {
        return extension === acceptedType
      }
      if (/\/\*$/.test(acceptedType)) {
        return baseType === acceptedType.replace(/\/\*$/, '')
      }
      if (/^[^/]+\/[^/]+$/.test(acceptedType)) {
        return type === acceptedType
      }
    })
}

这个方法是文件拖入之后的处理,当我们获得需要的文件之后就是根据autoUpload来判断一下是否上传

function onDrop(e) {
  const accept = props.accept
  const list = [].slice.call(e.dataTransfer.files).filter((file) => {
    if (accept) {
      return checkType(file, accept)
    }
    return true
  })
  fileList = list.map((p) => {
    return handleStart(p)
  })
  // 触发事件
  onChange()
  if (props.autoUpload) {
    if (props.action === '') {
      onError()
      throw 'need action'
      return
    }
    list.forEach((file) => {
      post(file) // 上传文件
    })
  }
}

源码如下:

<template>
  <div class="mask" v-show="show" id="mask">
    <h3>拖拽到这里上传</h3>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
// import ajax from './ajax'
const props = defineProps({
  name: String, // 上传的字段名
  header: { Object, Number, String }, // 上传的文件头
  // 验证的文件类型,有值的时候只会拖入所有的文件只会保留设置过滤后的文件
  accept: {
    type: String,
    default: '',
  },
  // 是否开启自动上传
  autoUpload: {
    type: Boolean,
    default: false,
  },
  // 上传地址
  action: {
    type: String,
    default: '#',
  },
})

const emit = defineEmits(['onError', 'onProgress', 'onSuccess', 'onChange']) // 默认emit事件
let show = ref(false) // 是否展示遮罩
let fileList = reactive([]) // 文件列表
let tempIndex = 0 // 做一个标记
onMounted(() => {
  disableDefaultEvents()
  init()
})
// 初始化拖入事件
function init() {
  const ele = document.querySelector('body')
  ele.addEventListener('dragenter', () => {
    show.value = true
  }) //拖后放
  ele.addEventListener('dragleave', (e) => {
    if (
      e.target.nodeName === 'HTML' ||
      e.target === e.explicitOriginalTarget ||
      (!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
          e.clientY >= window.innerHeight))
    ) {
      show.value = false
    }
  }) //拖离
  ele.addEventListener('drop', (e) => {
    show.value = false
    e.preventDefault()
    onDrop(e)
  }) //拖进
}
// 禁用默认拖拽事件
function disableDefaultEvents() {
  const doc = document.documentElement
  doc.addEventListener('dragleave', (e) => e.preventDefault()) //拖离
  doc.addEventListener('drop', (e) => e.preventDefault()) //拖后放
  doc.addEventListener('dragenter', (e) => e.preventDefault()) //拖进
  doc.addEventListener('dragover', (e) => e.preventDefault()) //拖来拖去
}
// 拖入时的事件
function onDrop(e) {
  const accept = props.accept
  const list = [].slice.call(e.dataTransfer.files).filter((file) => {
    if (accept) {
      return checkType(file, accept)
    }
    return true
  })
  fileList = list.map((p) => {
    return handleStart(p)
  })
  onChange()
  if (props.autoUpload) {
    if (props.action === '') {
      onError()
      throw 'need action'
      return
    }
    list.forEach((file) => {
      post(file)
    })
  }
}
// 检查文件类型
function checkType(file, accept = '') {
  const { type, name } = file
  if (accept.length === 0) return true
  const extension = name.indexOf('.') > -1 ? `.${name.split('.').pop()}` : ''
  const baseType = type.replace(/\/.*$/, '')
  return accept
    .split(',')
    .map((type) => type.trim())
    .filter((type) => type)
    .some((acceptedType) => {
      if (/\..+$/.test(acceptedType)) {
        return extension === acceptedType
      }
      if (/\/\*$/.test(acceptedType)) {
        return baseType === acceptedType.replace(/\/\*$/, '')
      }
      if (/^[^/]+\/[^/]+$/.test(acceptedType)) {
        return type === acceptedType
      }
    })
}
// 处理文件列表返回值
function handleStart(rawFile) {
  rawFile.uid = Date.now() + tempIndex++
  return {
    status: 'ready',
    name: rawFile.name,
    size: rawFile.size,
    percentage: 0,
    uid: rawFile.uid,
    raw: rawFile,
  }
}
// 上传的事件
function post(rawFile) {
  const options = {
    headers: props.header,
    file: rawFile,
    data: props.data || '',
    filename: props.name || 'file',
    action: props.action,
  }
  upload(options)
    .then((res) => {
      res.json()
    })
    .then((json) => {
      onSuccess(json, rawFile)
    })
    .catch((err) => {
      onError(err, rawFile)
    })
}
// 文件上传方法
function upload(option) {
  const action = option.action

  const formData = new FormData()

  if (option.data) {
    Object.keys(option.data).forEach((key) => {
      formData.append(key, option.data[key])
    })
  }
  formData.append(option.filename, option.file, option.file.name)

  const headers = new Headers()
  for (let item in headers) {
    if (headers.hasOwnProperty(item) && headers[item] !== null) {
      headers.append(i, option.headers[i])
    }
  }
  return fetch(action, {
    mode: 'no-cors',
    body: formData,
    headers: headers,
    method: 'post',
  })
}

// 拖拽进去获取文件列表的事件
function onChange() {
  emit('onChange', fileList)
}
// 上传中的事件
function onProgress(e, file) {
  emit('onProgress', e, file, fileList)
}
// 上传成功事件
function onSuccess(res, file) {
  emit('onProgress', res, file, fileList)
}
// 上传失败事件
function onError() {
  emit('onError')
}
</script>
<style scoped>
.mask {
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  position: fixed;
  z-index: 9999;
  opacity: 0.6;
  text-align: center;
  background: #000;
}
h3 {
  margin: -0.5em 0 0;
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
  font-size: 40px;
  color: #fff;
  padding: 0;
}
</style>

到此这篇关于基于Vue3的全屏拖拽上传组件的文章就介绍到这了,更多相关Vue3 全屏拖拽上传内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能

    用Vue2实现移动端图片上传.压缩.拖拽排序.拖拽删除功能 图片上传图片压缩拖拽排序.拖拽删除 之前在公司开发过一段时间的移动端H5页面,有个功能就是要上传图片+压缩.参考了一下网上的方法,外加自己摸索的过程,最终实现了这个功能.后面在家闲的时候又加多了个长按选中图片,并且可以拖拽排序.拖拽到指定位置删除的功能. github地址:代码地址 下面直接进入正题: 图片上传 图片上传用的是HTML的input标签实现的.核心就是把获取到的文件通过FileReader转换成图片,代码如下: <inpu

  • vue实现拖拽或点击上传图片

    本文实例为大家分享了vue实现拖拽或点击上传图片的具体代码,供大家参考,具体内容如下 一.预览图 二.实现 点击上传思路:将input的type设置为"file"类型即可上传文件.隐藏该input框,同时点击按钮时,调取该input的点击上传功能.剩下的就是css优化页面了. 拖拽上传思路:通过给拖拽框dropbox绑定拖拽事件,当组件销毁时解绑事件.在拖拽结束,通过event.dataTransfer.files获取上传的文件信息.然后在对文件进行上传服务器操作. 接下来请允许我简单

  • vue+flask实现视频合成功能(拖拽上传)

    vue+flask实现视频合成 效果如下 拖拽上传我们之前一个文章有写过 //www.jb51.net/article/206543.htm 原理就是 监听drop事件 来获取拖拽的文件列表 上传文件 通过axios 上传文件 this,.fileList就是我们的文件列表 let files = this.fileList; let formd = new FormData(); let i = 1; //添加上传列表 files.forEach(item => { formd.append(

  • 基于Vue3的全屏拖拽上传组件

    本文主要介绍了基于Vue3的全屏拖拽上传组件,分享给大家,具体如下: 知识点 浏览器拖拽 api fetch 请求 vue3 说来话长,长话短说,关于 html5 的拖拽 api 也只是做过一些拖拽排序的例子.其实思路上与其他拖拽上传组件基本一样,都是指定一个区域可拖拽,然后读取文件在上传 先说说拖拽 api,这个是 html5 新增的一个 api,给一个元素设置 draggable = true 属性时,该元素就会支持拖拽 拖拽元素事件如下 1. ondrag 当拖动元素的时候运行脚本 2.

  • Nodejs+express+html5 实现拖拽上传

    一.前言 文件上传是一个比较常见的功能,传统的选择方式的上传比较麻烦,需要先点击上传按钮,然后再找到文件的路径,然后上传.给用户体验带来很大问题.html5开始支持拖拽上传的需要的api.nodejs也是一个最近越来越流行的技术,这也是自己第一次接触nodejs,在nodejs开发中,最常用的开发框架之一是expess,它是一个类似mvc模式的框架.结合html5.nodejs express实现了拖拽上传的功能. 二.基础知识普及 1.NodeJs基础知识 nodejs简单来说就是一个可以让j

  • HTML5附件拖拽上传drop & google.gears实现代码

    腾讯微博也已近实现了拖拽上传.其实很简单. 由于没有服务器支持在文章里不能做上传演示,下载实例 拖拽上传需要什么支持 1:需要浏览器支持 drop 事件.(响应拖拽事件获取file对象); 2:XMLHttpRequest 对象有 sendAsBinary 方法(用于发送数据); 以上两个条件 目前仅有 firefox 能达到. chrome 第一项达标,第2项可以使用 google.gears 来模拟. 所以能实现拖拽上传的浏览器 有 firefox3.6 + 和 chrome7+. 如何实现

  • 全屏js头像上传插件源码高清版

    本文实例为大家分享了全屏js头像上传插件源码,供大家参考,具体内容如下 index.html <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>ccp</title> <link href="Content/ccp.css" rel=&qu

  • 前端页面文件拖拽上传模块js代码示例

    最近给卫生局做一个表格上传/可视化系统,算是小有成果.今天把项目中的文件拖拽上传模块分离出来,做了一个独立的小demo,并把相关代码打包上传到了我的github中,为了其他学习者和开发者提供拙见. gitHub地址:https://github.com/codeplay2015/dragToUpload 由于代码中我的注释很详尽,所以具体逻辑实现及不介绍了,大家直接看代码及能明白.现在简单列一个功能清单和一些用到的知识点清单: 模态框 文件的批量上传 使用formData API 封装数据 并通

  • Js+php实现异步拖拽上传文件

    异步拖拽上传文件--小实例 upload.html <!DOCTYPE html> <!-- To change this license header, choose License Headers in Project Properties. To change this template file, choose Tools | Templates and open the template in the editor. --> <html> <head&g

  • 利用Vue3+Element-plus实现大文件分片上传组件

    目录 一.背景 二.技术栈 三.核心代码实现 四.总结 一.背景 实际项目中遇到需要上传几十个G的3d模型文件,传统上传就不适用了. 结合element提供的上传组件自己封装了文件分片上传的组件. 思路: 把文件拆分成若干分片 依次上传分片(每次上传前可校验该分片是否已经上传) 发起合并分片的请求 二.技术栈 Vue3+Ts+Element-Plus 其他库:spark-md5 后端接口: 上传分片接口 校验分片是否已上传接口 合并分片接口 三.核心代码实现 Element组件基础配置 <el-

  • 简单实现ajax拖拽上传文件

    AJAX拖拽上传功能实现,供大家参考,具体内容如下 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="

  • 关于js拖拽上传 [一个拖拽上传修改头像的流程]

    如今现代的浏览器已经有很多支持拖拽文件读取操作,其优点不再复述.前端时间利用拖拽改进了一下网站的头像上传流程,对其中的要点和实践体会做一点总结. 先看一下总体视图:1. 文件拖拽接受区域要有明显的标示,并且要尽可能的大(由于版面的原因,这个界面的拖放盒子并不大).可以用虚线框盒子等样式吸引用户拖拽文件.最好有明显的文字提示和图标配合. 2. 在交互体验上当文件拖入浏览器窗口时,可以用拖放区变换背景颜色等向用户发起放置操作邀请. 实现代码: 复制代码 代码如下: doc.bind({ 'drage

随机推荐