You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.5 KiB
Vue
140 lines
3.5 KiB
Vue
<template>
|
|
<div @dragover.prevent @drop.prevent>
|
|
<div class="drop" @drop="dragFile">
|
|
<span v-if="loading" class="loading">
|
|
Loading...
|
|
</span>
|
|
<span v-else>
|
|
Drag file here
|
|
</span>
|
|
<img ref="thumb" />
|
|
</div>
|
|
</div>
|
|
|
|
</template>
|
|
<script setup>
|
|
|
|
import {ref, computed} from 'vue'
|
|
import {utils} from '../composables/utils.js'
|
|
|
|
const {base64ToBlob} = utils()
|
|
const emit = defineEmits(['upload', 'file'])
|
|
|
|
const dropFile = ref([])
|
|
const fileType = ref("")
|
|
const thumb = ref(null)
|
|
const loading = ref(false)
|
|
|
|
|
|
const dragFile = (e) => {
|
|
dropFile.value = e.dataTransfer.files[0]
|
|
createPreview(dropFile.value)
|
|
}
|
|
|
|
const createPreview = async (file) => {
|
|
|
|
loading.value = true
|
|
|
|
// get file type
|
|
fileType.value = file.type.toLowerCase().substr(0, file.type.indexOf("/"))
|
|
let filePath = (window.URL || window.webkitURL).createObjectURL(file)
|
|
|
|
// store the initial file
|
|
let fileToRead = file
|
|
|
|
// if file is video create a snapshot and use that instead of the orignal file
|
|
if (fileType.value == 'video') {
|
|
fileToRead = await videoSnapshot(filePath)
|
|
}
|
|
|
|
if (fileType.value == 'image') {
|
|
fileToRead = await imageSnapshot(filePath)
|
|
}
|
|
|
|
filePath = (window.URL || window.webkitURL).createObjectURL(fileToRead)
|
|
|
|
emit('file', fileToRead)
|
|
|
|
const reader = new FileReader()
|
|
reader.readAsDataURL(fileToRead)
|
|
reader.onloadend = (e) => {
|
|
if (e.target.readyState == FileReader.DONE) {
|
|
thumb.value.src = filePath
|
|
emit('upload', filePath)
|
|
loading.value = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: scale image to thumb size
|
|
const imageSnapshot = (url) => {
|
|
return new Promise((resolve, reject)=>{
|
|
let image = document.createElement('img')
|
|
image.src = url
|
|
image.onload = () => {
|
|
let file = snapshot(image)
|
|
resolve(file)
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
|
|
|
|
const videoSnapshot = (url) => {
|
|
return new Promise((resolve, reject)=>{
|
|
let video = document.createElement('video')
|
|
video.src = url
|
|
video.addEventListener('canplay', ()=>{
|
|
let file = snapshot(video)
|
|
video.removeEventListener('canplay', snapshot)
|
|
resolve(file)
|
|
})
|
|
})
|
|
}
|
|
|
|
const snapshot = async (source) => {
|
|
let canvas = document.createElement('canvas')
|
|
let width = source.tagName =='IMG' ? source.naturalWidth : source.videoWidth
|
|
let height = source.tagName == 'IMG' ? source.naturalHeight : source.videoHeight
|
|
canvas.width = 640
|
|
canvas.height = 360
|
|
let ctx = canvas.getContext('2d')
|
|
ctx.drawImage(source, 0, 0, canvas.width, canvas.height)
|
|
let file = await base64ToBlob(canvas.toDataURL('image/jpeg'), 'snapshot')
|
|
return file
|
|
}
|
|
|
|
</script>
|
|
|
|
<style>
|
|
|
|
.drop {
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 16px;
|
|
}
|
|
|
|
.drop img,
|
|
.drop video {
|
|
position: absolute;
|
|
z-index: 100;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
.loading {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
</style>
|