重构添加背景页手机端右边栏布局
This commit is contained in:
parent
b0a213ce4a
commit
630e441a97
@ -1113,12 +1113,12 @@
|
|||||||
"Add custom background color to images. Free online tool to add background to transparent images. Supports JPG, PNG, WebP formats.": "为图片添加自定义背景颜色。免费在线工具,可为透明背景图片添加背景。支持 JPG、PNG、WebP 格式。",
|
"Add custom background color to images. Free online tool to add background to transparent images. Supports JPG, PNG, WebP formats.": "为图片添加自定义背景颜色。免费在线工具,可为透明背景图片添加背景。支持 JPG、PNG、WebP 格式。",
|
||||||
"add background, background color, image background, transparent background, online background tool, image processing, background editor, free tool": "添加背景、背景颜色、图片背景、透明背景、在线背景工具、图片处理、背景编辑器、免费工具",
|
"add background, background color, image background, transparent background, online background tool, image processing, background editor, free tool": "添加背景、背景颜色、图片背景、透明背景、在线背景工具、图片处理、背景编辑器、免费工具",
|
||||||
"Background Color": "背景颜色",
|
"Background Color": "背景颜色",
|
||||||
"Add to favorites": "添加到收藏",
|
"Add to favorites": "添加到收藏夹",
|
||||||
"Remove from favorites": "从收藏中移除",
|
"Remove from favorites": "从收藏夹中移除",
|
||||||
"Favorite Colors": "收藏的颜色",
|
"Favorite Colors": "收藏的颜色",
|
||||||
"Color already in favorites": "颜色已在收藏中",
|
"Color already in favorites": "颜色已在收藏夹",
|
||||||
"Maximum 12 favorite colors allowed. Please remove one first.": "最多允许12个收藏颜色,请先移除一个",
|
"Maximum 12 favorite colors allowed. Please remove one first.": "最多允许12个收藏颜色,请先移除一个",
|
||||||
"Color added to favorites": "颜色已添加到收藏",
|
"Color added to favorites": "颜色已添加到收藏夹",
|
||||||
"Color Tones": "色调",
|
"Color Tones": "色调",
|
||||||
"Common Colors": "常用颜色",
|
"Common Colors": "常用颜色",
|
||||||
"Apply Background": "应用背景",
|
"Apply Background": "应用背景",
|
||||||
|
|||||||
@ -50,6 +50,13 @@
|
|||||||
>
|
>
|
||||||
<i class="fa fa-refresh"></i>
|
<i class="fa fa-refresh"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="toolbar-btn mobile-color-picker-btn"
|
||||||
|
@click="toggleMobileColorPicker"
|
||||||
|
:title="t('Color Picker')"
|
||||||
|
>
|
||||||
|
<i class="fa fa-palette"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -157,8 +164,103 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right Sidebar for Common Colors and Color Picker -->
|
<!-- Mobile Bottom Sheet Color Picker -->
|
||||||
<div v-if="uploadedImage" class="right-sidebar">
|
<transition name="slide-up">
|
||||||
|
<div
|
||||||
|
v-if="uploadedImage && showMobileColorPicker"
|
||||||
|
class="mobile-color-sheet-overlay"
|
||||||
|
@click="closeMobileColorPicker"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mobile-color-sheet"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<div class="sheet-handle"></div>
|
||||||
|
<div class="sheet-header">
|
||||||
|
<h4>{{ t('Background Color') }}</h4>
|
||||||
|
<button
|
||||||
|
class="sheet-close-btn"
|
||||||
|
@click="closeMobileColorPicker"
|
||||||
|
:title="t('Close')"
|
||||||
|
>
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="sheet-content">
|
||||||
|
<div class="color-picker-section">
|
||||||
|
<div class="color-picker-container">
|
||||||
|
<input
|
||||||
|
v-model="backgroundColor"
|
||||||
|
type="color"
|
||||||
|
class="sidebar-color-picker"
|
||||||
|
@input="onColorChange"
|
||||||
|
/>
|
||||||
|
<div class="hex-input-wrapper">
|
||||||
|
<input
|
||||||
|
v-model="backgroundColor"
|
||||||
|
type="text"
|
||||||
|
class="sidebar-hex-input"
|
||||||
|
placeholder="#FFFFFF"
|
||||||
|
@input="onHexInputChange"
|
||||||
|
@blur="onHexInputBlur"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="add-favorite-btn"
|
||||||
|
@click="addToFavorites"
|
||||||
|
:title="t('Add to favorites')"
|
||||||
|
:disabled="favoriteColors.length >= MAX_FAVORITE_COLORS"
|
||||||
|
>
|
||||||
|
<i class="fa fa-star"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="favoriteColors.length > 0" class="favorite-colors-section">
|
||||||
|
<h4 class="section-title">{{ t('Favorite Colors') }}</h4>
|
||||||
|
<div class="favorite-colors-grid">
|
||||||
|
<div
|
||||||
|
v-for="(color, index) in favoriteColors"
|
||||||
|
:key="index"
|
||||||
|
class="favorite-color-item"
|
||||||
|
:class="{ 'active': color === backgroundColor }"
|
||||||
|
:style="{ backgroundColor: color }"
|
||||||
|
@click="selectCommonColor(color)"
|
||||||
|
:title="color"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="remove-favorite-btn"
|
||||||
|
@click.stop="removeFavoriteColor(color)"
|
||||||
|
:title="t('Remove from favorites')"
|
||||||
|
:aria-label="t('Remove from favorites')"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="common-colors-section">
|
||||||
|
<h4 class="section-title">{{ t('Common Colors') }}</h4>
|
||||||
|
<div class="common-colors-grid">
|
||||||
|
<div
|
||||||
|
v-for="(color, index) in commonColors"
|
||||||
|
:key="index"
|
||||||
|
class="common-color-item"
|
||||||
|
:class="{ 'active': color === backgroundColor }"
|
||||||
|
:style="{ backgroundColor: color }"
|
||||||
|
@click="selectCommonColor(color)"
|
||||||
|
:title="color"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<!-- Right Sidebar for Common Colors and Color Picker (Desktop Only) -->
|
||||||
|
<div v-if="uploadedImage" class="right-sidebar desktop-only">
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="color-picker-section">
|
<div class="color-picker-section">
|
||||||
<h4 class="section-title">{{ t('Background Color') }}</h4>
|
<h4 class="section-title">{{ t('Background Color') }}</h4>
|
||||||
@ -501,6 +603,7 @@ const currentHistoryIndex = ref<number>(-1)
|
|||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
const dragCounter = ref(0)
|
const dragCounter = ref(0)
|
||||||
const processing = ref(false)
|
const processing = ref(false)
|
||||||
|
const showMobileColorPicker = ref(false)
|
||||||
let fabricCanvas: Canvas | null = null
|
let fabricCanvas: Canvas | null = null
|
||||||
|
|
||||||
const adjustCanvasSize = () => {
|
const adjustCanvasSize = () => {
|
||||||
@ -513,8 +616,11 @@ const adjustCanvasSize = () => {
|
|||||||
const previewSection = container.closest('.preview-section') as HTMLElement
|
const previewSection = container.closest('.preview-section') as HTMLElement
|
||||||
if (!previewSection) return
|
if (!previewSection) return
|
||||||
|
|
||||||
|
// Check if mobile
|
||||||
|
const isMobile = window.innerWidth <= 768
|
||||||
|
|
||||||
const previewRect = previewSection.getBoundingClientRect()
|
const previewRect = previewSection.getBoundingClientRect()
|
||||||
const padding = 48
|
const padding = isMobile ? 24 : 48
|
||||||
const maxAvailableWidth = Math.max(0, previewRect.width - padding)
|
const maxAvailableWidth = Math.max(0, previewRect.width - padding)
|
||||||
const maxAvailableHeight = Math.max(0, previewRect.height - padding)
|
const maxAvailableHeight = Math.max(0, previewRect.height - padding)
|
||||||
|
|
||||||
@ -695,6 +801,8 @@ onUnmounted(() => {
|
|||||||
fabricCanvas.dispose()
|
fabricCanvas.dispose()
|
||||||
fabricCanvas = null
|
fabricCanvas = null
|
||||||
}
|
}
|
||||||
|
// Restore body scroll
|
||||||
|
document.body.style.overflow = ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const triggerFileInput = () => {
|
const triggerFileInput = () => {
|
||||||
@ -1056,11 +1164,34 @@ const onHexInputBlur = () => {
|
|||||||
const selectShade = (color: string) => {
|
const selectShade = (color: string) => {
|
||||||
backgroundColor.value = color
|
backgroundColor.value = color
|
||||||
applyBackgroundSilent()
|
applyBackgroundSilent()
|
||||||
|
// Auto-close mobile color picker on mobile
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
closeMobileColorPicker()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectCommonColor = (color: string) => {
|
const selectCommonColor = (color: string) => {
|
||||||
backgroundColor.value = color
|
backgroundColor.value = color
|
||||||
applyBackgroundSilent()
|
applyBackgroundSilent()
|
||||||
|
// Auto-close mobile color picker on mobile
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
closeMobileColorPicker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleMobileColorPicker = () => {
|
||||||
|
showMobileColorPicker.value = !showMobileColorPicker.value
|
||||||
|
// Prevent body scroll when sheet is open
|
||||||
|
if (showMobileColorPicker.value) {
|
||||||
|
document.body.style.overflow = 'hidden'
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeMobileColorPicker = () => {
|
||||||
|
showMobileColorPicker.value = false
|
||||||
|
document.body.style.overflow = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const debouncedApplyBackground = () => {
|
const debouncedApplyBackground = () => {
|
||||||
@ -1857,6 +1988,127 @@ const removeHistoryItem = (index: number) => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide desktop sidebar on mobile */
|
||||||
|
.desktop-only {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Color Picker Button - Hidden on desktop */
|
||||||
|
.mobile-color-picker-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Bottom Sheet */
|
||||||
|
.mobile-color-sheet-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-color-sheet {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 70vh;
|
||||||
|
background: white;
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.15);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-handle {
|
||||||
|
width: 40px;
|
||||||
|
height: 4px;
|
||||||
|
background: #cbd5e1;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 12px auto 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-close-btn {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: #6b7280;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slide up animation */
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active .mobile-color-sheet,
|
||||||
|
.slide-up-leave-active .mobile-color-sheet {
|
||||||
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from,
|
||||||
|
.slide-up-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from .mobile-color-sheet,
|
||||||
|
.slide-up-leave-to .mobile-color-sheet {
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-to .mobile-color-sheet,
|
||||||
|
.slide-up-leave-from .mobile-color-sheet {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-content {
|
.sidebar-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@ -2282,6 +2534,16 @@ const removeHistoryItem = (index: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
/* Hide desktop sidebar on mobile */
|
||||||
|
.desktop-only {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show mobile color picker button */
|
||||||
|
.mobile-color-picker-btn {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -2320,10 +2582,7 @@ const removeHistoryItem = (index: number) => {
|
|||||||
.page-content {
|
.page-content {
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
padding-right: 12px !important;
|
padding-right: 12px !important;
|
||||||
}
|
padding-bottom: 12px !important; /* Remove bottom padding since no fixed sidebar */
|
||||||
|
|
||||||
.page-content.has-sidebar {
|
|
||||||
padding-bottom: 220px; /* Make space for bottom sidebar */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-section {
|
.upload-section {
|
||||||
@ -2365,25 +2624,14 @@ const removeHistoryItem = (index: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preview-section {
|
.preview-section {
|
||||||
padding: 0;
|
padding: 12px;
|
||||||
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas-container {
|
.canvas-container {
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
padding-right: 20px;
|
margin: 0 auto; /* Center horizontally */
|
||||||
}
|
display: block; /* Ensure proper centering */
|
||||||
|
|
||||||
.right-sidebar {
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
top: auto;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
border-left: none;
|
|
||||||
border-top: 1px solid #e5e7eb;
|
|
||||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hex-input-wrapper {
|
.hex-input-wrapper {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user