Skip to content

Commit 9f4b142

Browse files
committed
Add interactive cursor-tracking gradient animation to hero section
1 parent 4de0c20 commit 9f4b142

3 files changed

Lines changed: 124 additions & 8 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<template>
2+
<div ref="gradientContainer" class="cursor-gradient-container"></div>
3+
</template>
4+
5+
<script setup lang="ts">
6+
import { onMounted, onUnmounted, ref } from 'vue'
7+
8+
const gradientContainer = ref<HTMLElement>()
9+
10+
let mouseX = 0
11+
let mouseY = 0
12+
let currentX = 0
13+
let currentY = 0
14+
let animationFrame: number
15+
16+
const lerp = (start: number, end: number, factor: number) => {
17+
return start + (end - start) * factor
18+
}
19+
20+
const updateGradient = () => {
21+
// Smooth interpolation for fluid movement
22+
currentX = lerp(currentX, mouseX, 0.1)
23+
currentY = lerp(currentY, mouseY, 0.1)
24+
25+
// Update CSS custom properties
26+
document.documentElement.style.setProperty('--cursor-x', `${currentX}%`)
27+
document.documentElement.style.setProperty('--cursor-y', `${currentY}%`)
28+
29+
animationFrame = requestAnimationFrame(updateGradient)
30+
}
31+
32+
const handleMouseMove = (e: MouseEvent) => {
33+
// Calculate mouse position as percentage
34+
mouseX = (e.clientX / window.innerWidth) * 100
35+
mouseY = (e.clientY / window.innerHeight) * 100
36+
}
37+
38+
onMounted(() => {
39+
// Set initial position to center
40+
mouseX = 50
41+
mouseY = 50
42+
currentX = 50
43+
currentY = 50
44+
45+
// Start animation loop
46+
updateGradient()
47+
48+
// Add mouse move listener
49+
window.addEventListener('mousemove', handleMouseMove)
50+
51+
// Add touch support for mobile
52+
window.addEventListener('touchmove', (e) => {
53+
if (e.touches.length > 0) {
54+
const touch = e.touches[0]
55+
mouseX = (touch.clientX / window.innerWidth) * 100
56+
mouseY = (touch.clientY / window.innerHeight) * 100
57+
}
58+
})
59+
})
60+
61+
onUnmounted(() => {
62+
window.removeEventListener('mousemove', handleMouseMove)
63+
if (animationFrame) {
64+
cancelAnimationFrame(animationFrame)
65+
}
66+
})
67+
</script>
68+
69+
<style scoped>
70+
.cursor-gradient-container {
71+
position: fixed;
72+
top: 0;
73+
left: 0;
74+
width: 100%;
75+
height: 100%;
76+
pointer-events: none;
77+
z-index: -1;
78+
}
79+
</style>

docs/.vitepress/theme/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import DefaultTheme from 'vitepress/theme'
55
import './style.css'
66
import 'virtual:group-icons.css'
77
import FeatureCards from './components/FeatureCards.vue'
8+
import CursorGradient from './components/CursorGradient.vue'
89
import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client'
910

1011
export default {
@@ -13,11 +14,13 @@ export default {
1314
return h(DefaultTheme.Layout, null, {
1415
// https://vitepress.dev/guide/extending-default-theme#layout-slots
1516
// 'nav-bar-content-after': () => h(GitHubStars)
17+
'layout-top': () => h(CursorGradient)
1618
})
1719
},
1820
enhanceApp({ app, router, siteData }) {
1921
// Register components globally if needed
2022
app.component('FeatureCards', FeatureCards)
23+
app.component('CursorGradient', CursorGradient)
2124
enhanceAppWithTabs(app)
2225
}
2326
} satisfies Theme

docs/.vitepress/theme/style.css

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,26 @@
9191
* -------------------------------------------------------------------------- */
9292

9393
:root {
94+
/* Cursor tracking variables */
95+
--cursor-x: 50%;
96+
--cursor-y: 50%;
97+
9498
--vp-home-hero-name-color: transparent;
95-
--vp-home-hero-name-background: -webkit-linear-gradient(
96-
120deg,
97-
rgb(250, 52, 59) 30%,
98-
rgb(255, 249, 245)
99+
--vp-home-hero-name-background: radial-gradient(
100+
circle at var(--cursor-x) var(--cursor-y),
101+
rgb(255, 249, 245) 0%,
102+
rgb(255, 100, 100) 20%,
103+
rgb(250, 52, 59) 40%,
104+
rgb(255, 100, 100) 60%,
105+
rgb(250, 52, 59) 100%
99106
);
100107

101-
--vp-home-hero-image-background-image: linear-gradient(
102-
-45deg,
103-
rgb(250, 52, 59) 50%,
104-
rgb(255, 249, 245) 50%
108+
--vp-home-hero-image-background-image: radial-gradient(
109+
circle at var(--cursor-x) var(--cursor-y),
110+
rgb(255, 249, 245) 0%,
111+
rgb(255, 100, 100) 30%,
112+
rgb(250, 52, 59) 60%,
113+
rgb(255, 100, 100) 100%
105114
);
106115
--vp-home-hero-image-filter: blur(44px);
107116
}
@@ -182,3 +191,28 @@
182191
border-top-left-radius: 0 !important;
183192
border-top-right-radius: 0 !important;
184193
}
194+
195+
/**
196+
* Hero Cursor Tracking
197+
* -------------------------------------------------------------------------- */
198+
199+
.VPHero .name {
200+
background: var(--vp-home-hero-name-background);
201+
-webkit-background-clip: text;
202+
background-clip: text;
203+
transition: background 0.3s ease;
204+
}
205+
206+
.VPHero .image-bg {
207+
background: var(--vp-home-hero-image-background-image);
208+
transition: background 0.3s ease;
209+
}
210+
211+
/* Add hover effect for enhanced interactivity */
212+
.VPHero:hover .name {
213+
filter: brightness(1.1);
214+
}
215+
216+
.VPHero:hover .image-bg {
217+
filter: var(--vp-home-hero-image-filter) brightness(1.05);
218+
}

0 commit comments

Comments
 (0)