improve RemoveBackground tool UI/UX
This commit is contained in:
parent
cac901e235
commit
4bd17dd6e0
@ -36,90 +36,66 @@
|
|||||||
|
|
||||||
<!-- 图片预览和处理区域 -->
|
<!-- 图片预览和处理区域 -->
|
||||||
<div v-else class="preview-section">
|
<div v-else class="preview-section">
|
||||||
<div class="image-preview-container">
|
<!-- 单窗口对比视图 -->
|
||||||
<div class="preview-header">
|
<div class="comparison-view" v-if="resultImage">
|
||||||
<h3>{{ t('Image Preview') }}</h3>
|
<div class="comparison-container" ref="comparisonContainerRef">
|
||||||
<div class="header-actions">
|
<!-- 原图(显示左侧部分) -->
|
||||||
<n-button
|
<div
|
||||||
v-if="resultImage"
|
class="comparison-image original-image"
|
||||||
size="medium"
|
:style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }"
|
||||||
@click="handleDownload"
|
>
|
||||||
class="download-btn"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<i class="fa fa-download"></i>
|
|
||||||
</template>
|
|
||||||
{{ t('Download') }}
|
|
||||||
</n-button>
|
|
||||||
<button class="change-image-btn" @click="resetUpload">
|
|
||||||
<i class="fa fa-refresh"></i>
|
|
||||||
{{ t('Change Image') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 单窗口对比视图 -->
|
|
||||||
<div class="comparison-view" v-if="resultImage">
|
|
||||||
<div class="comparison-container" ref="comparisonContainerRef">
|
|
||||||
<!-- 原图(显示左侧部分) -->
|
|
||||||
<div
|
|
||||||
class="comparison-image original-image"
|
|
||||||
:style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }"
|
|
||||||
>
|
|
||||||
<img :src="uploadedImageUrl" alt="Original" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 去背景后的图片(显示右侧部分) -->
|
|
||||||
<div
|
|
||||||
class="comparison-image result-image"
|
|
||||||
:style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }"
|
|
||||||
>
|
|
||||||
<img :src="resultImageUrl" alt="Result" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 拖动竖线 -->
|
|
||||||
<div
|
|
||||||
class="split-line"
|
|
||||||
:class="{ 'dragging': isDraggingSplitLine }"
|
|
||||||
:style="{ left: `${splitPosition}%` }"
|
|
||||||
@mousedown.prevent.stop="handleSplitLineMouseDown"
|
|
||||||
>
|
|
||||||
<div class="split-line-handle">
|
|
||||||
<i class="fa fa-arrows-h"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 处理中或未处理状态 -->
|
|
||||||
<div v-else class="single-image-view">
|
|
||||||
<div class="image-wrapper">
|
|
||||||
<img :src="uploadedImageUrl" alt="Original" />
|
<img :src="uploadedImageUrl" alt="Original" />
|
||||||
<div v-if="processing" class="processing-overlay">
|
</div>
|
||||||
<div class="spinner"></div>
|
|
||||||
<p>{{ t('Processing...') }}</p>
|
<!-- 去背景后的图片(显示右侧部分) -->
|
||||||
|
<div
|
||||||
|
class="comparison-image result-image"
|
||||||
|
:style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }"
|
||||||
|
>
|
||||||
|
<img :src="resultImageUrl" alt="Result" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 拖动竖线 -->
|
||||||
|
<div
|
||||||
|
class="split-line"
|
||||||
|
:class="{ 'dragging': isDraggingSplitLine }"
|
||||||
|
:style="{ left: `${splitPosition}%` }"
|
||||||
|
@mousedown.prevent.stop="handleSplitLineMouseDown"
|
||||||
|
>
|
||||||
|
<div class="split-line-handle">
|
||||||
|
<i class="fa fa-arrows-h"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 处理中或未处理状态 -->
|
||||||
|
<div v-else class="single-image-view">
|
||||||
|
<div class="image-wrapper">
|
||||||
|
<img :src="uploadedImageUrl" alt="Original" />
|
||||||
|
<div v-if="processing" class="processing-overlay">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<p>{{ t('Processing...') }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 使用说明 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="info-section">
|
<div v-if="uploadedImage" class="action-buttons">
|
||||||
<div class="info-card">
|
<button
|
||||||
<div class="info-icon">
|
v-if="resultImage"
|
||||||
<i class="fa fa-info-circle"></i>
|
class="action-btn download-btn"
|
||||||
</div>
|
@click="handleDownload"
|
||||||
<div class="info-content">
|
>
|
||||||
<h4>{{ t('How it works') }}</h4>
|
<i class="fa fa-download"></i>
|
||||||
<ul>
|
{{ t('Download') }}
|
||||||
<li>{{ t('Upload an image with a clear subject') }}</li>
|
</button>
|
||||||
<li>{{ t('Click \"Remove Background\" to process') }}</li>
|
<button class="action-btn change-image-btn" @click="resetUpload">
|
||||||
<li>{{ t('Download the result with transparent background') }}</li>
|
<i class="fa fa-refresh"></i>
|
||||||
</ul>
|
{{ t('Change Image') }}
|
||||||
</div>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -128,7 +104,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { NButton, useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { t } from '@/shared/i18n'
|
import { t } from '@/shared/i18n'
|
||||||
import { get_session_api_headers } from '@/shared/api/auth'
|
import { get_session_api_headers } from '@/shared/api/auth'
|
||||||
@ -155,7 +131,7 @@ const resultImageUrl = computed(() => {
|
|||||||
// 状态
|
// 状态
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
const processing = ref(false)
|
const processing = ref(false)
|
||||||
const splitPosition = ref(50) // 分割线位置(百分比)
|
const splitPosition = ref(0) // 分割线位置(百分比),0表示最左边,默认只显示结果图
|
||||||
const comparisonContainerRef = ref<HTMLElement | null>(null)
|
const comparisonContainerRef = ref<HTMLElement | null>(null)
|
||||||
const isDraggingSplitLine = ref(false)
|
const isDraggingSplitLine = ref(false)
|
||||||
|
|
||||||
@ -210,7 +186,7 @@ const processFile = (file: File) => {
|
|||||||
|
|
||||||
uploadedImage.value = file
|
uploadedImage.value = file
|
||||||
resultImage.value = ''
|
resultImage.value = ''
|
||||||
splitPosition.value = 50 // 重置分割线位置
|
splitPosition.value = 0 // 重置分割线位置到最左边,默认显示完整结果图
|
||||||
|
|
||||||
// 创建预览URL
|
// 创建预览URL
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
@ -227,7 +203,7 @@ const resetUpload = () => {
|
|||||||
uploadedImage.value = null
|
uploadedImage.value = null
|
||||||
uploadedImageUrl.value = ''
|
uploadedImageUrl.value = ''
|
||||||
resultImage.value = ''
|
resultImage.value = ''
|
||||||
splitPosition.value = 50
|
splitPosition.value = 0
|
||||||
if (fileInputRef.value) {
|
if (fileInputRef.value) {
|
||||||
fileInputRef.value.value = ''
|
fileInputRef.value.value = ''
|
||||||
}
|
}
|
||||||
@ -492,6 +468,15 @@ h1 {
|
|||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 当显示图片时,移除 padding 和边框 */
|
||||||
|
.upload-section:has(.preview-section) {
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.upload-area {
|
.upload-area {
|
||||||
border: 2px dashed #cbd5e1;
|
border: 2px dashed #cbd5e1;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@ -560,47 +545,11 @@ h1 {
|
|||||||
/* 预览区域 */
|
/* 预览区域 */
|
||||||
.preview-section {
|
.preview-section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.preview-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #0f172a;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.change-image-btn {
|
|
||||||
height: 36px;
|
|
||||||
padding: 0 16px;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: white;
|
|
||||||
color: #64748b;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: #1fc76f;
|
|
||||||
color: #1fc76f;
|
|
||||||
background: #f0fdf4;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-grid {
|
.preview-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -632,7 +581,6 @@ h1 {
|
|||||||
/* 对比视图 */
|
/* 对比视图 */
|
||||||
.comparison-view {
|
.comparison-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 24px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -640,11 +588,11 @@ h1 {
|
|||||||
|
|
||||||
.comparison-container {
|
.comparison-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 60%;
|
width: min(90vw, 1200px, calc(100vh - 200px));
|
||||||
max-width: 600px;
|
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
border-radius: 12px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
background:
|
background:
|
||||||
linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
|
linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
|
||||||
linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
|
linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
|
||||||
@ -727,7 +675,6 @@ h1 {
|
|||||||
/* 单图视图 */
|
/* 单图视图 */
|
||||||
.single-image-view {
|
.single-image-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 24px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -735,11 +682,11 @@ h1 {
|
|||||||
|
|
||||||
.image-wrapper {
|
.image-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 60%;
|
width: min(90vw, 1200px, calc(100vh - 200px));
|
||||||
max-width: 600px;
|
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
background:
|
background:
|
||||||
linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
|
linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
|
||||||
linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
|
linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
|
||||||
@ -819,104 +766,44 @@ h1 {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.process-btn,
|
.action-btn {
|
||||||
.download-btn {
|
min-width: 140px;
|
||||||
min-width: 180px;
|
|
||||||
height: 48px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 2px 8px rgba(31, 199, 111, 0.2);
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
|
|
||||||
&:hover:not(:disabled) {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 12px rgba(31, 199, 111, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.process-btn {
|
|
||||||
background: linear-gradient(135deg, #1fc76f 0%, #16a085 100%);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
&:hover:not(:disabled) {
|
|
||||||
background: linear-gradient(135deg, #1dd87f 0%, #1ab894 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.download-btn {
|
|
||||||
background: white;
|
|
||||||
border: 2px solid #1fc76f;
|
|
||||||
color: #1fc76f;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #f0fdf4;
|
|
||||||
border-color: #1dd87f;
|
|
||||||
color: #1dd87f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 信息卡片 */
|
|
||||||
.info-section {
|
|
||||||
background: white;
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 24px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-card {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 10px;
|
padding: 0 20px;
|
||||||
background: #ecfdf5;
|
border: 1px solid #e5e7eb;
|
||||||
display: flex;
|
border-radius: 8px;
|
||||||
|
background: white;
|
||||||
|
color: #64748b;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
gap: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 20px;
|
|
||||||
color: #1fc76f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-content {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #0f172a;
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
color: #64748b;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.8;
|
}
|
||||||
|
|
||||||
li {
|
&:hover {
|
||||||
margin-bottom: 8px;
|
border-color: #1fc76f;
|
||||||
|
color: #1fc76f;
|
||||||
|
background: #f0fdf4;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(31, 199, 111, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:active {
|
||||||
margin-bottom: 0;
|
transform: translateY(0);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.remove-background-page {
|
.remove-background-page {
|
||||||
@ -947,8 +834,7 @@ h1 {
|
|||||||
.action-buttons {
|
.action-buttons {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.process-btn,
|
.action-btn {
|
||||||
.download-btn {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user