feat: Enhance MainPageLayout with improved star decoration animations and initial fill colors; add new teal star layers and update localization strings across multiple languages for better user experience
This commit is contained in:
@@ -14,24 +14,149 @@ import { STAR_POLYGON_POINTS } from "../utils/starPolygon.js";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const HOME_STAR_LAYERS = [
|
||||
{ className: "star-rotate-slow-cw", size: 168, staticDeg: 20, fill: "#B8860B" },
|
||||
{ className: "star-rotate-slow-ccw", size: 159, staticDeg: -25, fill: "#DAA520" },
|
||||
{ className: "star-rotate-medium-cw", size: 150, staticDeg: null, fill: "#FFD700" },
|
||||
{ className: "star-rotate-slow-cw", size: 168 },
|
||||
{ className: "star-rotate-slow-ccw", size: 159 },
|
||||
{ className: "star-rotate-medium-cw", size: 150 },
|
||||
];
|
||||
|
||||
const FILIALE_STAR_LAYERS = [
|
||||
{ className: "star-rotate-slow-ccw", size: 168, staticDeg: 20, fill: "#5F9EA0" },
|
||||
{ className: "star-rotate-medium-cw", size: 159, staticDeg: -25, fill: "#7FCDCD" },
|
||||
{ className: "star-rotate-slow-cw", size: 150, staticDeg: null, fill: "#AFEEEE" },
|
||||
/** Teal/cyan stack for the right (Konfigurator) star — same motion, blue color scheme */
|
||||
const TEAL_STAR_LAYERS = [
|
||||
{ className: "star-rotate-slow-ccw", size: 168 },
|
||||
{ className: "star-rotate-medium-cw", size: 159 },
|
||||
{ className: "star-rotate-slow-cw", size: 150 },
|
||||
];
|
||||
|
||||
const StarDecorationLayers = ({ layers }) => (
|
||||
/** Initial fill per variant (matches keyframe 0%) — avoids black flash before CSS animates */
|
||||
const STAR_INITIAL_FILLS = {
|
||||
home: ["#B8860B", "#DAA520", "#FFD700"],
|
||||
filiale: ["#5F9EA0", "#7FCDCD", "#AFEEEE"],
|
||||
};
|
||||
|
||||
/** Injected in render (not useEffect) so first paint already has keyframes — avoids angle/color snap on load */
|
||||
const STAR_DECORATION_CSS = `
|
||||
@keyframes rotateClockwise {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes rotateCounterClockwise {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(-360deg); }
|
||||
}
|
||||
.star-rotate-slow-cw,
|
||||
.star-rotate-slow-ccw,
|
||||
.star-rotate-medium-cw {
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
}
|
||||
.star-rotate-slow-cw {
|
||||
animation: rotateClockwise 60s linear infinite;
|
||||
}
|
||||
.star-rotate-slow-ccw {
|
||||
animation: rotateCounterClockwise 45s linear infinite;
|
||||
}
|
||||
.star-rotate-medium-cw {
|
||||
animation: rotateClockwise 30s linear infinite;
|
||||
}
|
||||
|
||||
.star-layer-svg-home { mix-blend-mode: screen; opacity: 0.92; }
|
||||
.star-layer-svg-filiale { mix-blend-mode: soft-light; opacity: 0.94; }
|
||||
|
||||
@keyframes starFillHome0 {
|
||||
0%, 100% { fill: #B8860B; }
|
||||
33% { fill: #FFD700; }
|
||||
66% { fill: #DAA520; }
|
||||
}
|
||||
@keyframes starFillHome1 {
|
||||
0%, 100% { fill: #DAA520; }
|
||||
33% { fill: #B8860B; }
|
||||
66% { fill: #FFD700; }
|
||||
}
|
||||
@keyframes starFillHome2 {
|
||||
0%, 100% { fill: #FFD700; }
|
||||
33% { fill: #DAA520; }
|
||||
66% { fill: #B8860B; }
|
||||
}
|
||||
@keyframes starDriftHome0 {
|
||||
0%, 100% { transform: rotate(20deg) translate(0px, 0px); }
|
||||
50% { transform: rotate(20deg) translate(5px, -5px); }
|
||||
}
|
||||
@keyframes starDriftHome1 {
|
||||
0%, 100% { transform: rotate(-25deg) translate(0px, 0px); }
|
||||
50% { transform: rotate(-25deg) translate(-4px, 6px); }
|
||||
}
|
||||
@keyframes starDriftHome2 {
|
||||
0%, 100% { transform: translate(0px, 0px); }
|
||||
50% { transform: translate(3px, 4px); }
|
||||
}
|
||||
.star-layer-wrap.star-layer-home-0 {
|
||||
animation: starDriftHome0 6.5s ease-in-out infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.star-layer-wrap.star-layer-home-1 {
|
||||
animation: starDriftHome1 7s ease-in-out 0.4s infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.star-layer-wrap.star-layer-home-2 {
|
||||
animation: starDriftHome2 5.5s ease-in-out 0.8s infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.star-poly-home-0 { animation: starFillHome0 10s ease-in-out 0s infinite both; }
|
||||
.star-poly-home-1 { animation: starFillHome1 10s ease-in-out 1.1s infinite both; }
|
||||
.star-poly-home-2 { animation: starFillHome2 10s ease-in-out 2.2s infinite both; }
|
||||
|
||||
@keyframes starFillFil0 {
|
||||
0%, 100% { fill: #5F9EA0; }
|
||||
33% { fill: #AFEEEE; }
|
||||
66% { fill: #7FCDCD; }
|
||||
}
|
||||
@keyframes starFillFil1 {
|
||||
0%, 100% { fill: #7FCDCD; }
|
||||
33% { fill: #5F9EA0; }
|
||||
66% { fill: #AFEEEE; }
|
||||
}
|
||||
@keyframes starFillFil2 {
|
||||
0%, 100% { fill: #AFEEEE; }
|
||||
33% { fill: #7FCDCD; }
|
||||
66% { fill: #5F9EA0; }
|
||||
}
|
||||
@keyframes starDriftFil0 {
|
||||
0%, 100% { transform: rotate(20deg) translate(0px, 0px); }
|
||||
50% { transform: rotate(20deg) translate(4px, -4px); }
|
||||
}
|
||||
@keyframes starDriftFil1 {
|
||||
0%, 100% { transform: rotate(-25deg) translate(0px, 0px); }
|
||||
50% { transform: rotate(-25deg) translate(-5px, 5px); }
|
||||
}
|
||||
@keyframes starDriftFil2 {
|
||||
0%, 100% { transform: translate(0px, 0px); }
|
||||
50% { transform: translate(3px, 3px); }
|
||||
}
|
||||
.star-layer-wrap.star-layer-filiale-0 {
|
||||
animation: starDriftFil0 6.5s ease-in-out infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.star-layer-wrap.star-layer-filiale-1 {
|
||||
animation: starDriftFil1 7s ease-in-out 0.4s infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.star-layer-wrap.star-layer-filiale-2 {
|
||||
animation: starDriftFil2 5.5s ease-in-out 0.8s infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.star-poly-filiale-0 { animation: starFillFil0 10s ease-in-out 0s infinite both; }
|
||||
.star-poly-filiale-1 { animation: starFillFil1 10s ease-in-out 1.1s infinite both; }
|
||||
.star-poly-filiale-2 { animation: starFillFil2 10s ease-in-out 2.2s infinite both; }
|
||||
`;
|
||||
|
||||
const StarDecorationLayers = ({ layers, variant }) => (
|
||||
<>
|
||||
{layers.map(({ className, size, staticDeg, fill }, i) => {
|
||||
{layers.map(({ className, size }, i) => {
|
||||
const half = size / 2;
|
||||
const initialFill = STAR_INITIAL_FILLS[variant][i];
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={`star-layer-wrap star-layer-${variant}-${i}`}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "50%",
|
||||
@@ -40,11 +165,21 @@ const StarDecorationLayers = ({ layers }) => (
|
||||
height: size,
|
||||
marginLeft: -half,
|
||||
marginTop: -half,
|
||||
...(staticDeg != null ? { transform: `rotate(${staticDeg}deg)` } : {}),
|
||||
zIndex: 3 - i,
|
||||
}}
|
||||
>
|
||||
<svg viewBox="0 0 60 60" width="100%" height="100%" className={className} style={{ display: "block" }}>
|
||||
<polygon points={STAR_POLYGON_POINTS} fill={fill} />
|
||||
<svg
|
||||
viewBox="0 0 60 60"
|
||||
width="100%"
|
||||
height="100%"
|
||||
className={`${className} star-layer-svg star-layer-svg-${variant}`}
|
||||
style={{ display: "block" }}
|
||||
>
|
||||
<polygon
|
||||
points={STAR_POLYGON_POINTS}
|
||||
fill={initialFill}
|
||||
className={`star-poly-fill star-poly-${variant}-${i}`}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
@@ -69,7 +204,7 @@ const ContentBox = ({ box, index, pageType, starHovered, setStarHovered, opacity
|
||||
display: { xs: 'none', sm: 'block' }
|
||||
}}
|
||||
>
|
||||
<StarDecorationLayers layers={HOME_STAR_LAYERS} />
|
||||
<StarDecorationLayers layers={HOME_STAR_LAYERS} variant="home" />
|
||||
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%) rotate(-10deg)', color: 'white', fontWeight: '900', fontSize: '20px', textShadow: '0px 3px 6px rgba(0,0,0,0.5)', zIndex: 1000, textAlign: 'center', lineHeight: '1.1', width: '135px', transition: 'opacity 0.3s ease', opacity: starHovered ? 0 : 1 }}>
|
||||
{translatedContent.outdoorSeason}
|
||||
</div>
|
||||
@@ -78,7 +213,7 @@ const ContentBox = ({ box, index, pageType, starHovered, setStarHovered, opacity
|
||||
</div>
|
||||
</Box>
|
||||
)}
|
||||
{index === 1 && pageType === "filiale" && (
|
||||
{index === 1 && pageType === "home" && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
@@ -93,9 +228,9 @@ const ContentBox = ({ box, index, pageType, starHovered, setStarHovered, opacity
|
||||
display: { xs: 'none', sm: 'block' }
|
||||
}}
|
||||
>
|
||||
<StarDecorationLayers layers={FILIALE_STAR_LAYERS} />
|
||||
<StarDecorationLayers layers={TEAL_STAR_LAYERS} variant="filiale" />
|
||||
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%) rotate(10deg)', color: 'white', fontWeight: '900', fontSize: '20px', textShadow: '0px 3px 6px rgba(0,0,0,0.5)', zIndex: 1000, textAlign: 'center', lineHeight: '1.1', width: '135px' }}>
|
||||
{translatedContent.showUsPhoto}
|
||||
{translatedContent.buildYourSet}
|
||||
</div>
|
||||
</Box>
|
||||
)}
|
||||
@@ -117,12 +252,12 @@ const ContentBox = ({ box, index, pageType, starHovered, setStarHovered, opacity
|
||||
"&:hover": { transform: "translateY(-5px)", boxShadow: 20 },
|
||||
}}
|
||||
onMouseEnter={
|
||||
(pageType === "home" && index === 0) || (pageType === "filiale" && index === 1)
|
||||
pageType === "home" && index === 0
|
||||
? () => setStarHovered(true)
|
||||
: undefined
|
||||
}
|
||||
onMouseLeave={
|
||||
(pageType === "home" && index === 0) || (pageType === "filiale" && index === 1)
|
||||
pageType === "home" && index === 0
|
||||
? () => setStarHovered(false)
|
||||
: undefined
|
||||
}
|
||||
@@ -146,7 +281,7 @@ const MainPageLayout = () => {
|
||||
const { t } = useTranslation();
|
||||
const [starHovered, setStarHovered] = React.useState(false);
|
||||
const translatedContent = {
|
||||
showUsPhoto: t('sections.showUsPhoto'),
|
||||
buildYourSet: t('sections.buildYourSet'),
|
||||
selectSeedRate: t('sections.selectSeedRate'),
|
||||
outdoorSeason: t('sections.outdoorSeason')
|
||||
};
|
||||
@@ -155,37 +290,6 @@ const MainPageLayout = () => {
|
||||
const isAktionen = currentPath === "/aktionen";
|
||||
const isFiliale = currentPath === "/filiale";
|
||||
|
||||
React.useEffect(() => {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes rotateClockwise {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes rotateCounterClockwise {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(-360deg); }
|
||||
}
|
||||
.star-rotate-slow-cw,
|
||||
.star-rotate-slow-ccw,
|
||||
.star-rotate-medium-cw {
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
}
|
||||
.star-rotate-slow-cw {
|
||||
animation: rotateClockwise 60s linear infinite;
|
||||
}
|
||||
.star-rotate-slow-ccw {
|
||||
animation: rotateCounterClockwise 45s linear infinite;
|
||||
}
|
||||
.star-rotate-medium-cw {
|
||||
animation: rotateClockwise 30s linear infinite;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
return () => document.head.removeChild(style);
|
||||
}, []);
|
||||
|
||||
const getNavigationConfig = () => {
|
||||
if (isHome) return { leftNav: { text: t('navigation.aktionen'), link: "/aktionen" }, rightNav: { text: t('navigation.filiale'), link: "/filiale" } };
|
||||
if (isAktionen) return { leftNav: { text: t('navigation.filiale'), link: "/filiale" }, rightNav: { text: t('navigation.home'), link: "/" } };
|
||||
@@ -231,6 +335,7 @@ const MainPageLayout = () => {
|
||||
return (
|
||||
<Container maxWidth="lg" sx={{ py: 2 }}>
|
||||
<style>{getCombinedAnimatedBorderStyles(['seeds', 'cutlings'])}</style>
|
||||
<style>{STAR_DECORATION_CSS}</style>
|
||||
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", mb: 4, mt: 2, px: 0, transition: "all 0.3s ease-in-out", flexDirection: { xs: "column", sm: "row" } }}>
|
||||
<Box sx={{ display: { xs: "block", sm: "none" }, mb: { xs: 2, sm: 0 }, width: "100%", textAlign: "center", position: "relative" }}>
|
||||
{Object.entries(allTitles).map(([pageType, title]) => (
|
||||
|
||||
Reference in New Issue
Block a user