Compare commits

..

No commits in common. "e29a718b2fa5562f059c463de07dee7c518ce83d" and "4d42d9a6d08c39be90617b465b5f8663e80f6919" have entirely different histories.

2 changed files with 53 additions and 1185 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,52 @@
<template> <template>
<scroll-view class="waterfall-scroll" :style="scrollStyle" scroll-x :show-scrollbar="false" <scroll-view
:scroll-left="isIOS ? undefined : scrollLeft" :bounce="false" @scroll="onScroll" @touchstart="onTouchStart" class="waterfall-scroll"
@touchend="onTouchEnd" @touchcancel="onTouchEnd"> :style="scrollStyle"
<view ref="waterfallInnerRef" :class="{ 'waterfall-inner': true, 'ios-css-animate': !iosScrollPaused }" scroll-x
:style="innerStyle"> :show-scrollbar="false"
<view v-for="card in cards" :key="card.id" class="wf-card" :style="cardStyle(card)" :scroll-left="isIOS ? undefined : scrollLeft"
@click="handleCardClick(card)"> :bounce="false"
@scroll="onScroll"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
@touchcancel="onTouchEnd"
>
<view
ref="waterfallInnerRef"
:class="{'waterfall-inner': true, 'ios-css-animate': !iosScrollPaused}"
:style="innerStyle"
>
<view
v-for="card in cards"
:key="card.id"
class="wf-card"
:style="cardStyle(card)"
@click="handleCardClick(card)"
>
<!-- 渐变边框光效 --> <!-- 渐变边框光效 -->
<view class="wf-card-border" :style="borderStyle(card)" /> <view class="wf-card-border" :style="borderStyle(card)" />
<!-- 封面图 --> <!-- 封面图 -->
<image v-if="card.coverUrl" class="wf-card-img" :src="card.coverUrl" mode="aspectFill" <image
:style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }" /> v-if="card.coverUrl"
class="wf-card-img"
:src="card.coverUrl"
mode="aspectFill"
:style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }"
/>
<!-- 光波动画层 - 外层 --> <!-- 光波动画层 - 外层 -->
<view class="wf-like-wave wf-like-wave-outer" :class="{ 'wf-like-wave-active': likingMap[card.id] }" <view
:style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }" /> class="wf-like-wave wf-like-wave-outer"
:class="{ 'wf-like-wave-active': likingMap[card.id] }"
:style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }"
/>
<!-- 光波动画层 - 内层 --> <!-- 光波动画层 - 内层 -->
<view class="wf-like-wave wf-like-wave-inner" :class="{ 'wf-like-wave-active': likingMap[card.id] }" <view
:style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }" /> class="wf-like-wave wf-like-wave-inner"
:class="{ 'wf-like-wave-active': likingMap[card.id] }"
:style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }"
/>
<!-- 底部点赞数 --> <!-- 底部点赞数 -->
<view class="wf-card-footer"> <view class="wf-card-footer">
@ -156,10 +184,6 @@ const startAutoScroll = () => {
scrollUpdateTimer = setInterval(() => { scrollUpdateTimer = setInterval(() => {
if (!isComponentMounted || userInteracting || isLoadingMore) return if (!isComponentMounted || userInteracting || isLoadingMore) return
autoScrollPos += AUTO_SCROLL_SPEED_ANDROID autoScrollPos += AUTO_SCROLL_SPEED_ANDROID
// 0
if (autoScrollPos >= totalWidth.value) {
autoScrollPos = 0
}
scrollLeft.value = autoScrollPos scrollLeft.value = autoScrollPos
// //
@ -334,13 +358,6 @@ class WaterfallLayout {
// = rowH × 9/16 9:16span // = rowH × 9/16 9:16span
this.colW = Math.round(this.rowH * 4 / 3) this.colW = Math.round(this.rowH * 4 / 3)
this.curX = 0 this.curX = 0
// N 5 6 N
this.consecutiveNCount = 0
}
_isNLevel(item) {
const span = item.span != null ? item.span : this._span(item.likes || 0)
return span === 1
} }
_cardSize(span) { _cardSize(span) {
@ -375,28 +392,10 @@ class WaterfallLayout {
let i = 0 let i = 0
while (i < remaining.length) { while (i < remaining.length) {
const rawSpan = remaining[i].span const rawSpan = remaining[i].span
const isN = remaining[i]._blank ? false : this._isNLevel(remaining[i])
// N 5 6 N 1-2
if (isN && this.consecutiveNCount >= 5) {
const blankCount = Math.random() < 0.5 ? 1 : 2
for (let b = 0; b < blankCount; b++) {
if (sum + 1 <= ROWS) {
remaining.splice(i, 0, { id: idCounter++, span: 1, _blank: true, likes: 0 })
}
}
this.consecutiveNCount = 0
// i break
break
}
// ROWS // ROWS
if (sum + rawSpan > ROWS) { if (sum + rawSpan > ROWS) {
i++ i++
// ROWS
if (i >= remaining.length && col.length > 0) {
break
}
continue continue
} }
@ -405,15 +404,6 @@ class WaterfallLayout {
col.push(item) col.push(item)
sum += rawSpan sum += rawSpan
// N N
if (!item._blank) {
if (this._isNLevel(item)) {
this.consecutiveNCount++
} else {
this.consecutiveNCount = 0
}
}
// ROWS // ROWS
if (sum >= ROWS) break if (sum >= ROWS) break
} }
@ -423,13 +413,6 @@ class WaterfallLayout {
const item = remaining.shift() const item = remaining.shift()
col.push(item) col.push(item)
sum = item.span sum = item.span
if (!item._blank) {
if (this._isNLevel(item)) {
this.consecutiveNCount++
} else {
this.consecutiveNCount = 0
}
}
} }
colIndex++ colIndex++
@ -454,7 +437,7 @@ class WaterfallLayout {
for (const u of colUsers) { for (const u of colUsers) {
const span = u.span || 1 const span = u.span || 1
const { w, h } = this._cardSize(span) const { w, h } = this._cardSize(span)
if (!u._pad && !u._blank) { if (!u._pad) {
result.push({ ...u, left: colX, top: curY, w, h, radius: 8 }) result.push({ ...u, left: colX, top: curY, w, h, radius: 8 })
} }
curY += h + this.gap curY += h + this.gap
@ -467,7 +450,6 @@ class WaterfallLayout {
// //
compute(users) { compute(users) {
this.curX = 0 this.curX = 0
this.consecutiveNCount = 0
const columns = this._groupIntoColumns(users) const columns = this._groupIntoColumns(users)
const result = [] const result = []
for (const col of columns) { for (const col of columns) {
@ -526,9 +508,9 @@ const cardStyle = (card) => ({
const borderStyle = (card) => ({ const borderStyle = (card) => ({
position: 'absolute', position: 'absolute',
inset: `-${BORDER_W}px`,
borderRadius: (card.radius + BORDER_W) + 'px', borderRadius: (card.radius + BORDER_W) + 'px',
background: BORDER_COLORS[Math.abs(card.id) % BORDER_COLORS.length], background: BORDER_COLORS[Math.abs(card.id) % BORDER_COLORS.length],
inset: `-${BORDER_W}px`,
zIndex: 0, zIndex: 0,
opacity: 0.85, opacity: 0.85,
}) })
@ -584,7 +566,7 @@ const batchGetPresignedUrls = async (urls) => {
} }
return map return map
} }
} catch (_) { } } catch (_) {}
return {} return {}
} }
@ -659,7 +641,7 @@ const loadUsers = async () => {
span: item.span ?? null, span: item.span ?? null,
} }
}) })
allUsers.value = withData allUsers.value = withData
cards.value = layout.compute(withData) cards.value = layout.compute(withData)
totalWidth.value = layout.getTotalWidth() totalWidth.value = layout.getTotalWidth()
@ -749,12 +731,6 @@ const appendMore = async () => {
cards.value = [...cards.value, ...placed] cards.value = [...cards.value, ...placed]
totalWidth.value = layout.getTotalWidth() totalWidth.value = layout.getTotalWidth()
// totalWidth iOS CSS
if (isIOS && !iosScrollPaused.value) {
stopIOSAutoScroll()
startIOSAutoScroll()
}
} else { } else {
// //
appendFailed = true appendFailed = true
@ -798,7 +774,7 @@ const handleCardClick = (card) => {
}); });
} else { } else {
// //
if (card.id) { if(card.id){
cardTapTimers[card.id] = setTimeout(() => { cardTapTimers[card.id] = setTimeout(() => {
delete cardTapTimers[card.id]; delete cardTapTimers[card.id];
uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` }); uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` });
@ -939,7 +915,7 @@ onUnmounted(() => {
try { try {
uni.offAppShow(onAppShowHandler) uni.offAppShow(onAppShowHandler)
uni.offAppHide(onAppHideHandler) uni.offAppHide(onAppHideHandler)
} catch (_) { } } catch (_) {}
} }
}) })
@ -1023,11 +999,8 @@ const loadUsersAndStartScroll = () => {
} }
@keyframes iosAutoScroll { @keyframes iosAutoScroll {
/* from { transform: translateX(0); } */ /* from { transform: translateX(0); } */
to { to { transform: translateX(var(--scroll-dist)); }
transform: translateX(var(--scroll-dist));
}
} }
.wf-card { .wf-card {
@ -1037,7 +1010,6 @@ const loadUsersAndStartScroll = () => {
transform-origin: top center; transform-origin: top center;
overflow: visible; overflow: visible;
pointer-events: visible; pointer-events: visible;
transform: translateZ(0);
} }
.wf-card:active { .wf-card:active {
@ -1047,7 +1019,6 @@ const loadUsersAndStartScroll = () => {
.wf-card-border { .wf-card-border {
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
will-change: opacity;
} }
.wf-card-img { .wf-card-img {
@ -1075,9 +1046,10 @@ const loadUsersAndStartScroll = () => {
.wf-likes-wrap { .wf-likes-wrap {
background: linear-gradient(to bottom right, background: linear-gradient(to bottom right,
#F0E4B1 0%, #F0E4B1 0%,
#F08399 50%, #F08399 50%,
#B94E73 100%); #B94E73 100%
);
border-radius: 999rpx; border-radius: 999rpx;
padding: 2rpx 10rpx; padding: 2rpx 10rpx;
display: flex; display: flex;
@ -1127,10 +1099,10 @@ const loadUsersAndStartScroll = () => {
transform: scale(1); transform: scale(1);
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: scale(1.1); transform: scale(1.1);
opacity: 0; opacity: 0;
} }
} }
</style> </style>