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

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