Add slower smooth scrolling and scroll to explore indicator
All checks were successful
build-website / build (push) Successful in 1m30s

Features added:
- Custom smooth scroll composable with adjustable duration (1.5 seconds)
- Replaced native browser smooth scroll with custom implementation
- Added 'Scroll to explore' text with chevron at bottom of hero
- Animated bounce effect for scroll indicator
- Chevron icon with subtle animation
- Click handler to scroll to services section
- Hidden on mobile for cleaner experience

All anchor links now scroll more slowly and smoothly for better UX.
This commit is contained in:
2025-09-19 11:18:25 +02:00
parent bcb729c017
commit e0794df092
3 changed files with 129 additions and 15 deletions

View File

@@ -36,7 +36,9 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useSmoothScroll } from '~/composables/useSmoothScroll'
const { scrollToElement, scrollToTop: smoothScrollToTop } = useSmoothScroll()
const isScrolled = ref(false)
const navLinks = [
@@ -62,17 +64,12 @@ const handleSmoothScroll = (event: Event, href: string) => {
return
}
const target = document.querySelector(href)
if (!target) {
return
}
event.preventDefault()
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
scrollToElement(href, 1500) // 1.5 seconds for slower scrolling
}
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
smoothScrollToTop(1500) // 1.5 seconds for slower scrolling
}
onMounted(() => {

View File

@@ -65,6 +65,12 @@
View Our Services
</button>
</div>
<!-- Scroll to explore indicator -->
<div class="scroll-to-explore" @click="handleScrollToExplore">
<span>Scroll to explore</span>
<LucideChevronDown class="chevron-icon" />
</div>
</div>
</section>
</template>
@@ -74,7 +80,9 @@ import { ref, onMounted } from 'vue'
import { useParallax } from '~/composables/useParallax'
import { useIntersectionAnimations } from '~/composables/useIntersectionAnimations'
import { useRipple } from '~/composables/useRipple'
import { useSmoothScroll } from '~/composables/useSmoothScroll'
const { scrollToElement } = useSmoothScroll()
const videoLoaded = ref(false)
const videoContainer = ref<HTMLElement | null>(null)
const videoElement = ref<HTMLVideoElement | null>(null)
@@ -93,10 +101,11 @@ const handlePhoneClick = () => {
}
const handleServicesClick = () => {
const element = document.querySelector('#services')
if (element) {
element.scrollIntoView({ behavior: 'smooth' })
scrollToElement('#services', 1500) // 1.5 seconds for slower scrolling
}
const handleScrollToExplore = () => {
scrollToElement('#services', 1500) // Scroll to services section
}
const handleVideoVisibility = () => {
@@ -127,10 +136,7 @@ const initSmoothScroll = () => {
if (!href) {
return
}
const target = document.querySelector(href)
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
scrollToElement(href, 1500) // 1.5 seconds for slower scrolling
})
})
}
@@ -210,4 +216,67 @@ onMounted(() => {
overflow-x: hidden !important;
max-width: 100vw !important;
}
/* Scroll to explore indicator */
.scroll-to-explore {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
cursor: pointer;
color: white;
opacity: 0.8;
transition: all 0.3s ease;
animation: bounce 2s infinite;
}
.scroll-to-explore:hover {
opacity: 1;
transform: translateX(-50%) translateY(-3px);
}
.scroll-to-explore span {
font-size: 0.875rem;
font-weight: 300;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.chevron-icon {
width: 24px;
height: 24px;
animation: chevronBounce 2s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateX(-50%) translateY(0);
}
40% {
transform: translateX(-50%) translateY(-10px);
}
60% {
transform: translateX(-50%) translateY(-5px);
}
}
@keyframes chevronBounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(5px);
}
}
/* Hide on mobile for better UX */
@media (max-width: 768px) {
.scroll-to-explore {
display: none;
}
}
</style>

View File

@@ -0,0 +1,48 @@
export const useSmoothScroll = () => {
const smoothScrollTo = (target: number | Element, duration: number = 1200) => {
const targetPosition = typeof target === 'number'
? target
: target.getBoundingClientRect().top + window.pageYOffset
const startPosition = window.pageYOffset
const distance = targetPosition - startPosition
let startTime: number | null = null
const animation = (currentTime: number) => {
if (startTime === null) startTime = currentTime
const timeElapsed = currentTime - startTime
const progress = Math.min(timeElapsed / duration, 1)
// Easing function for smooth deceleration
const easeInOutCubic = (t: number) => {
return t < 0.5
? 4 * t * t * t
: 1 - Math.pow(-2 * t + 2, 3) / 2
}
window.scrollTo(0, startPosition + distance * easeInOutCubic(progress))
if (progress < 1) {
requestAnimationFrame(animation)
}
}
requestAnimationFrame(animation)
}
const scrollToElement = (selector: string, duration: number = 1200) => {
const element = document.querySelector(selector)
if (element) {
smoothScrollTo(element, duration)
}
}
const scrollToTop = (duration: number = 1200) => {
smoothScrollTo(0, duration)
}
return {
smoothScrollTo,
scrollToElement,
scrollToTop
}
}