dml_frontend/components/HeroBox.vue
2025-06-04 14:36:46 +02:00

204 lines
4.4 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"
priority
loading="eager"
preload
fetchpriority="high"
/>
<!-- Optionales Overlay-Bild -->
<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 content">
<h1>{{ $t(content.headline1) }}</h1>
<h2>{{ $t(content.headline2) }}</h2>
<h3>{{ $t(content.headline3) }}</h3>
</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>
// Parallax Overlay-Image
import { ref, onMounted, onUnmounted } from 'vue'
import { useMainStore } from '@/stores/main'
const mainStore = useMainStore()
const props = defineProps({
image: String,
ariaLabel: String,
content: {
type: Object,
required: true
},
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 = () => {
// Ease: Linear interpolation (lerp) zwischen aktuellem Wert und Zielwert
parallaxY.value += (targetY - parallaxY.value) * 0.1
animationFrame = requestAnimationFrame(updateParallax)
}
const onScroll = () => {
targetY = window.scrollY * 0.3 // oder was dir gefällt
}
const updateHeight = () => {
if (heroBoxRef.value) {
mainStore.setHeroBoxHeight(heroBoxRef.value.offsetHeight)
mainStore.setDarkHeroBack(props.darkBackground)
}
}
onMounted(() => {
window.addEventListener('scroll', onScroll, { passive: true })
updateParallax()
// HeroBoxHeight for Logo
updateHeight()
window.addEventListener('resize', updateHeight)
})
onUnmounted(() => {
window.removeEventListener('scroll', onScroll)
cancelAnimationFrame(animationFrame)
window.removeEventListener('resize', updateHeight)
})
</script>
<style lang="sass" scoped>
.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
.content, 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
color: white
&.light h1, &.light h2, &.light h3
color: black
h1
margin-top: 3rem
font-size: clamp(2rem, 1.4rem + 3vw, 2.8rem)
line-height: 140%
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: 1.2rem
</style>