Browse Source

ça avance

feat/year-wrapped
Elias Sebbar 1 week ago
parent
commit
7af4a1736f
3 changed files with 400 additions and 43 deletions
  1. 30
    11
      vite-wrapped/src/App.tsx
  2. 257
    0
      vite-wrapped/src/steps/GlobalTop.tsx
  3. 113
    32
      vite-wrapped/src/steps/welcome.tsx

+ 30
- 11
vite-wrapped/src/App.tsx View File

@@ -2,20 +2,19 @@ import "./App.css";
2 2
 // @deno-types="@types/react"
3 3
 import React, { useEffect, useState } from "react";
4 4
 
5
-import Canvas from "./layout/canvas.tsx";
6
-import Masked from "./components/mask/Masked.tsx";
7 5
 import { motion, useMotionValue, useMotionValueEvent } from "framer-motion";
8 6
 import { useAnimationFrame } from "framer-motion";
9 7
 import Welcome from "./steps/welcome.tsx";
8
+import GlobalTop from "./steps/GlobalTop.tsx";
10 9
 
11 10
 const steps = [
12 11
   {
13 12
     title: "Step 1",
14
-    content: "Content of step 1",
13
+    content: Welcome,
15 14
   },
16 15
   {
17 16
     title: "Step 2",
18
-    content: "Content of step 2",
17
+    content: GlobalTop,
19 18
   },
20 19
   {
21 20
     title: "Step 3",
@@ -25,26 +24,46 @@ const steps = [
25 24
 
26 25
 function App() {
27 26
   const [currentStep, setCurrentStep] = useState(0);
28
-  const animationDuration = 5000;
27
+  const animationDuration = 10_000;
29 28
   const animationProgress = useMotionValue(0);
30
-  const [animationFrame, setAnimationFrame] = useState(0);
31 29
   const [initialAnimationFrame, setInitialAnimationFrame] = useState(0);
32 30
 
33 31
   useAnimationFrame((time, delta) => {
34 32
     if (time - initialAnimationFrame >= animationDuration) {
35 33
       setInitialAnimationFrame(time);
36
-      setCurrentStep((prev) => prev + 1);
34
+      //setCurrentStep((prev) => prev + 1);
37 35
       animationProgress.set(0);
38 36
     } else {
39 37
       animationProgress.set(animationProgress.get() + delta);
40 38
     }
41
-    setAnimationFrame(time);
42 39
   });
43 40
 
41
+  useEffect(() => {
42
+    animationProgress.set(0);
43
+    setInitialAnimationFrame(0);
44
+  }, [currentStep]);
45
+
46
+  const CurrentStepContent = steps[currentStep].content;
44 47
   return (
45 48
     <>
46 49
       <div style={page as React.CSSProperties}>
47
-        <Welcome progress={animationProgress} />
50
+        <div
51
+          style={{
52
+            position: "absolute",
53
+            top: "0",
54
+            bottom: "0",
55
+            left: "0",
56
+            right: "0",
57
+          }}
58
+        >
59
+          <CurrentStepContent key={`${currentStep}`} progress={animationProgress} />
60
+          {/*  <Welcome progress={animationProgress} /> */}
61
+        </div>
62
+        <div style={{ zIndex: 100, fontSize: "5px" }}>
63
+          <h1>Step selector (current = {currentStep})</h1>
64
+          <button onClick={() => setCurrentStep(0)}>Step 1</button>
65
+          <button onClick={() => setCurrentStep(1)}>Step 2</button>
66
+        </div>
48 67
       </div>
49 68
     </>
50 69
   );
@@ -53,8 +72,8 @@ function App() {
53 72
 const page = {
54 73
   display: "flex",
55 74
   position: "relative",
56
-  justifyContent: "center",
57
-  alignItems: "center",
75
+  justifyContent: "start",
76
+  alignItems: "start",
58 77
   margin: 0,
59 78
   padding: 0,
60 79
   width: "100%",

+ 257
- 0
vite-wrapped/src/steps/GlobalTop.tsx View File

@@ -0,0 +1,257 @@
1
+import { motion, Variants } from "framer-motion";
2
+import { StepProps } from "./stepProps.ts";
3
+import Masked from "../components/mask/Masked.tsx";
4
+import React, { useState, useEffect, useRef } from "react";
5
+
6
+type VariantProps = {
7
+  delay: number;
8
+};
9
+
10
+const leftSideVariants: Variants = {
11
+  "1": (props: VariantProps) => ({
12
+    width: 1,
13
+    transition: {
14
+      duration: 1.5,
15
+      repeat: 0,
16
+      type: "spring",
17
+      ease: "easeInOut",
18
+      delay: 0.5 + props.delay,
19
+    },
20
+  }),
21
+  "2": (props: VariantProps) => ({
22
+    width: 10,
23
+    transition: {
24
+      duration: 1,
25
+      repeat: 0,
26
+      delay: 0,
27
+    },
28
+  }),
29
+  "3": (props: VariantProps) => ({
30
+    width: 100 + props.delay * 400,
31
+    transition: {
32
+      duration: 2,
33
+      repeat: 0,
34
+      ease: "easeInOut",
35
+      delay: 0,
36
+    },
37
+  }),
38
+};
39
+
40
+const textAreaVariants: Variants = {
41
+  "1": {
42
+    opacity: 1,
43
+    width: "80%",
44
+    height: "80%",
45
+    transition: {
46
+      duration: 0.5,
47
+    },
48
+  },
49
+  "2": {
50
+    opacity: 1,
51
+    width: "80%",
52
+    height: "80%",
53
+    transition: {
54
+      duration: 1.5,
55
+    },
56
+  },
57
+  "3": {
58
+    opacity: 1,
59
+    width: "80%",
60
+    height: "80%",
61
+    transition: {
62
+      duration: 1,
63
+    },
64
+  },
65
+};
66
+
67
+type RunningAnimations = {
68
+  [key: string]: number;
69
+};
70
+
71
+function usePrevious(value: any) {
72
+  const ref = useRef();
73
+  useEffect(() => {
74
+    ref.current = value;
75
+  }, [value]);
76
+  return ref.current;
77
+}
78
+
79
+function GlobalTop({ progress }: StepProps) {
80
+  const [starVariant, setStarVariant] = useState("1");
81
+  const [runningAnimations, setRunningAnimations] = useState<RunningAnimations>(
82
+    {}
83
+  );
84
+  const previousRunningAnimations = usePrevious(runningAnimations);
85
+
86
+  // implement the logic of "animationComplete" in a useEffect
87
+  // Consider an animation just completed when the associated number of running animations just reached 0
88
+
89
+  useEffect(() => {
90
+    for (const variant in runningAnimations) {
91
+      if (
92
+        runningAnimations[variant] === 0 &&
93
+        previousRunningAnimations[variant] !== 0
94
+      ) {
95
+        if (variant === "1") {
96
+          setStarVariant("2");
97
+        } else if (variant === "2") {
98
+          setStarVariant("3");
99
+        }
100
+      }
101
+    }
102
+  }, [runningAnimations]);
103
+
104
+  const animationComplete = (variant: string) => {
105
+    console.log("animationComplete", variant);
106
+    decrementAnimation(variant);
107
+  };
108
+
109
+  const animationStarted = (variant: string) => {
110
+    console.log("animationStarted", variant);
111
+    incrementAnimation(variant);
112
+  };
113
+
114
+  const incrementAnimation = (animation: string) => {
115
+    setRunningAnimations((prev: RunningAnimations) => {
116
+      const newRunningAnimations = { ...prev };
117
+      if (!newRunningAnimations[animation]) {
118
+        newRunningAnimations[animation] = 1;
119
+      } else {
120
+        newRunningAnimations[animation]++;
121
+      }
122
+      return newRunningAnimations;
123
+    });
124
+  };
125
+
126
+  const decrementAnimation = (animation: string) => {
127
+    setRunningAnimations((prev: RunningAnimations) => {
128
+      const newRunningAnimations = { ...prev };
129
+      if (newRunningAnimations[animation] > 0) {
130
+        newRunningAnimations[animation]--;
131
+      }
132
+      return newRunningAnimations;
133
+    });
134
+  };
135
+
136
+  return (
137
+    <div style={pageStyle as React.CSSProperties}>
138
+      <div
139
+        style={{
140
+          display: "flex",
141
+          position: "absolute",
142
+          top: 0,
143
+          bottom: 0,
144
+          left: 0,
145
+          right: 0,
146
+          flexDirection: "column",
147
+          alignItems: "start",
148
+          justifyContent: "start",
149
+        }}
150
+      >
151
+        {Array.from({ length: 7 }).map((_, i) => (
152
+          <motion.div
153
+            key={i}
154
+            variants={leftSideVariants}
155
+            style={baseStyle as React.CSSProperties}
156
+            animate={starVariant}
157
+            custom={{ delay: i * 0.05 }}
158
+            onAnimationStart={() => animationStarted(starVariant)}
159
+            onAnimationComplete={() => animationComplete(starVariant)}
160
+          >
161
+            <div style={starStyle as React.CSSProperties}></div>
162
+          </motion.div>
163
+        ))}
164
+      </div>
165
+      <div
166
+        style={{
167
+          display: "flex",
168
+          position: "absolute",
169
+          top: 0,
170
+          bottom: 0,
171
+          left: 0,
172
+          right: 0,
173
+          flexDirection: "column",
174
+          alignItems: "end",
175
+          justifyContent: "end",
176
+        }}
177
+      >
178
+        {Array.from({ length: 7 }).map((_, i) => (
179
+          <motion.div
180
+            key={i}
181
+            variants={leftSideVariants}
182
+            style={baseStyle as React.CSSProperties}
183
+            animate={starVariant}
184
+            custom={{ delay: i * 0.05 }}
185
+            onAnimationStart={() => animationStarted(starVariant)}
186
+            onAnimationComplete={() => animationComplete(starVariant)}
187
+          >
188
+            <div style={starStyle as React.CSSProperties}></div>
189
+          </motion.div>
190
+        ))}
191
+      </div>
192
+
193
+      <div style={bgSquareStyle as React.CSSProperties}></div>
194
+
195
+      <motion.div
196
+        variants={textAreaVariants}
197
+        animate={starVariant}
198
+        style={textAreaStyle as React.CSSProperties}
199
+      >
200
+        Coucou ! {starVariant}
201
+      </motion.div>
202
+    </div>
203
+  );
204
+}
205
+
206
+const pageStyle = {
207
+  position: "relative",
208
+  width: "100%",
209
+  height: "100%",
210
+  display: "flex",
211
+  justifyContent: "center",
212
+  alignItems: "center",
213
+  overflow: "hidden",
214
+};
215
+
216
+const baseStyle = {
217
+  zIndex: 1,
218
+  width: "50%",
219
+  flex: 1,
220
+};
221
+
222
+const starStyle = {
223
+  position: "relative",
224
+  width: "100%",
225
+  height: "100%",
226
+  backgroundBlendMode: "multiply",
227
+  background:
228
+    "linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(195,203,56,1) 86%)",
229
+};
230
+
231
+const textAreaStyle = {
232
+  background:
233
+    "linear-gradient(194deg, rgba(255,255,255,0.3650366748166259) 0%, rgba(195,203,56,0.2885085574572127) 86%)",
234
+  backdropFilter: "blur(10px)",
235
+  color: "#000000FF",
236
+  opacity: 0,
237
+  padding: "2",
238
+  width: "200px",
239
+  height: "100px",
240
+  zIndex: 0,
241
+  borderRadius: "10px",
242
+};
243
+
244
+const bgSquareStyle = {
245
+  width: "80%",
246
+  height: "70%",
247
+  position: "absolute",
248
+  top: 300,
249
+  left: 5,
250
+  backgroundImage:
251
+    "linear-gradient(#07b503 1px, transparent 1px, transparent calc(100% - 1px), rgba(195,203,56,1) calc(100% - 1px)), linear-gradient(90deg, rgba(195,203,56,1) 1px, transparent 1px, transparent calc(100% - 1px), rgba(195,203,56,1) calc(100% - 1px))",
252
+  backgroundSize: "3.3333333333333335% 3.3333333333333335%",
253
+  border: "1px solid rgba(195,203,56,1)",
254
+  zIndex: 0,
255
+};
256
+
257
+export default GlobalTop;

+ 113
- 32
vite-wrapped/src/steps/welcome.tsx View File

@@ -1,71 +1,152 @@
1
-import { motion, MotionConfig, Variants } from "framer-motion";
1
+import { motion, Variants } from "framer-motion";
2 2
 import { StepProps } from "./stepProps.ts";
3 3
 import Masked from "../components/mask/Masked.tsx";
4
-import React, { useState, useEffect } from "react";
4
+import React, { useState } from "react";
5
+
6
+type VariantProps = {
7
+  delay: number;
8
+};
5 9
 
6 10
 const starVariants: Variants = {
7
-  enter: {
8
-    top: "80%",
9
-    left: "80%",
10
-    rotate: 45,
11
+  "1": (props: VariantProps) => ({
12
+    rotate: 360,
13
+    scale: 3,
11 14
     transition: {
12 15
       duration: 2,
13 16
       repeat: 0,
14
-      delay: 1,
17
+      delay: props.delay,
15 18
     },
16
-  },
17
-  idle: {
18
-    top: "80.1%",
19
-    left: "80.1%",
20
-    scale: 1.01,
21
-    rotate: 45,
19
+  }),
20
+  "2": (props: VariantProps) => ({
21
+    scale: 3,
22
+    x: props.delay,
23
+    rotate: props.delay + 365 * 2,
22 24
     transition: {
23
-      duration: 0.5,
25
+      duration: 1,
26
+      repeat: 0,
27
+      delay: props.delay,
28
+    },
29
+  }),
30
+  "3": (props: VariantProps) => ({
31
+    scale: 3,
32
+    rotate: props.delay + 365 * 3,
33
+    transition: {
34
+      duration: 6,
24 35
       repeat: Infinity,
25 36
       repeatType: "reverse",
37
+      delay: props.delay,
38
+    },
39
+  }),
40
+};
41
+
42
+const textAreaVariants: Variants = {
43
+  "1": {
44
+    opacity: 1,
45
+    width: "80%",
46
+    height: "80%",
47
+    transition: {
48
+      duration: 0.5,
49
+    },
50
+  },
51
+  "2": {
52
+    opacity: 1,
53
+    width: "80%",
54
+    height: "80%",
55
+    transition: {
56
+      duration: 1.5,
26 57
     },
27 58
   },
59
+  "3": {
60
+    opacity: 1,
61
+    width: "80%",
62
+    height: "80%",
63
+    transition: {
64
+      duration: 1,
65
+    },
66
+  },
67
+};
68
+
69
+type RunningAnimations = {
70
+  [key: string]: number;
28 71
 };
29 72
 
30 73
 function Welcome({ progress }: StepProps) {
31
-  const [starVariant, setStarVariant] = useState("enter");
74
+  const [starVariant, setStarVariant] = useState("1");
75
+  const [runningAnimations, setRunningAnimations] = useState<RunningAnimations>(
76
+    {}
77
+  );
32 78
 
33
-  useEffect(() => {
34
-    if (progress.get() > 3000) {
35
-      setStarVariant("idle");
79
+  const animationComplete = (variant: string) => {
80
+    const newRunningAnimations = { ...runningAnimations };
81
+    if (newRunningAnimations[variant] > 0) {
82
+      newRunningAnimations[variant]--;
83
+      setRunningAnimations(newRunningAnimations);
36 84
     } else {
37
-      setStarVariant("enter");
85
+      if (variant === "1") {
86
+        setStarVariant("2");
87
+      } else if (variant === "2") {
88
+        setStarVariant("3");
89
+      }
38 90
     }
39
-  }, [progress.get()]);
91
+  };
92
+
93
+  const animationStarted = (variant: string) => {
94
+    const newRunningAnimations = { ...runningAnimations };
95
+    if (!newRunningAnimations[variant]) {
96
+      newRunningAnimations[variant] = 1;
97
+    } else {
98
+      newRunningAnimations[variant]++;
99
+    }
100
+    setRunningAnimations(newRunningAnimations);
101
+  };
40 102
 
41 103
   return (
42
-    <div style={{ position: "relative", width: "100%", height: "100%" }}>
43
-      <motion.div
44
-        variants={starVariants}
45
-        style={baseStyle as React.CSSProperties}
46
-        animate={starVariant}
47
-      >
48
-        <div style={{ width: "800px", height: "800px" }}>
104
+    <div style={pageStyle as React.CSSProperties}>
105
+      {Array.from({ length: 10 }).map((_, i) => (
106
+        <motion.div
107
+          key={i}
108
+          variants={starVariants}
109
+          style={baseStyle as React.CSSProperties}
110
+          animate={starVariant}
111
+          custom={{ delay: i * 0.05 }}
112
+          onAnimationStart={() => animationStarted(starVariant)}
113
+          onAnimationComplete={() => animationComplete(starVariant)}
114
+        >
115
+          <div style={{ width: "800px", height: "800px" }}>
49 116
             <Masked mask="Star" style={starStyle} />
50
-        </div>
51
-      </motion.div>
117
+          </div>
118
+        </motion.div>
119
+      ))}
120
+
52 121
     </div>
53 122
   );
54 123
 }
55 124
 
125
+const pageStyle = {
126
+  position: "relative",
127
+  width: "100%",
128
+  height: "100%",
129
+  display: "flex",
130
+  justifyContent: "center",
131
+  alignItems: "center",
132
+  overflow: "hidden",
133
+};
134
+
56 135
 const baseStyle = {
57 136
   position: "absolute",
58
-  top: "50%",
59
-  left: "50%",
137
+  top: "calc(50% - 400px)",
138
+  left: "calc(50% - 400px)",
139
+  zIndex: 1,
60 140
 };
61 141
 
62 142
 const starStyle = {
63 143
   position: "relative",
64 144
   width: "100%",
65 145
   height: "100%",
146
+  backgroundBlendMode: "multiply",
66 147
   backgroundImage:
67 148
     "radial-gradient(circle at center center,rgb(95, 247, 68),rgb(247, 255, 19)), repeating-radial-gradient(circle at center center,rgb(0, 255, 140),rgb(77, 247, 68), 10px, transparent 90px, transparent 10px)",
68
-  backgroundBlendMode: "multiply",
69 149
 };
70 150
 
151
+
71 152
 export default Welcome;

Loading…
Cancel
Save