Fix carousel: full-width image slides with transform animation
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 12s
Test / e2e-test (push) Failing after 23s
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 12s
Test / e2e-test (push) Failing after 23s
Replaced horizontal scroll tags with original-style carousel: - Full-width slides with background image + gradient overlay - translateX transform animation (0.4s ease) - Left/right arrow buttons (semi-transparent, blur backdrop) - Dot indicators with active state (elongated pill) - Category filter banner when a category is selected - Click slide to filter recipes by category tag Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +1,38 @@
|
||||
<template>
|
||||
<div class="recipe-search">
|
||||
<!-- Category Carousel -->
|
||||
<div class="cat-wrap" v-if="categories.length">
|
||||
<button class="cat-arrow cat-arrow-left" @click="scrollCat(-1)" :disabled="catScrollPos <= 0">‹</button>
|
||||
<div class="cat-track" ref="catTrack">
|
||||
<!-- Category Carousel (full-width image slides) -->
|
||||
<div class="cat-wrap" v-if="categories.length && !selectedCategory">
|
||||
<div class="cat-track" :style="{ transform: `translateX(-${catIdx * 100}%)` }">
|
||||
<div
|
||||
v-for="cat in categories"
|
||||
:key="cat.name"
|
||||
class="cat-card"
|
||||
:class="{ active: selectedCategory === cat.name }"
|
||||
@click="toggleCategory(cat.name)"
|
||||
:style="{ backgroundImage: cat.bg_image ? `url(${cat.bg_image})` : `linear-gradient(135deg, ${cat.color_from || '#7a9e7e'}, ${cat.color_to || '#5a7d5e'})` }"
|
||||
@click="selectCategory(cat)"
|
||||
>
|
||||
<span class="cat-icon">{{ cat.icon || '📁' }}</span>
|
||||
<span class="cat-label">{{ cat.name }}</span>
|
||||
<div class="cat-inner">
|
||||
<div class="cat-icon">{{ cat.icon || '🌿' }}</div>
|
||||
<div class="cat-name">{{ cat.name }}</div>
|
||||
<div v-if="cat.subtitle" class="cat-sub">{{ cat.subtitle }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="cat-arrow cat-arrow-right" @click="scrollCat(1)">›</button>
|
||||
<button class="cat-arrow left" @click="slideCat(-1)">‹</button>
|
||||
<button class="cat-arrow right" @click="slideCat(1)">›</button>
|
||||
</div>
|
||||
<div class="cat-dots" v-if="categories.length > 1 && !selectedCategory">
|
||||
<span
|
||||
v-for="(cat, i) in categories"
|
||||
:key="i"
|
||||
class="cat-dot"
|
||||
:class="{ active: catIdx === i }"
|
||||
@click="catIdx = i"
|
||||
></span>
|
||||
</div>
|
||||
<!-- Category filter active banner -->
|
||||
<div v-if="selectedCategory" class="cat-filter-bar">
|
||||
<span>📂 {{ selectedCategory }}</span>
|
||||
<button @click="selectedCategory = null; catIdx = 0" class="btn-sm btn-outline">✕ 返回全部</button>
|
||||
</div>
|
||||
|
||||
<!-- Search Box -->
|
||||
@@ -126,8 +143,7 @@ const categories = ref([])
|
||||
const selectedRecipeIndex = ref(null)
|
||||
const showMyRecipes = ref(true)
|
||||
const showFavorites = ref(true)
|
||||
const catScrollPos = ref(0)
|
||||
const catTrack = ref(null)
|
||||
const catIdx = ref(0)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
@@ -140,15 +156,13 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
function toggleCategory(name) {
|
||||
selectedCategory.value = selectedCategory.value === name ? null : name
|
||||
function selectCategory(cat) {
|
||||
selectedCategory.value = cat.tag_name || cat.name
|
||||
}
|
||||
|
||||
function scrollCat(dir) {
|
||||
if (!catTrack.value) return
|
||||
const scrollAmount = 200
|
||||
catTrack.value.scrollLeft += dir * scrollAmount
|
||||
catScrollPos.value = catTrack.value.scrollLeft + dir * scrollAmount
|
||||
function slideCat(dir) {
|
||||
const len = categories.value.length
|
||||
catIdx.value = (catIdx.value + dir + len) % len
|
||||
}
|
||||
|
||||
const filteredRecipes = computed(() => {
|
||||
@@ -219,55 +233,121 @@ function clearSearch() {
|
||||
|
||||
.cat-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
gap: 4px;
|
||||
margin: 0 -12px 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cat-track {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
overflow-x: auto;
|
||||
scroll-behavior: smooth;
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.cat-track::-webkit-scrollbar {
|
||||
display: none;
|
||||
transition: transform 0.4s ease;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.cat-card {
|
||||
flex: 0 0 100%;
|
||||
min-height: 200px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.cat-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, rgba(0,0,0,0.45), rgba(0,0,0,0.25));
|
||||
}
|
||||
|
||||
.cat-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
background: #f8f7f5;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s;
|
||||
min-width: 64px;
|
||||
border: 1.5px solid transparent;
|
||||
}
|
||||
|
||||
.cat-card:hover {
|
||||
background: #f0eeeb;
|
||||
}
|
||||
|
||||
.cat-card.active {
|
||||
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
|
||||
border-color: #7ec6a4;
|
||||
color: #2e7d5a;
|
||||
font-weight: 600;
|
||||
padding: 36px 24px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cat-icon {
|
||||
font-size: 20px;
|
||||
font-size: 48px;
|
||||
margin-bottom: 10px;
|
||||
filter: drop-shadow(0 2px 6px rgba(0,0,0,0.3));
|
||||
}
|
||||
|
||||
.cat-name {
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 3px;
|
||||
text-shadow: 0 2px 8px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.cat-sub {
|
||||
font-size: 13px;
|
||||
margin-top: 6px;
|
||||
opacity: 0.9;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.cat-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 2;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.25);
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
backdrop-filter: blur(4px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.cat-arrow:hover { background: rgba(255,255,255,0.45); }
|
||||
.cat-arrow.left { left: 12px; }
|
||||
.cat-arrow.right { right: 12px; }
|
||||
|
||||
.cat-dots {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.cat-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--border, #e0d4c0);
|
||||
cursor: pointer;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
.cat-dot.active {
|
||||
background: var(--sage, #7a9e7e);
|
||||
width: 22px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.cat-filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: var(--sage-mist, #eef4ee);
|
||||
border-radius: 10px;
|
||||
padding: 10px 16px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--sage-dark, #5a7d5e);
|
||||
}
|
||||
|
||||
.cat-label {
|
||||
|
||||
Reference in New Issue
Block a user