dml_frontend/components/HeroBox.vue
2025-07-08 12:35:23 +02:00

210 lines
4.3 KiB
Vue

// components/HeroBox.vue
<template>
<section
ref="heroBoxRef"
class="heroBox"
:class="{ 'dark': darkBackground, 'light': !darkBackground }"
:aria-label="ariaLabel"
>
<NuxtImg
provider="strapi"
:src="image"
class="hero-bg"
sizes="sm:100vw md:100vw lg:100vw"
alt=""
aria-hidden="true"
loading="eager"
priority
fetchpriority="high"
/>
<NuxtImg
v-if="overlayImage"
provider="strapi"
:src="overlayImage"
class="overlay-img"
:alt="overlayAltText"
:style="{
...overlayPosition,
width: typeof overlayWidth === 'number' ? overlayWidth + 'px' : overlayWidth,
transform: `translate3d(0, ${parallaxY}px, 0)`
}"
aria-hidden="true"
/>
<div class="container-10 heroContent">
<slot />
</div>
<svg class="sectionWave wave-bottom" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20" aria-hidden="true">
<path d="M 0 0 L 500 0 L 500 14 Q 354.4 -2.8 250 11 Q 145.6 24.8 0 14 L 0 0 Z" fill="#FFF"/>
</svg>
</section>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useMainStore } from '@/stores/main'
const mainStore = useMainStore()
const props = defineProps({
image: String,
ariaLabel: String,
overlayImage: {
type: String,
default: null
},
overlayAltText: {
type: String,
default: ''
},
overlayWidth: {
type: [String, Number],
default: '200px'
},
darkBackground: {
type: Boolean,
default: false
},
overlayPosition: {
type: Object,
default: () => ({
bottom: '0px',
right: '0px'
})
}
})
const parallaxY = ref(0)
const heroBoxRef = ref(null)
let targetY = 0
let animationFrame
const updateParallax = () => {
parallaxY.value += (targetY - parallaxY.value) * 0.1
animationFrame = requestAnimationFrame(updateParallax)
}
const onScroll = () => {
targetY = window.scrollY * 0.3
}
const updateHeight = () => {
if (heroBoxRef.value) {
mainStore.setHeroBoxHeight(heroBoxRef.value.offsetHeight)
mainStore.setDarkHeroBack(props.darkBackground)
}
}
onMounted(() => {
window.addEventListener('scroll', onScroll, { passive: true })
updateParallax()
updateHeight()
window.addEventListener('resize', updateHeight)
})
onUnmounted(() => {
window.removeEventListener('scroll', onScroll)
cancelAnimationFrame(animationFrame)
window.removeEventListener('resize', updateHeight)
})
</script>
<style lang="sass">
.heroBox
position: relative
min-height: 35rem
height: 70vh
display: flex
align-items: center
justify-content: center
overflow: hidden
&::after
position: absolute
content: ""
z-index: 0
top: 0
left: 0
width: 100%
height: 100%
&.dark::after
background-image: linear-gradient(
to bottom right,
rgba(255,255,255,0.4) 0%,
rgba(255,255,255,0.3) 2%,
rgba(0, 0, 0, 0.7) 60%,
rgba(255, 255, 255, 0) 100%,
transparent 100%
)
&.light::after
background-image: linear-gradient(
to bottom right,
rgba(0, 0, 0, 0.1) 0%,
rgba(255, 255, 255, 0.8) 40%,
rgba(255, 255, 255, 1) 100%
)
.hero-bg
position: absolute
inset: 0
width: 100%
height: 100%
object-fit: cover
object-position: center top
z-index: -1
.overlay-img
position: absolute
bottom: 0
right: 0
z-index: 1
height: auto
max-height: 100%
max-width: 350px
object-fit: contain
.heroContent, h1, h2, h3
position: relative
z-index: 2
h1, h2, h3
line-height: 1.5
max-width: 70%
@media (max-width: 768px)
max-width: 100%
&.dark h1, &.dark h2, &.dark h3, &.dark p
color: white
&.light h1, &.light h2, &.light h3, , &.light p
color: black
h1
margin-top: 3rem
font-size: clamp(2rem, 1.4rem + 3vw, 2.8rem)
line-height: 135%
margin-bottom: 0
font-family: 'Comfortaa'
hyphens: auto
h2
font-size: clamp(1.2rem, .7rem + 2vw, 2rem)
margin: .8rem 0 .8rem 0
font-family: 'Comfortaa'
h3
font-size: clamp(1rem, .6rem + 2vw, 1.6rem)
margin: .8rem 0 .8rem 0
p
font-size: clamp(1rem, .5rem + 2vw, 1.2rem)
margin: .8rem 0 .8rem 0
@media(max-width: $breakPointMD)
h1, h2, h3, p
line-height: 120%
.btn
margin: 1rem auto
</style>