|
1 | 1 | <script setup lang="ts" name="HomeBanner">
|
2 | 2 | import { useDesign } from "../hooks";
|
3 |
| -import { useData } from "vitepress"; |
4 |
| -import { onMounted, onUnmounted, unref } from "vue"; |
| 3 | +import { withBase } from "vitepress"; |
| 4 | +import { onMounted, onUnmounted, unref, ref, nextTick } from "vue"; |
5 | 5 | import { useTypes } from "../hooks";
|
| 6 | +import { useUnrefData } from "../configProvider"; |
| 7 | +import { isNumber } from "../helper"; |
| 8 | +import HomeBannerWaves from "./HomeBannerWaves.vue"; |
6 | 9 |
|
7 | 10 | const { getPrefixClass } = useDesign();
|
8 | 11 | const prefixClass = getPrefixClass("banner");
|
9 | 12 |
|
10 |
| -const { site, frontmatter } = useData(); |
11 |
| -
|
12 |
| -const title = unref(frontmatter).name || unref(site).title || ""; |
13 |
| -const descArray = [...new Set(unref(frontmatter).tk?.description?.filter((v: string) => !!v))] as string[]; |
14 |
| -
|
15 |
| -const { text, shouldAnimate, startTypes, stopTypes } = useTypes(descArray, { |
16 |
| - typesInTime: unref(frontmatter).tk?.typesInTime, |
17 |
| - typesOutTime: unref(frontmatter).tk?.typesOutTime, |
18 |
| - typesNextTime: unref(frontmatter).tk?.typesNextTime, |
19 |
| -}); |
| 13 | +const { site, theme, frontmatter } = useUnrefData(); |
| 14 | +
|
| 15 | +const title = frontmatter.name || site.title || ""; |
| 16 | +const descArray = [...new Set(frontmatter.tk?.description?.filter((v: string) => !!v))] as string[]; |
| 17 | +const { |
| 18 | + bgStyle = "default", |
| 19 | + bigImgSrc, |
| 20 | + maskBg = "rgba(0,0,0,0.4)", |
| 21 | + defaultBgColor = "#e5e5e5", |
| 22 | + defaultTextColor = "#000000", |
| 23 | + features = [], |
| 24 | + typesInTime = 200, |
| 25 | + typesOutTime = 100, |
| 26 | + typesNextTime = 800, |
| 27 | + titleFontSize = "3.2rem", |
| 28 | + descFontSize = "1.4rem", |
| 29 | +} = { ...theme.banner, ...frontmatter.tk }; |
| 30 | +
|
| 31 | +const isDefaultStyle = bgStyle === "default"; |
| 32 | +const isBigImgStyle = bgStyle === "bigImg"; |
| 33 | +const isGridStyle = bgStyle === "grid"; |
| 34 | +
|
| 35 | +const getStyle = () => { |
| 36 | + let baseStyle = { "--banner-title-text": titleFontSize, "--banner-desc-text": descFontSize }; |
| 37 | +
|
| 38 | + if (isDefaultStyle) return { ...baseStyle, backgroundColor: defaultBgColor, "--banner-text-color": defaultTextColor }; |
| 39 | + if (isGridStyle) { |
| 40 | + return { |
| 41 | + ...baseStyle, |
| 42 | + background: |
| 43 | + "rgb(40,40,45) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABOSURBVFhH7c6xCQAgDAVRR9A6E4hLu4uLiWJ7tSnuQcIvr2TRYsw3/zOGGEOMIcYQY4gxxBhiDDGGGEOMIcYQY4gxxBhiDLkx52W4Gn1tuslCtHJvL54AAAAASUVORK5CYII=)", |
| 44 | + "--banner-text-color": "#ffffff", |
| 45 | + }; |
| 46 | + } |
| 47 | + if (isBigImgStyle) { |
| 48 | + return { |
| 49 | + ...baseStyle, |
| 50 | + backgroundImage: bigImgSrc ? `url(${bigImgSrc})` : "", |
| 51 | + "--banner-text-color": "#ffffff", |
| 52 | + "--banner-mask-bg-color": isNumber(maskBg) ? `rgba(0, 0, 0, ${maskBg})` : maskBg, |
| 53 | + }; |
| 54 | + } |
| 55 | +}; |
20 | 56 |
|
21 | 57 | const bannerRef = ref<HTMLElement | null>(null);
|
22 | 58 |
|
23 | 59 | const watchScroll = () => {
|
24 | 60 | const vPNavDom = document.querySelector(".VPNavBar");
|
25 | 61 | // 获取窗口高度
|
26 | 62 | const windowH = unref(bannerRef)?.clientHeight;
|
| 63 | +
|
27 | 64 | if (!vPNavDom || !windowH) return;
|
28 | 65 | const toggleClass = () => {
|
29 |
| - if (document.documentElement.scrollTop < windowH) vPNavDom.classList.add("big-img-nav-bar"); |
30 |
| - else vPNavDom.classList.remove("big-img-nav-bar"); |
| 66 | + if (unref(bannerRef) && document.documentElement.scrollTop + 100 < windowH) { |
| 67 | + vPNavDom.classList.add("big-img-nav-bar"); |
| 68 | + } else vPNavDom.classList.remove("big-img-nav-bar"); |
31 | 69 | };
|
32 | 70 |
|
33 | 71 | toggleClass();
|
34 |
| -
|
35 | 72 | window.onscroll = () => {
|
36 | 73 | toggleClass();
|
37 | 74 | };
|
38 | 75 | };
|
39 | 76 |
|
| 77 | +// 打字效果 |
| 78 | +const { text, shouldAnimate, startTypes, stopTypes } = useTypes(descArray, { |
| 79 | + typesInTime, |
| 80 | + typesOutTime, |
| 81 | + typesNextTime, |
| 82 | +}); |
| 83 | +
|
40 | 84 | onMounted(() => {
|
41 | 85 | startTypes();
|
| 86 | + if (isBigImgStyle) nextTick(() => watchScroll()); |
42 | 87 | });
|
43 | 88 |
|
44 | 89 | onUnmounted(() => {
|
45 | 90 | stopTypes();
|
| 91 | + window.onscroll = null; |
46 | 92 | });
|
47 | 93 | </script>
|
48 | 94 |
|
49 | 95 | <template>
|
50 |
| - <div ref="bannerRef" :class="`${prefixClass} big-img`" :style="{ backgroundImage: bigImg ? `url(${bigImg})` : '' }"> |
51 |
| - <div class="mask" /> |
52 |
| - |
53 |
| - <div :class="`${prefixClass}-content`"> |
| 96 | + <div |
| 97 | + ref="bannerRef" |
| 98 | + :class="[prefixClass, { default: isDefaultStyle, 'big-img': isBigImgStyle, grid: isGridStyle }]" |
| 99 | + :style="getStyle()" |
| 100 | + > |
| 101 | + <div v-if="isBigImgStyle" class="mask" /> |
| 102 | + |
| 103 | + <div :class="[`${prefixClass}-content`, { center: isBigImgStyle || !features.length }]"> |
54 | 104 | <h1 :class="`${prefixClass}-content__title`">{{ title }}</h1>
|
55 | 105 | <p v-if="descArray.length" :class="`${prefixClass}-content__desc`">
|
56 | 106 | <span>{{ text }}</span>
|
57 | 107 | <span :class="['typed', { 'is-animation': shouldAnimate }]">|</span>
|
58 | 108 | </p>
|
59 | 109 | </div>
|
| 110 | + |
| 111 | + <div v-if="features.length && !isBigImgStyle" :class="`${prefixClass}-feature flx-wrap-between`"> |
| 112 | + <div :class="`${prefixClass}-feature__item`" v-for="(feature, index) in features" :key="index"> |
| 113 | + <a v-if="feature.link" :href="feature.link" class="flx-column-center"> |
| 114 | + <img v-if="feature.imgUrl" class="feature-img" :src="withBase(feature.imgUrl)" :alt="feature.title" /> |
| 115 | + <p class="feature-title">{{ feature.title }}</p> |
| 116 | + <p class="feature-description">{{ feature.description }}</p> |
| 117 | + </a> |
| 118 | + </div> |
| 119 | + </div> |
60 | 120 | </div>
|
| 121 | + <HomeBannerWaves v-if="isBigImgStyle" /> |
61 | 122 | </template>
|
62 | 123 |
|
63 | 124 | <style lang="scss" scoped>
|
64 | 125 | @use "../styles/components/homeBanner.scss";
|
65 | 126 | </style>
|
66 | 127 |
|
67 | 128 | <style lang="scss">
|
| 129 | +// 大图风格时,指定顶部导航栏样式 |
68 | 130 | .VPNavBar.home.big-img-nav-bar {
|
69 | 131 | background-color: transparent !important;
|
70 | 132 |
|
71 | 133 | .VPNavBarTitle .title,
|
72 | 134 | .VPNavBarMenuLink,
|
73 |
| - .VPNavBarMenuGroup .text { |
| 135 | + .VPNavBarMenuGroup .text, |
| 136 | + .VPSocialLink { |
74 | 137 | color: #ffffff;
|
| 138 | +
|
75 | 139 | &.active,
|
76 | 140 | &:hover {
|
77 | 141 | color: var(--tk-theme-color);
|
|
0 commit comments