polish global drag state with minimal futuristic feedback
This commit is contained in:
parent
4d4ab272ca
commit
3a31d9db69
@ -1,5 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="remove-background-page">
|
<div
|
||||||
|
class="remove-background-page"
|
||||||
|
@dragenter.prevent="handleDragEnter"
|
||||||
|
@dragover.prevent="handleDragOver"
|
||||||
|
@dragleave="handleDragLeave"
|
||||||
|
@drop.prevent="handleDrop"
|
||||||
|
>
|
||||||
|
<div v-if="isDragging" class="global-drag-overlay">
|
||||||
|
<div class="overlay-content">
|
||||||
|
<p>{{ t('Drop image anywhere to remove background') }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 文件输入框,始终存在 -->
|
<!-- 文件输入框,始终存在 -->
|
||||||
<input
|
<input
|
||||||
ref="fileInputRef"
|
ref="fileInputRef"
|
||||||
@ -39,9 +50,6 @@
|
|||||||
v-if="!uploadedImage"
|
v-if="!uploadedImage"
|
||||||
class="upload-area"
|
class="upload-area"
|
||||||
:class="{ 'dragging': isDragging }"
|
:class="{ 'dragging': isDragging }"
|
||||||
@drop.prevent="handleDrop"
|
|
||||||
@dragover.prevent="handleDragOver"
|
|
||||||
@dragleave="handleDragLeave"
|
|
||||||
@click="triggerFileInput"
|
@click="triggerFileInput"
|
||||||
>
|
>
|
||||||
<div class="upload-content">
|
<div class="upload-content">
|
||||||
@ -191,6 +199,7 @@ const currentHistoryIndex = ref<number>(-1) // -1 表示当前正在处理的图
|
|||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
|
const dragCounter = ref(0)
|
||||||
const processing = ref(false)
|
const processing = ref(false)
|
||||||
const splitPosition = ref(0) // 分割线位置(百分比),0表示最左边,默认只显示结果图
|
const splitPosition = ref(0) // 分割线位置(百分比),0表示最左边,默认只显示结果图
|
||||||
const comparisonContainerRef = ref<HTMLElement | null>(null)
|
const comparisonContainerRef = ref<HTMLElement | null>(null)
|
||||||
@ -214,16 +223,38 @@ const handleFileSelect = (event: Event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断拖拽是否包含文件
|
||||||
|
const hasFiles = (event: DragEvent) => {
|
||||||
|
const types = event.dataTransfer?.types
|
||||||
|
if (!types) return false
|
||||||
|
return Array.from(types).includes('Files')
|
||||||
|
}
|
||||||
|
|
||||||
// 处理拖拽
|
// 处理拖拽
|
||||||
const handleDragOver = () => {
|
const handleDragEnter = (event: DragEvent) => {
|
||||||
|
if (!hasFiles(event)) return
|
||||||
|
dragCounter.value += 1
|
||||||
isDragging.value = true
|
isDragging.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDragLeave = () => {
|
const handleDragOver = (event: DragEvent) => {
|
||||||
isDragging.value = false
|
if (!hasFiles(event)) return
|
||||||
|
event.dataTransfer!.dropEffect = 'copy'
|
||||||
|
isDragging.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragLeave = (event: DragEvent) => {
|
||||||
|
if (!hasFiles(event)) return
|
||||||
|
dragCounter.value = Math.max(0, dragCounter.value - 1)
|
||||||
|
if (dragCounter.value === 0) {
|
||||||
|
isDragging.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDrop = (event: DragEvent) => {
|
const handleDrop = (event: DragEvent) => {
|
||||||
|
if (!hasFiles(event)) return
|
||||||
|
event.preventDefault()
|
||||||
|
dragCounter.value = 0
|
||||||
isDragging.value = false
|
isDragging.value = false
|
||||||
const file = event.dataTransfer?.files[0]
|
const file = event.dataTransfer?.files[0]
|
||||||
if (file && file.type.startsWith('image/')) {
|
if (file && file.type.startsWith('image/')) {
|
||||||
@ -497,6 +528,112 @@ const handleDownload = () => {
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.global-drag-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: radial-gradient(circle at center, rgba(248, 250, 252, 0.92), rgba(248, 250, 252, 0.82));
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: fade-in 0.15s ease;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 55vmax;
|
||||||
|
height: 55vmax;
|
||||||
|
border-radius: 35%;
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
|
animation: rotate 12s linear infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-content {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 18px 30px;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid rgba(79, 70, 229, 0.35);
|
||||||
|
opacity: 0.7;
|
||||||
|
animation: pulse-line 1.8s ease-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -28px 16px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: linear-gradient(90deg, rgba(59, 130, 246, 0), rgba(59, 130, 246, 0.24), rgba(59, 130, 246, 0));
|
||||||
|
filter: blur(10px);
|
||||||
|
opacity: 0.65;
|
||||||
|
animation: shimmer 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-line {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.9);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-background-page > .page-content {
|
.remove-background-page > .page-content {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user