feat: Enhance scrolling behavior with pause before refresh

- Introduced a pause mechanism at the end of scrolling for both Ladder and Ranking boards, allowing the last item to be fully visible before triggering a data refresh.
- Updated scrolling logic to prevent immediate resets, improving user experience during data loading.
- Added a consistent pause duration across components to standardize behavior during scrolling interactions.
This commit is contained in:
Ethanfly 2026-02-10 10:50:41 +08:00
parent f861f82675
commit ccea3a99e5
5 changed files with 127 additions and 15 deletions

View File

@ -243,6 +243,7 @@ const boardsScrollY = ref(0);
const matchesScrollY = ref(0);
const REFRESH_COOLDOWN_MS = 3000;
const PAUSE_AT_END_MS = 2800; //
let lastRefreshAt = 0;
const onScrollEndRefresh = () => {
@ -360,6 +361,8 @@ const startBoardsScroll = () => {
if (!boardsScrollEl.value || !boardsGroup1.value) return;
if (loadingBoards.value) return;
let boardsPauseScheduled = false;
const tick = () => {
if (!boardsScrollEl.value || !boardsGroup1.value) {
boardsRaf = requestAnimationFrame(tick);
@ -369,6 +372,10 @@ const startBoardsScroll = () => {
boardsRaf = requestAnimationFrame(tick);
return;
}
if (boardsPauseScheduled) {
boardsRaf = requestAnimationFrame(tick);
return;
}
const h = boardsGroup1.value.offsetHeight;
const containerH = boardsScrollEl.value.offsetHeight;
@ -380,8 +387,15 @@ const startBoardsScroll = () => {
boardsScrollY.value -= 0.35;
if (Math.abs(boardsScrollY.value) >= h) {
boardsScrollY.value = 0;
boardsScrollY.value = -h;
if (!boardsPauseScheduled) {
boardsPauseScheduled = true;
setTimeout(() => {
onScrollEndRefresh();
boardsScrollY.value = 0;
boardsPauseScheduled = false;
}, PAUSE_AT_END_MS);
}
}
boardsRaf = requestAnimationFrame(tick);
};
@ -394,6 +408,8 @@ const startMatchesScroll = () => {
if (!matchesScrollEl.value || !matchesGroup1.value) return;
if (loadingMatches.value) return;
let matchesPauseScheduled = false;
const tick = () => {
if (!matchesScrollEl.value || !matchesGroup1.value) {
matchesRaf = requestAnimationFrame(tick);
@ -403,6 +419,10 @@ const startMatchesScroll = () => {
matchesRaf = requestAnimationFrame(tick);
return;
}
if (matchesPauseScheduled) {
matchesRaf = requestAnimationFrame(tick);
return;
}
const h = matchesGroup1.value.offsetHeight;
const containerH = matchesScrollEl.value.offsetHeight;
@ -414,8 +434,15 @@ const startMatchesScroll = () => {
matchesScrollY.value -= 0.4;
if (Math.abs(matchesScrollY.value) >= h) {
matchesScrollY.value = 0;
matchesScrollY.value = -h;
if (!matchesPauseScheduled) {
matchesPauseScheduled = true;
setTimeout(() => {
onScrollEndRefresh();
matchesScrollY.value = 0;
matchesPauseScheduled = false;
}, PAUSE_AT_END_MS);
}
}
matchesRaf = requestAnimationFrame(tick);
};

View File

@ -243,6 +243,7 @@ const boardsScrollY = ref(0);
const matchesScrollY = ref(0);
const REFRESH_COOLDOWN_MS = 3000;
const PAUSE_AT_END_MS = 2800; //
let lastRefreshAt = 0;
const onScrollEndRefresh = () => {
@ -360,6 +361,8 @@ const startBoardsScroll = () => {
if (!boardsScrollEl.value || !boardsGroup1.value) return;
if (loadingBoards.value) return;
let boardsPauseScheduled = false;
const tick = () => {
if (!boardsScrollEl.value || !boardsGroup1.value) {
boardsRaf = requestAnimationFrame(tick);
@ -369,6 +372,10 @@ const startBoardsScroll = () => {
boardsRaf = requestAnimationFrame(tick);
return;
}
if (boardsPauseScheduled) {
boardsRaf = requestAnimationFrame(tick);
return;
}
const h = boardsGroup1.value.offsetHeight;
const containerH = boardsScrollEl.value.offsetHeight;
@ -380,8 +387,15 @@ const startBoardsScroll = () => {
boardsScrollY.value -= 0.35;
if (Math.abs(boardsScrollY.value) >= h) {
boardsScrollY.value = 0;
boardsScrollY.value = -h;
if (!boardsPauseScheduled) {
boardsPauseScheduled = true;
setTimeout(() => {
onScrollEndRefresh();
boardsScrollY.value = 0;
boardsPauseScheduled = false;
}, PAUSE_AT_END_MS);
}
}
boardsRaf = requestAnimationFrame(tick);
};
@ -394,6 +408,8 @@ const startMatchesScroll = () => {
if (!matchesScrollEl.value || !matchesGroup1.value) return;
if (loadingMatches.value) return;
let matchesPauseScheduled = false;
const tick = () => {
if (!matchesScrollEl.value || !matchesGroup1.value) {
matchesRaf = requestAnimationFrame(tick);
@ -403,6 +419,10 @@ const startMatchesScroll = () => {
matchesRaf = requestAnimationFrame(tick);
return;
}
if (matchesPauseScheduled) {
matchesRaf = requestAnimationFrame(tick);
return;
}
const h = matchesGroup1.value.offsetHeight;
const containerH = matchesScrollEl.value.offsetHeight;
@ -414,8 +434,15 @@ const startMatchesScroll = () => {
matchesScrollY.value -= 0.4;
if (Math.abs(matchesScrollY.value) >= h) {
matchesScrollY.value = 0;
matchesScrollY.value = -h;
if (!matchesPauseScheduled) {
matchesPauseScheduled = true;
setTimeout(() => {
onScrollEndRefresh();
matchesScrollY.value = 0;
matchesPauseScheduled = false;
}, PAUSE_AT_END_MS);
}
}
matchesRaf = requestAnimationFrame(tick);
};

View File

@ -500,6 +500,7 @@
const scrollYMale = ref(0);
const scrollYFemale = ref(0);
const REFRESH_COOLDOWN_MS = 3000;
const PAUSE_AT_END_MS = 2800; //
let lastRefreshAt = 0;
//
@ -518,22 +519,37 @@
const startContinuousScroll = (listRef, scrollYRef, reqIdRef) => {
if (reqIdRef.value) cancelAnimationFrame(reqIdRef.value);
let pauseScheduled = false;
const scroll = () => {
if (isRefreshing.value || listRef.value.length === 0) {
reqIdRef.value = requestAnimationFrame(scroll);
return;
}
//
if (pauseScheduled) {
reqIdRef.value = requestAnimationFrame(scroll);
return;
}
// (使)
scrollYRef.value -= 0.6;
// 100 * (90px + 12px)
const singleSetHeight = listRef.value.length * 102;
// 0
//
if (Math.abs(scrollYRef.value) >= singleSetHeight) {
scrollYRef.value = 0;
scrollYRef.value = -singleSetHeight;
if (!pauseScheduled) {
pauseScheduled = true;
setTimeout(() => {
onScrollEndRefresh();
scrollYRef.value = 0;
pauseScheduled = false;
}, PAUSE_AT_END_MS);
}
}
reqIdRef.value = requestAnimationFrame(scroll);

View File

@ -549,6 +549,7 @@
const scrollYMale = ref(0);
const scrollYFemale = ref(0);
const REFRESH_COOLDOWN_MS = 3000;
const PAUSE_AT_END_MS = 2800; //
let lastRefreshAt = 0;
const updateTime = () => {
@ -564,19 +565,33 @@
const startContinuousScroll = (listRef, scrollYRef, reqIdRef, groupRef) => {
if (reqIdRef.value) cancelAnimationFrame(reqIdRef.value);
let pauseScheduled = false;
const scroll = () => {
if (isRefreshing.value || listRef.value.length <= 3 || !groupRef.value) {
reqIdRef.value = requestAnimationFrame(scroll);
return;
}
if (pauseScheduled) {
reqIdRef.value = requestAnimationFrame(scroll);
return;
}
scrollYRef.value -= 0.6;
const singleSetHeight = groupRef.value.offsetHeight + 12;
if (Math.abs(scrollYRef.value) >= singleSetHeight) {
scrollYRef.value = 0;
scrollYRef.value = -singleSetHeight;
if (!pauseScheduled) {
pauseScheduled = true;
setTimeout(() => {
onScrollEndRefresh();
scrollYRef.value = 0;
pauseScheduled = false;
}, PAUSE_AT_END_MS);
}
}
reqIdRef.value = requestAnimationFrame(scroll);

View File

@ -226,6 +226,12 @@ class UserController {
updateData.gender = normalizedGender;
await existingUser.update(updateData);
if (nickname) {
await LadderUser.update(
{ real_name: nickname },
{ where: { user_id: existingUser.id } },
);
}
user = existingUser;
} else {
// 新用户注册
@ -260,7 +266,12 @@ class UserController {
updateData.gender = normalizedGender;
await user.update(updateData);
if (nickname) {
await LadderUser.update(
{ real_name: nickname },
{ where: { user_id: user.id } },
);
}
// 如果手机号变化,重新关联天梯用户
await this.linkLadderUsers(user.id, phone);
}
@ -371,6 +382,14 @@ class UserController {
await user.update(updateData);
// 用户修改昵称时,同步更新该用户下所有天梯用户的 real_name保持两边一致
if (nickname) {
await LadderUser.update(
{ real_name: nickname },
{ where: { user_id: user.id } },
);
}
res.json(
success(
{
@ -447,13 +466,21 @@ class UserController {
const { nickname, avatar, phone, gender } = req.body;
const user = req.user;
const newNickname = nickname || user.nickname;
await user.update({
nickname: nickname || user.nickname,
nickname: newNickname,
avatar: avatar || user.avatar,
phone: phone || user.phone,
gender: gender !== undefined ? gender : user.gender,
});
if (nickname) {
await LadderUser.update(
{ real_name: nickname },
{ where: { user_id: user.id } },
);
}
res.json(success(null, "更新成功"));
} catch (err) {
console.error("更新用户信息失败:", err);