update
Browse files
client/src/components/InfiniteBackground.jsx
ADDED
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Box } from "@mui/material";
|
2 |
+
import { keyframes } from "@mui/system";
|
3 |
+
import { useState, useEffect } from "react";
|
4 |
+
import { motion, AnimatePresence } from "framer-motion";
|
5 |
+
|
6 |
+
const slideLeft = keyframes`
|
7 |
+
from {
|
8 |
+
transform: translateX(0);
|
9 |
+
}
|
10 |
+
to {
|
11 |
+
transform: translateX(-100%);
|
12 |
+
}
|
13 |
+
`;
|
14 |
+
|
15 |
+
const slideRight = keyframes`
|
16 |
+
from {
|
17 |
+
transform: translateX(0);
|
18 |
+
}
|
19 |
+
to {
|
20 |
+
transform: translateX(100%);
|
21 |
+
}
|
22 |
+
`;
|
23 |
+
|
24 |
+
// Hook pour précharger l'image et obtenir ses dimensions
|
25 |
+
const useImageDimensions = (imagePath) => {
|
26 |
+
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
27 |
+
const [isLoaded, setIsLoaded] = useState(false);
|
28 |
+
|
29 |
+
useEffect(() => {
|
30 |
+
const img = new Image();
|
31 |
+
img.src = imagePath;
|
32 |
+
img.onload = () => {
|
33 |
+
setDimensions({ width: img.width, height: img.height });
|
34 |
+
setIsLoaded(true);
|
35 |
+
};
|
36 |
+
}, [imagePath]);
|
37 |
+
|
38 |
+
return { dimensions, isLoaded };
|
39 |
+
};
|
40 |
+
|
41 |
+
const Row = ({ imagePath, direction = "left", speed = 1, containerHeight }) => {
|
42 |
+
const { dimensions, isLoaded } = useImageDimensions(imagePath);
|
43 |
+
const animation = direction === "left" ? slideLeft : slideRight;
|
44 |
+
const duration = 120 / speed;
|
45 |
+
|
46 |
+
if (!isLoaded) return null;
|
47 |
+
|
48 |
+
// Calculer la hauteur de l'image pour qu'elle s'adapte à la hauteur du conteneur
|
49 |
+
const scale = containerHeight / dimensions.height;
|
50 |
+
const scaledWidth = dimensions.width * scale;
|
51 |
+
|
52 |
+
return (
|
53 |
+
<motion.div
|
54 |
+
initial={{ opacity: 0 }}
|
55 |
+
animate={{ opacity: 1 }}
|
56 |
+
transition={{ duration: 1 }}
|
57 |
+
>
|
58 |
+
<Box
|
59 |
+
sx={{
|
60 |
+
position: "relative",
|
61 |
+
width: "100%",
|
62 |
+
height: containerHeight,
|
63 |
+
overflow: "hidden",
|
64 |
+
display: "flex",
|
65 |
+
}}
|
66 |
+
>
|
67 |
+
{/* Premier set d'images */}
|
68 |
+
<Box
|
69 |
+
sx={{
|
70 |
+
display: "flex",
|
71 |
+
animation: `${animation} ${duration}s linear infinite`,
|
72 |
+
height: "100%",
|
73 |
+
}}
|
74 |
+
>
|
75 |
+
{[1, 2, 3].map((i) => (
|
76 |
+
<img
|
77 |
+
key={i}
|
78 |
+
src={imagePath}
|
79 |
+
alt="panorama"
|
80 |
+
style={{
|
81 |
+
height: containerHeight,
|
82 |
+
width: scaledWidth,
|
83 |
+
objectFit: "contain",
|
84 |
+
}}
|
85 |
+
/>
|
86 |
+
))}
|
87 |
+
</Box>
|
88 |
+
{/* Second set d'images pour la boucle continue */}
|
89 |
+
<Box
|
90 |
+
sx={{
|
91 |
+
display: "flex",
|
92 |
+
animation: `${animation} ${duration}s linear infinite`,
|
93 |
+
position: "absolute",
|
94 |
+
left: direction === "left" ? "100%" : `-${scaledWidth * 3}px`,
|
95 |
+
height: "100%",
|
96 |
+
}}
|
97 |
+
>
|
98 |
+
{[1, 2, 3].map((i) => (
|
99 |
+
<img
|
100 |
+
key={i}
|
101 |
+
src={imagePath}
|
102 |
+
alt="panorama"
|
103 |
+
style={{
|
104 |
+
height: containerHeight,
|
105 |
+
width: scaledWidth,
|
106 |
+
objectFit: "contain",
|
107 |
+
}}
|
108 |
+
/>
|
109 |
+
))}
|
110 |
+
</Box>
|
111 |
+
</Box>
|
112 |
+
</motion.div>
|
113 |
+
);
|
114 |
+
};
|
115 |
+
|
116 |
+
export function InfiniteBackground() {
|
117 |
+
const [containerHeight, setContainerHeight] = useState(0);
|
118 |
+
|
119 |
+
useEffect(() => {
|
120 |
+
const updateHeight = () => {
|
121 |
+
const vh = window.innerHeight;
|
122 |
+
setContainerHeight(vh / 3); // Divise la hauteur de la fenêtre en 3
|
123 |
+
};
|
124 |
+
|
125 |
+
updateHeight();
|
126 |
+
window.addEventListener("resize", updateHeight);
|
127 |
+
return () => window.removeEventListener("resize", updateHeight);
|
128 |
+
}, []);
|
129 |
+
|
130 |
+
return (
|
131 |
+
<Box
|
132 |
+
sx={{
|
133 |
+
position: "absolute",
|
134 |
+
top: 0,
|
135 |
+
left: 0,
|
136 |
+
right: 0,
|
137 |
+
bottom: 0,
|
138 |
+
overflow: "hidden",
|
139 |
+
zIndex: 0,
|
140 |
+
"&::after": {
|
141 |
+
content: '""',
|
142 |
+
position: "absolute",
|
143 |
+
top: 0,
|
144 |
+
left: 0,
|
145 |
+
right: 0,
|
146 |
+
bottom: 0,
|
147 |
+
background: "rgba(0, 0, 0, 0.85)",
|
148 |
+
zIndex: 1,
|
149 |
+
},
|
150 |
+
}}
|
151 |
+
>
|
152 |
+
<Row
|
153 |
+
imagePath="/bande-1.webp"
|
154 |
+
direction="left"
|
155 |
+
speed={1}
|
156 |
+
containerHeight={containerHeight}
|
157 |
+
/>
|
158 |
+
<Row
|
159 |
+
imagePath="/bande-2.webp"
|
160 |
+
direction="right"
|
161 |
+
speed={0.8}
|
162 |
+
containerHeight={containerHeight}
|
163 |
+
/>
|
164 |
+
<Row
|
165 |
+
imagePath="/bande-3.webp"
|
166 |
+
direction="left"
|
167 |
+
speed={1.2}
|
168 |
+
containerHeight={containerHeight}
|
169 |
+
/>
|
170 |
+
</Box>
|
171 |
+
);
|
172 |
+
}
|
client/src/pages/Home.jsx
CHANGED
@@ -4,6 +4,7 @@ import { useNavigate } from "react-router-dom";
|
|
4 |
import { usePageSound } from "../hooks/usePageSound";
|
5 |
import { BlinkingText } from "../components/BlinkingText";
|
6 |
import { BookPages } from "../components/BookPages";
|
|
|
7 |
|
8 |
export function Home() {
|
9 |
const navigate = useNavigate();
|
@@ -31,40 +32,9 @@ export function Home() {
|
|
31 |
minHeight: "100vh",
|
32 |
width: "100%",
|
33 |
position: "relative",
|
34 |
-
"&::before": {
|
35 |
-
content: '""',
|
36 |
-
position: "fixed",
|
37 |
-
top: 0,
|
38 |
-
left: 0,
|
39 |
-
right: 0,
|
40 |
-
bottom: 0,
|
41 |
-
backgroundImage: "url('/home.webp')",
|
42 |
-
backgroundSize: "cover",
|
43 |
-
backgroundPosition: "center",
|
44 |
-
backgroundRepeat: "no-repeat",
|
45 |
-
opacity: 0.3,
|
46 |
-
zIndex: 0,
|
47 |
-
},
|
48 |
}}
|
49 |
>
|
50 |
-
|
51 |
-
variant="h1"
|
52 |
-
component="h1"
|
53 |
-
sx={{
|
54 |
-
fontWeight: "bold",
|
55 |
-
marginBottom: 2,
|
56 |
-
color: "#f0e6d9",
|
57 |
-
textShadow: `
|
58 |
-
0 -1px 1px rgba(0,0,0,0.3),
|
59 |
-
0 1px 1px rgba(255,255,255,0.2)
|
60 |
-
`,
|
61 |
-
letterSpacing: "0.5px",
|
62 |
-
filter: "brightness(0.95)",
|
63 |
-
}}
|
64 |
-
>
|
65 |
-
Sarah's
|
66 |
-
<br /> Chronicles
|
67 |
-
</Typography> */}
|
68 |
<Box
|
69 |
sx={{
|
70 |
position: "relative",
|
|
|
4 |
import { usePageSound } from "../hooks/usePageSound";
|
5 |
import { BlinkingText } from "../components/BlinkingText";
|
6 |
import { BookPages } from "../components/BookPages";
|
7 |
+
import { InfiniteBackground } from "../components/InfiniteBackground";
|
8 |
|
9 |
export function Home() {
|
10 |
const navigate = useNavigate();
|
|
|
32 |
minHeight: "100vh",
|
33 |
width: "100%",
|
34 |
position: "relative",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
}}
|
36 |
>
|
37 |
+
<InfiniteBackground />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
<Box
|
39 |
sx={{
|
40 |
position: "relative",
|
client/src/pages/Tutorial.jsx
CHANGED
@@ -271,31 +271,6 @@ export function Tutorial() {
|
|
271 |
Start the game
|
272 |
</Button>
|
273 |
</Box>
|
274 |
-
<Box
|
275 |
-
sx={{
|
276 |
-
display: "flex",
|
277 |
-
flexDirection: "column",
|
278 |
-
alignItems: "center",
|
279 |
-
justifyContent: "center",
|
280 |
-
minHeight: "100vh",
|
281 |
-
width: "100%",
|
282 |
-
position: "relative",
|
283 |
-
"&::before": {
|
284 |
-
content: '""',
|
285 |
-
position: "fixed",
|
286 |
-
top: 0,
|
287 |
-
left: 0,
|
288 |
-
right: 0,
|
289 |
-
bottom: 0,
|
290 |
-
backgroundImage: "url('/home.webp')",
|
291 |
-
backgroundSize: "cover",
|
292 |
-
backgroundPosition: "center",
|
293 |
-
backgroundRepeat: "no-repeat",
|
294 |
-
opacity: 0.3,
|
295 |
-
zIndex: 0,
|
296 |
-
},
|
297 |
-
}}
|
298 |
-
></Box>
|
299 |
</motion.div>
|
300 |
);
|
301 |
}
|
|
|
271 |
Start the game
|
272 |
</Button>
|
273 |
</Box>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
</motion.div>
|
275 |
);
|
276 |
}
|