improve RemoveBackground comparison view and split line styling
This commit is contained in:
parent
6d861a2829
commit
cac901e235
@ -39,69 +39,67 @@
|
||||
<div class="image-preview-container">
|
||||
<div class="preview-header">
|
||||
<h3>{{ t('Image Preview') }}</h3>
|
||||
<button class="change-image-btn" @click="resetUpload">
|
||||
<i class="fa fa-refresh"></i>
|
||||
{{ t('Change Image') }}
|
||||
</button>
|
||||
<div class="header-actions">
|
||||
<n-button
|
||||
v-if="resultImage"
|
||||
size="medium"
|
||||
@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="preview-grid">
|
||||
<!-- 原图 -->
|
||||
<div class="preview-card">
|
||||
<div class="preview-label">{{ t('Original') }}</div>
|
||||
<div class="image-wrapper">
|
||||
<!-- 单窗口对比视图 -->
|
||||
<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 v-if="processing" class="processing-overlay">
|
||||
<div class="spinner"></div>
|
||||
<p>{{ t('Processing...') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 去背景后的图片 -->
|
||||
<div class="preview-card">
|
||||
<div class="preview-label">{{ t('Background Removed') }}</div>
|
||||
<div class="image-wrapper">
|
||||
<div v-if="!resultImage && !processing" class="placeholder">
|
||||
<i class="fa fa-image"></i>
|
||||
<p>{{ t('Result will appear here') }}</p>
|
||||
</div>
|
||||
<img v-else-if="resultImage" :src="resultImageUrl" alt="Result" />
|
||||
<div v-else class="processing-overlay">
|
||||
<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 class="action-buttons">
|
||||
<n-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="processing"
|
||||
:disabled="!uploadedImage || processing"
|
||||
@click="handleRemoveBackground"
|
||||
class="process-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="fa fa-magic"></i>
|
||||
</template>
|
||||
{{ processing ? t('Processing...') : t('Remove Background') }}
|
||||
</n-button>
|
||||
|
||||
<n-button
|
||||
v-if="resultImage"
|
||||
size="large"
|
||||
@click="handleDownload"
|
||||
class="download-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="fa fa-download"></i>
|
||||
</template>
|
||||
{{ t('Download') }}
|
||||
</n-button>
|
||||
|
||||
<!-- 处理中或未处理状态 -->
|
||||
<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>
|
||||
@ -157,6 +155,9 @@ const resultImageUrl = computed(() => {
|
||||
// 状态
|
||||
const isDragging = ref(false)
|
||||
const processing = ref(false)
|
||||
const splitPosition = ref(50) // 分割线位置(百分比)
|
||||
const comparisonContainerRef = ref<HTMLElement | null>(null)
|
||||
const isDraggingSplitLine = ref(false)
|
||||
|
||||
// 触发文件选择
|
||||
const triggerFileInput = () => {
|
||||
@ -209,11 +210,14 @@ const processFile = (file: File) => {
|
||||
|
||||
uploadedImage.value = file
|
||||
resultImage.value = ''
|
||||
splitPosition.value = 50 // 重置分割线位置
|
||||
|
||||
// 创建预览URL
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
uploadedImageUrl.value = e.target?.result as string
|
||||
// 上传后自动触发去背景
|
||||
handleRemoveBackground()
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
@ -223,11 +227,36 @@ const resetUpload = () => {
|
||||
uploadedImage.value = null
|
||||
uploadedImageUrl.value = ''
|
||||
resultImage.value = ''
|
||||
splitPosition.value = 50
|
||||
if (fileInputRef.value) {
|
||||
fileInputRef.value.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 拖动竖线处理
|
||||
const handleSplitLineMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
isDraggingSplitLine.value = true
|
||||
|
||||
const handleMouseMove = (moveEvent: MouseEvent) => {
|
||||
if (!comparisonContainerRef.value || !isDraggingSplitLine.value) return
|
||||
|
||||
const rect = comparisonContainerRef.value.getBoundingClientRect()
|
||||
const x = moveEvent.clientX - rect.left
|
||||
const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100))
|
||||
splitPosition.value = percentage
|
||||
}
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isDraggingSplitLine.value = false
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
|
||||
// 处理去背景
|
||||
const handleRemoveBackground = async () => {
|
||||
// 检查用户是否已登录
|
||||
@ -378,6 +407,14 @@ const handleDownload = () => {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.remove-background-page > .page-content {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
@ -592,9 +629,114 @@ h1 {
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 对比视图 */
|
||||
.comparison-view {
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.comparison-container {
|
||||
position: relative;
|
||||
width: 60%;
|
||||
max-width: 600px;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
|
||||
linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, #f1f5f9 75%),
|
||||
linear-gradient(-45deg, transparent 75%, #f1f5f9 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
||||
}
|
||||
|
||||
.comparison-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.original-image {
|
||||
z-index: 1;
|
||||
/* clip-path 通过内联样式动态设置,用于裁剪显示区域 */
|
||||
}
|
||||
|
||||
.result-image {
|
||||
z-index: 2;
|
||||
/* clip-path 通过内联样式动态设置,用于裁剪显示区域 */
|
||||
}
|
||||
|
||||
/* 拖动竖线 */
|
||||
.split-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: transparent;
|
||||
cursor: col-resize;
|
||||
z-index: 10;
|
||||
transform: translateX(-50%);
|
||||
transition: left 0s;
|
||||
user-select: none;
|
||||
pointer-events: auto;
|
||||
|
||||
&:hover {
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
&.dragging {
|
||||
transition: none;
|
||||
cursor: col-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.split-line-handle {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
border: 2px solid #1fc76f;
|
||||
|
||||
i {
|
||||
color: #1fc76f;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 单图视图 */
|
||||
.single-image-view {
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
width: 60%;
|
||||
max-width: 600px;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user