Stop the profile header from flickering on scroll.
Use a sticky avatar pin instead of changing hero height while the white sheet moves. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -99,13 +99,6 @@ const unreadLabel = computed(() =>
|
||||
notificationStore.unreadCount > 9 ? '9+' : String(notificationStore.unreadCount),
|
||||
)
|
||||
const samplePrice = computed(() => sampleProduct.value.basePrice ?? SAMPLE_PRODUCT.basePrice ?? 0)
|
||||
const collapse = ref(0)
|
||||
const COLLAPSE_DISTANCE = 160
|
||||
|
||||
function onProfileScroll(event: Event) {
|
||||
const top = (event.target as HTMLElement).scrollTop
|
||||
collapse.value = Math.min(1, Math.max(0, top / COLLAPSE_DISTANCE))
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
@@ -134,7 +127,9 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="page profile-page">
|
||||
<header class="hero" :class="{ 'is-collapsed': collapse > 0.85 }" :style="{ '--c': String(collapse) }">
|
||||
<div class="page__scroll page__scroll--with-nav profile-scroll">
|
||||
<header class="hero">
|
||||
<div class="hero__pin">
|
||||
<div class="hero__top">
|
||||
<button
|
||||
type="button"
|
||||
@@ -161,13 +156,13 @@ onMounted(async () => {
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<UserAvatar
|
||||
class="hero__avatar"
|
||||
:src="auth.user?.avatarUrl"
|
||||
:initials="displayName.charAt(0)"
|
||||
:size="84"
|
||||
/>
|
||||
</div>
|
||||
<div class="hero__meta">
|
||||
<h2>{{ displayName }}</h2>
|
||||
<p>کد واسطه: {{ code }}</p>
|
||||
@@ -183,10 +178,7 @@ onMounted(async () => {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div
|
||||
class="page__scroll page__scroll--with-nav profile-scroll"
|
||||
@scroll.passive="onProfileScroll"
|
||||
>
|
||||
<div class="profile-sheet">
|
||||
<PageSkeleton v-if="loading" variant="profile" />
|
||||
<template v-else>
|
||||
<section class="panel">
|
||||
@@ -334,6 +326,7 @@ onMounted(async () => {
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -342,30 +335,39 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.hero {
|
||||
--c: 0;
|
||||
flex-shrink: 0;
|
||||
color: #fff;
|
||||
padding: 0 0 28px;
|
||||
text-align: center;
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
var(--color-primary) 0%,
|
||||
color-mix(in srgb, var(--color-primary) 78%, #0a1f18) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.hero__pin {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
padding: 12px var(--page-padding) 10px;
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
var(--color-primary) 0%,
|
||||
color-mix(in srgb, var(--color-primary) 78%, #0a1f18) 100%
|
||||
);
|
||||
color: #fff;
|
||||
padding: 12px var(--page-padding) calc(44px - var(--c) * 34px);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero__top {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 40px;
|
||||
align-items: center;
|
||||
margin-bottom: calc(16px * (1 - var(--c)));
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero__top h1 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
opacity: calc(1 - var(--c));
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
@@ -397,23 +399,13 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.hero__avatar {
|
||||
margin: 0 auto calc(12px * (1 - var(--c)));
|
||||
margin: 0 auto;
|
||||
border: 3px solid rgba(255, 255, 255, 0.35);
|
||||
color: #fff;
|
||||
transform: scale(calc(1 - var(--c) * 0.18));
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
.hero__meta {
|
||||
overflow: hidden;
|
||||
max-height: calc((1 - var(--c)) * 130px);
|
||||
opacity: calc(1 - var(--c));
|
||||
transform: translateY(calc(var(--c) * -12px));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero.is-collapsed .hero__meta {
|
||||
visibility: hidden;
|
||||
padding: 10px var(--page-padding) 0;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
@@ -447,11 +439,16 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.profile-scroll {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
margin-top: -24px;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.profile-sheet {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: -20px;
|
||||
padding: 20px var(--page-padding) 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -461,7 +458,7 @@ onMounted(async () => {
|
||||
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.profile-scroll > * {
|
||||
.profile-sheet > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user