Ver código fonte

feat: 新增 scrollreveal hooks

Lee 2 dias atrás
pai
commit
e4166749bf

+ 4 - 2
app/components/Header.vue

@@ -2,7 +2,7 @@
  * @Author: LiZhiWei
  * @Date: 2026-01-13 15:41:49
  * @LastEditors: LiZhiWei
- * @LastEditTime: 2026-01-22 10:21:52
+ * @LastEditTime: 2026-01-22 10:51:05
  * @Description: 
 -->
 <!--
@@ -12,7 +12,9 @@
 <template>
   <header class="landing-header" :class="{ 'is-scrolled': y > 0 }">
     <div class="landing-container header-content">
-      <i class="header-logo"></i>
+      <NuxtLink to="/">
+        <i class="header-logo"></i>
+      </NuxtLink>
 
       <nav class="hidden sm:flex items-center gap-52px text-16px text-#334155 h-full">
         <NuxtLink to="/" class="nav-link flex items-center">首页</NuxtLink>

+ 2 - 19
app/components/section/Ability.vue

@@ -3,7 +3,7 @@
     id="ability"
     ref="abilityRef"
     class="ability transition-all duration-1000 ease-out"
-    :class="[isAbilityVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-100px']"
+    :class="[isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-100px']"
   >
     <div class="ability-title pf-sc-semibold">一体化智慧解决方案</div>
     <div class="ability-desc pf-sc-regular">
@@ -58,7 +58,7 @@
   import { abilityTabs, abilities } from '@/constants/common'
 
   const abilityRef = ref<HTMLElement | null>(null)
-  const isAbilityVisible = ref(false)
+  const { isVisible } = useScrollReveal(abilityRef)
 
   const activeAbilityTab = ref<AbilityTab>(abilityTabs[0] as AbilityTab)
 
@@ -72,23 +72,6 @@
   const handleAbilityTabClick = (tab: AbilityTab) => {
     activeAbilityTab.value = tab
   }
-
-  onMounted(() => {
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (!entry.isIntersecting) return
-          isAbilityVisible.value = true
-          observer.unobserve(entry.target)
-        })
-      },
-      { threshold: 0.1 }
-    )
-
-    if (abilityRef.value) {
-      observer.observe(abilityRef.value)
-    }
-  })
 </script>
 
 <style scoped lang="scss">

+ 1 - 18
app/components/section/Cases.vue

@@ -63,7 +63,7 @@
   import { cases } from '@/constants/common'
 
   const casesRef = ref<HTMLElement | null>(null)
-  const isCasesVisible = ref(false)
+  const { isVisible: isCasesVisible } = useScrollReveal(casesRef)
   const activeCaseIndex = ref(0)
   const casesListRef = ref<HTMLElement | null>(null)
 
@@ -89,23 +89,6 @@
       behavior: 'smooth',
     })
   }
-
-  onMounted(() => {
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (!entry.isIntersecting) return
-          isCasesVisible.value = true
-          observer.unobserve(entry.target)
-        })
-      },
-      { threshold: 0.1 }
-    )
-
-    if (casesRef.value) {
-      observer.observe(casesRef.value)
-    }
-  })
 </script>
 
 <style scoped lang="scss">

+ 1 - 18
app/components/section/History.vue

@@ -158,7 +158,7 @@
   import { historyYears } from '@/constants/common'
 
   const timelineRef = ref<HTMLElement | null>(null)
-  const isTimelineVisible = ref(false)
+  const { isVisible: isTimelineVisible } = useScrollReveal(timelineRef)
 
   const currentYearIndex = ref(historyYears.length - 1)
   const currentEventIndex = ref(0)
@@ -262,23 +262,6 @@
         : 'text-#94A3B8 group-hover:text-[#64748B]',
     ]
   }
-
-  onMounted(() => {
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (!entry.isIntersecting) return
-          isTimelineVisible.value = true
-          observer.unobserve(entry.target)
-        })
-      },
-      { threshold: 0.1 }
-    )
-
-    if (timelineRef.value) {
-      observer.observe(timelineRef.value)
-    }
-  })
 </script>
 
 <style scoped lang="scss">

+ 9 - 20
app/components/section/Partnership.vue

@@ -1,9 +1,16 @@
+<!--
+ * @Author: LiZhiWei
+ * @Date: 2026-01-20 10:51:08
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2026-01-22 10:46:24
+ * @Description: 
+-->
 <template>
   <section
     id="partnership"
     ref="partnershipRef"
     class="partnership transition-all duration-1000 ease-out"
-    :class="[isPartnershipVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-100px']"
+    :class="[isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-100px']"
   >
     <div class="partnership-header">
       <div class="partnership-line-left"></div>
@@ -30,26 +37,8 @@
 
 <script setup lang="ts">
   import { partnersRow1, partnersRow2 } from '@/constants/common'
-
   const partnershipRef = ref<HTMLElement | null>(null)
-  const isPartnershipVisible = ref(false)
-
-  onMounted(() => {
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (!entry.isIntersecting) return
-          isPartnershipVisible.value = true
-          observer.unobserve(entry.target)
-        })
-      },
-      { threshold: 0.1 }
-    )
-
-    if (partnershipRef.value) {
-      observer.observe(partnershipRef.value)
-    }
-  })
+  const { isVisible } = useScrollReveal(partnershipRef)
 </script>
 
 <style scoped lang="scss">

+ 2 - 19
app/components/section/Solution.vue

@@ -166,12 +166,12 @@
 <script setup lang="ts">
   import { solutions } from '@/constants/common'
 
+  const solutionRef = ref<HTMLElement | null>(null)
+  const { isVisible: isSolutionVisible } = useScrollReveal(solutionRef)
   const { openConsultation } = useConsultation()
 
   const activeIndex = ref(0)
   const solutionListRef = ref<HTMLElement | null>(null)
-  const solutionRef = ref<HTMLElement | null>(null)
-  const isSolutionVisible = ref(false)
 
   const handleSolutionScroll = (e: Event) => {
     const el = e.target as HTMLElement
@@ -199,23 +199,6 @@
       behavior: 'smooth',
     })
   }
-
-  onMounted(() => {
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (!entry.isIntersecting) return
-          isSolutionVisible.value = true
-          observer.unobserve(entry.target)
-        })
-      },
-      { threshold: 0.1 }
-    )
-
-    if (solutionRef.value) {
-      observer.observe(solutionRef.value)
-    }
-  })
 </script>
 
 <style scoped lang="scss">

+ 49 - 0
app/composables/useScrollReveal.ts

@@ -0,0 +1,49 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2026-01-22 10:31:06
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2026-01-22 10:45:30
+ * @Description:
+ */
+import { ref, onMounted, isRef, type Ref } from 'vue'
+
+export const useScrollReveal = (
+  targetOrThreshold: Ref<HTMLElement | null> | number = 0.1,
+  threshold = 0.1
+) => {
+  let targetRef: Ref<HTMLElement | null>
+  let actualThreshold = threshold
+
+  if (isRef(targetOrThreshold)) {
+    targetRef = targetOrThreshold
+  } else {
+    targetRef = ref(null)
+    if (typeof targetOrThreshold === 'number') {
+      actualThreshold = targetOrThreshold
+    }
+  }
+
+  const isVisible = ref(false)
+
+  onMounted(() => {
+    const observer = new IntersectionObserver(
+      (entries) => {
+        entries.forEach((entry) => {
+          if (!entry.isIntersecting) return
+          isVisible.value = true
+          observer.unobserve(entry.target)
+        })
+      },
+      { threshold: actualThreshold }
+    )
+
+    if (targetRef.value) {
+      observer.observe(targetRef.value)
+    }
+  })
+
+  return {
+    targetRef,
+    isVisible,
+  }
+}

+ 7 - 2
nuxt.config.ts

@@ -3,7 +3,7 @@
  * @Author: wjc
  * @Date: 2023-10-25 19:39:32
  * @LastEditors: LiZhiWei
- * @LastEditTime: 2026-01-21 11:17:45
+ * @LastEditTime: 2026-01-22 10:30:54
  * @Description:
  */
 import { loadEnv } from 'vite'
@@ -38,7 +38,12 @@ export default defineNuxtConfig({
     typedPages: false,
   },
   imports: {
-    dirs: [resolve('./stores'), '~/stores', '~/components', '~/components/section'],
+    dirs: [
+      resolve('./app/stores'),
+      resolve('./app/composables'),
+      resolve('./app/components'),
+      resolve('./app/components/section'),
+    ],
   },
   css: ['@unocss/reset/tailwind.css', '~/assets/scss/common.scss'],
   vite: {