Skip to content

Commit bcea260

Browse files
feat: add back-to-top button (#23)
* feat: add back-to-top button
1 parent 40ce71e commit bcea260

6 files changed

Lines changed: 136 additions & 1 deletion

File tree

components/BackToTop.vue

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<template>
2+
<Transition name="back-to-top">
3+
<button
4+
v-show="isVisible"
5+
class="back-to-top-btn"
6+
@click="scrollToTop"
7+
:aria-label="$t('tooltip.back_to_top')"
8+
:title="$t('tooltip.back_to_top')"
9+
>
10+
<div class="i-ri-arrow-up-line"></div>
11+
</button>
12+
</Transition>
13+
</template>
14+
15+
<script setup lang="ts">
16+
import { ref, onMounted, onUnmounted } from 'vue'
17+
import { useI18n } from 'vue-i18n'
18+
19+
const { t } = useI18n()
20+
21+
const isVisible = ref(false)
22+
const scrollThreshold = 300
23+
24+
const handleScroll = () => {
25+
if (typeof window !== 'undefined') {
26+
isVisible.value = window.scrollY > scrollThreshold
27+
}
28+
}
29+
30+
const scrollToTop = () => {
31+
if (typeof window !== 'undefined') {
32+
window.scrollTo({
33+
top: 0,
34+
behavior: 'smooth',
35+
})
36+
}
37+
}
38+
39+
onMounted(() => {
40+
if (typeof window !== 'undefined') {
41+
window.addEventListener('scroll', handleScroll, { passive: true })
42+
handleScroll()
43+
}
44+
})
45+
46+
onUnmounted(() => {
47+
if (typeof window !== 'undefined') {
48+
window.removeEventListener('scroll', handleScroll)
49+
}
50+
})
51+
</script>
52+
53+
<style scoped>
54+
.back-to-top-btn {
55+
position: fixed;
56+
bottom: 2rem;
57+
right: 2rem;
58+
z-index: var(--pr-z-nav);
59+
width: 3rem;
60+
height: 3rem;
61+
border-radius: 50%;
62+
border: 1px solid var(--pr-c-divider-light);
63+
background: var(--pr-c-bg-soft);
64+
backdrop-filter: blur(8px);
65+
cursor: pointer;
66+
display: flex;
67+
align-items: center;
68+
justify-content: center;
69+
transition: all 0.3s ease;
70+
color: var(--pr-c-text-2);
71+
}
72+
73+
.back-to-top-btn:hover {
74+
background: var(--pr-c-bg);
75+
transform: translateY(-2px);
76+
color: var(--pr-c-text-1);
77+
border-color: var(--pr-c-brand);
78+
}
79+
80+
.back-to-top-btn:active {
81+
transform: translateY(0);
82+
}
83+
84+
.back-to-top-btn .i-ri-arrow-up-line {
85+
width: 1.25rem;
86+
height: 1.25rem;
87+
font-size: 1.25rem;
88+
}
89+
90+
.back-to-top-enter-active,
91+
.back-to-top-leave-active {
92+
transition: all 0.3s ease;
93+
}
94+
95+
.back-to-top-enter-from,
96+
.back-to-top-leave-to {
97+
opacity: 0;
98+
transform: translateY(20px) scale(0.8);
99+
}
100+
101+
.back-to-top-enter-to,
102+
.back-to-top-leave-from {
103+
opacity: 1;
104+
transform: translateY(0) scale(1);
105+
}
106+
107+
@media (max-width: 768px) {
108+
.back-to-top-btn {
109+
bottom: 1.5rem;
110+
right: 1.5rem;
111+
width: 2.75rem;
112+
height: 2.75rem;
113+
}
114+
115+
.back-to-top-btn .i-ri-arrow-up-line {
116+
width: 1.125rem;
117+
height: 1.125rem;
118+
font-size: 1.125rem;
119+
}
120+
}
121+
122+
@media (min-width: 1200px) {
123+
.back-to-top-btn {
124+
right: 2rem;
125+
}
126+
}
127+
</style>

layouts/default.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { computed } from 'vue'
66
import { useDark } from '@vueuse/core'
77
import AutoLanguageDetector from '../components/AutoLanguageDetector.vue'
8+
import BackToTop from '../components/BackToTop.vue'
89
910
const { t, locale } = useI18n()
1011
const siteConfig = useSiteConfig()
@@ -74,6 +75,8 @@
7475
</template>
7576
</component>
7677
</RouterView>
78+
79+
<BackToTop />
7780
</Layout>
7881
</template>
7982

layouts/home.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88

99
<script setup lang="ts">
1010
import AutoLanguageDetector from '../components/AutoLanguageDetector.vue'
11+
import BackToTop from '../components/BackToTop.vue'
1112
</script>
1213

1314
<template>
1415
<Layout>
1516
<AutoLanguageDetector />
1617

1718
<PressHome />
19+
20+
<BackToTop />
1821
</Layout>
1922
</template>

locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ wip: WIP, please stay tuned
4646
tooltip:
4747
# Reserved and used by valaxy-theme-press
4848
edit_this_page: Edit this page on GitHub
49+
back_to_top: Back to Top
4950

5051
post:
5152
# Reserved and used by valaxy-theme-press

locales/zh-CN.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ wip: 编写中,敬请期待
4646
tooltip:
4747
# Reserved and used by valaxy-theme-press
4848
edit_this_page: 在 GitHub 上编辑此页
49+
back_to_top: 回到顶部
4950

5051
post:
5152
# Reserved and used by valaxy-theme-press

valaxy.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const DOCS_BUILT_DATE = new Date().toISOString()
1414
const SITE_URL = process.env.SITE_URL || 'https://example.com'
1515
const VITE_BASE = process.env.VITE_BASE || '/'
1616

17-
const safelist = ['i-ri-home-line', 'i-ri-github-line']
17+
const safelist = ['i-ri-home-line', 'i-ri-github-line', 'i-ri-arrow-up-line']
1818

1919
export default defineValaxyConfig<PressTheme.Config>({
2020
siteConfig: {

0 commit comments

Comments
 (0)