最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue3的全屏拖拽上傳組件

 更新時(shí)間:2021年09月26日 15:31:15   作者:aloha  
本文主要介紹了基于Vue3的全屏拖拽上傳組件,其實(shí)思路上與其他拖拽上傳組件基本一樣,都是指定一個(gè)區(qū)域可拖拽,然后讀取文件在上傳,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

本文主要介紹了基于Vue3的全屏拖拽上傳組件,分享給大家,具體如下:

知識(shí)點(diǎn)

  • 瀏覽器拖拽 api
  • fetch 請(qǐng)求
  • vue3

說(shuō)來(lái)話長(zhǎng),長(zhǎng)話短說(shuō),關(guān)于 html5 的拖拽 api 也只是做過(guò)一些拖拽排序的例子.其實(shí)思路上與其他拖拽上傳組件基本一樣,都是指定一個(gè)區(qū)域可拖拽,然后讀取文件在上傳
先說(shuō)說(shuō)拖拽 api,這個(gè)是 html5 新增的一個(gè) api,給一個(gè)元素設(shè)置 draggable = true 屬性時(shí),該元素就會(huì)支持拖拽
拖拽元素事件如下

1. ondrag 當(dāng)拖動(dòng)元素的時(shí)候運(yùn)行腳本
2. ondragstart 當(dāng)拖動(dòng)操作開(kāi)始時(shí)候運(yùn)行腳本
3. ondragend 當(dāng)拖動(dòng)操作結(jié)束的時(shí)候運(yùn)行腳本

目標(biāo)元素的事件如下:
1. ondragover 當(dāng)元素被拖動(dòng)至有效拖放目標(biāo)上方時(shí)執(zhí)行腳本
2. ondragenter 當(dāng)元素被拖動(dòng)至有效拖動(dòng)目標(biāo)時(shí)執(zhí)行腳本
3. ondragleave 當(dāng)元素離開(kāi)至有效拖放目標(biāo)是運(yùn)行腳本
4. ondrop 當(dāng)被拖動(dòng)元素正在被放下的時(shí)候運(yùn)行腳本

比如我們想監(jiān)聽(tīng) body 的拖拽:

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

而當(dāng)我們想要阻止默認(rèn)事件的時(shí)候我們可以用 e.preventDefault()

組件

先看一下效果,此時(shí)我這里是設(shè)置的僅能上傳 png 與 jpg

使用:

    <upload
      accept=".jpg,.png,.ico" // 設(shè)置文件類(lèi)型
      @onChange="change" // 文件上傳事件
      action="http://localhost:3001/upload" // 上傳地址
      :header="header" // 上傳的header
      autoUpload // 是否自動(dòng)上傳
      name="file"http:// 上傳的字段名
      @onSuccess="onSuccess"  // 上傳成功回調(diào)
    ></upload>

最開(kāi)始的時(shí)候我想獲取拖拽元素的時(shí)候莫名發(fā)現(xiàn)盡管加了監(jiān)聽(tīng)事件,可還是會(huì)打開(kāi)新的窗口去預(yù)覽文件,所以我們第一步就是先把默認(rèn)事件都給禁用掉

// 禁用默認(rèn)拖拽事件
function disableDefaultEvents() {
  const doc = document.documentElement
  doc.addEventListener('dragleave', (e) => e.preventDefault()) //拖離
  doc.addEventListener('drop', (e) => e.preventDefault()) //拖后放
  doc.addEventListener('dragenter', (e) => e.preventDefault()) //拖進(jìn)
  doc.addEventListener('dragover', (e) => e.preventDefault()) //拖來(lái)拖去
}

直接獲取根元素,阻止拖拽的默認(rèn)事件

第二步就是我們給 body 或是其他元素加上我們想要監(jiān)聽(tīng)的事件,這里有一個(gè)注意的是 body 的高度一定是窗口的高度,這樣才會(huì)全屏拖拽,在拖離的時(shí)候我們還要判斷一下文件是否被拖出區(qū)域

這里一共有這么判斷,

e.target.nodeName === 'HTML',這個(gè)用來(lái)判斷根元素是不是 html
e.target === e.explicitOriginalTarget 這個(gè)是火狐特有的一個(gè) api,判斷這兩個(gè)觸發(fā)事件的目標(biāo)是否一致
(!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
e.clientY >= window.innerHeight))

這個(gè)是用來(lái)判斷鼠標(biāo)當(dāng)前的位置的,是否還在區(qū)域內(nèi)

// 初始化拖入事件
function init() {
    // 獲取body元素
  const ele = document.querySelector('body')
  // 添加事件
  //拖后放
  ele.addEventListener('dragenter', () => {
    show.value = true
  })
  // 這里判斷鼠標(biāo)拖離
  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
    }
  })
  //拖進(jìn)
  ele.addEventListener('drop', (e) => {
    show.value = false
    e.preventDefault()
    onDrop(e) // 拖入處理文件的方法
  })
}

第三步是處理拖入的文件,此時(shí) accept 是我們定義的文件類(lèi)型,此時(shí)我們用e.dataTransfer.files這個(gè)屬性可以獲得拖入的文件,
然后我們把拖入的文件用 filter 做一個(gè)過(guò)濾,只保留我們需要的文件類(lèi)型

checkType(file,accept)就是用來(lái)判斷文件類(lèi)型的,這一個(gè)函數(shù)是借鑒了 element ui 里面的上傳組件的篩選,當(dāng)時(shí)我也是寫(xiě)蒙了我 😂

// 檢查文件類(lèi)型
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
      }
    })
}

這個(gè)方法是文件拖入之后的處理,當(dāng)我們獲得需要的文件之后就是根據(jù)autoUpload來(lái)判斷一下是否上傳

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)
  })
  // 觸發(fā)事件
  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 }, // 上傳的文件頭
  // 驗(yàn)證的文件類(lèi)型,有值的時(shí)候只會(huì)拖入所有的文件只會(huì)保留設(shè)置過(guò)濾后的文件
  accept: {
    type: String,
    default: '',
  },
  // 是否開(kāi)啟自動(dòng)上傳
  autoUpload: {
    type: Boolean,
    default: false,
  },
  // 上傳地址
  action: {
    type: String,
    default: '#',
  },
})

const emit = defineEmits(['onError', 'onProgress', 'onSuccess', 'onChange']) // 默認(rèn)emit事件
let show = ref(false) // 是否展示遮罩
let fileList = reactive([]) // 文件列表
let tempIndex = 0 // 做一個(gè)標(biāo)記
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)
  }) //拖進(jìn)
}
// 禁用默認(rèn)拖拽事件
function disableDefaultEvents() {
  const doc = document.documentElement
  doc.addEventListener('dragleave', (e) => e.preventDefault()) //拖離
  doc.addEventListener('drop', (e) => e.preventDefault()) //拖后放
  doc.addEventListener('dragenter', (e) => e.preventDefault()) //拖進(jìn)
  doc.addEventListener('dragover', (e) => e.preventDefault()) //拖來(lái)拖去
}
// 拖入時(shí)的事件
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)
    })
  }
}
// 檢查文件類(lèi)型
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',
  })
}

// 拖拽進(jìn)去獲取文件列表的事件
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>

到此這篇關(guān)于基于Vue3的全屏拖拽上傳組件的文章就介紹到這了,更多相關(guān)Vue3 全屏拖拽上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Vue如何實(shí)現(xiàn)響應(yīng)式布局

    詳解Vue如何實(shí)現(xiàn)響應(yīng)式布局

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)響應(yīng)式布局的兩種方法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-12-12
  • VueJS 集成 Medium Editor的示例代碼 (自定義編輯器按鈕)

    VueJS 集成 Medium Editor的示例代碼 (自定義編輯器按鈕)

    本篇文章主要介紹了VueJS 集成 Medium Editor的示例代碼 (自定義編輯器按鈕),具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • Vue3 elementUI如何修改el-date-picker默認(rèn)時(shí)間

    Vue3 elementUI如何修改el-date-picker默認(rèn)時(shí)間

    這篇文章主要介紹了Vue3 elementUI如何修改el-date-picker默認(rèn)時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • antfu大佬的v-lazy-show教我學(xué)會(huì)了怎么編譯模板指令

    antfu大佬的v-lazy-show教我學(xué)會(huì)了怎么編譯模板指令

    這篇文章主要介紹了antfu大佬的v-lazy-show,我學(xué)會(huì)了怎么編譯模板指令示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 基于vue-cli 路由 實(shí)現(xiàn)類(lèi)似tab切換效果(vue 2.0)

    基于vue-cli 路由 實(shí)現(xiàn)類(lèi)似tab切換效果(vue 2.0)

    這篇文章主要介紹了基于vue-cli 路由 實(shí)現(xiàn)類(lèi)似tab切換效果(vue 2.0),非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-05-05
  • Vue3兄弟組件傳值之mitt的超詳細(xì)講解

    Vue3兄弟組件傳值之mitt的超詳細(xì)講解

    之前只是淺顯的使用插件進(jìn)行vue開(kāi)發(fā)展示,最近深入的研究了下,下面這篇文章主要給大家介紹了關(guān)于Vue3兄弟組件傳值之mitt的超詳細(xì)講解,需要的朋友可以參考下
    2022-06-06
  • vue2.x引入threejs的實(shí)例代碼

    vue2.x引入threejs的實(shí)例代碼

    這篇文章主要介紹了vue2.x引入threejs,如果在開(kāi)發(fā)過(guò)程中出現(xiàn)threejs生成的canvas出現(xiàn)外邊框,只需要用css樣式修改,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • vite+vue3項(xiàng)目中svg圖標(biāo)組件封裝的過(guò)程詳解

    vite+vue3項(xiàng)目中svg圖標(biāo)組件封裝的過(guò)程詳解

    這篇文章主要介紹了vite+vue3項(xiàng)目中svg圖標(biāo)組件封裝的過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-03-03
  • Vue3中使用富文本編輯器的方法詳解

    Vue3中使用富文本編輯器的方法詳解

    這篇文章主要為大家詳細(xì)介紹了如何在Vue3中使用富文本編輯器,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以參考一下
    2024-01-01
  • Vue實(shí)現(xiàn)五子棋小游戲

    Vue實(shí)現(xiàn)五子棋小游戲

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05

最新評(píng)論

九台市| 盈江县| 民勤县| 乌兰察布市| 拜泉县| 开远市| 扎赉特旗| 青田县| 六安市| 洪洞县| 广汉市| 棋牌| 弥渡县| 长岛县| 房产| 正安县| 饶河县| 汉川市| 福泉市| 娱乐| 林口县| 黄冈市| 灵宝市| 湛江市| 千阳县| 都匀市| 齐河县| 来凤县| 昌黎县| 榆中县| 玉屏| 金堂县| 邻水| 盐池县| 彭山县| 辛集市| 威远县| 黄石市| 兰考县| 阿合奇县| 临漳县|