Browse Source

Des tests plutôt concluants pour faire des animations

feat/year-wrapped
Elias Sebbar 2 weeks ago
parent
commit
4fe6e39e4a

+ 28
- 22
vite-wrapped/deno.json View File

@@ -1,23 +1,29 @@
1 1
 {
2
-  "tasks": {
3
-    "dev": "deno run -A --node-modules-dir npm:vite",
4
-    "build": "deno run -A --node-modules-dir npm:vite build",
5
-    "preview": "deno run -A --node-modules-dir npm:vite preview",
6
-    "serve": "deno run --allow-net --allow-read jsr:@std/http@1/file-server dist/"
7
-  },
8
-  "compilerOptions": {
9
-    "lib": ["ES2020", "DOM", "DOM.Iterable"],
10
-    "jsx": "react-jsx",
11
-    "jsxImportSource": "react",
12
-    "jsxImportSourceTypes": "@types/react"
13
-  },
14
-  "imports": {
15
-    "@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.0",
16
-    "@types/react": "npm:@types/react@^18.3.12",
17
-    "@types/react-dom": "npm:@types/react-dom@^18.3.1",
18
-    "@vitejs/plugin-react-swc": "npm:@vitejs/plugin-react-swc@^3.7.1",
19
-    "react": "npm:react@^18.3.1",
20
-    "react-dom": "npm:react-dom@^18.3.1",
21
-    "vite": "npm:vite@^6.0.1"
22
-  }
23
-}
2
+    "nodeModulesDir": "auto",
3
+    "tasks": {
4
+        "dev": "deno run -A --node-modules-dir npm:vite",
5
+        "build": "deno run -A --node-modules-dir npm:vite build",
6
+        "preview": "deno run -A --node-modules-dir npm:vite preview",
7
+        "serve": "deno run --allow-net --allow-read jsr:@std/http@1/file-server dist/"
8
+    },
9
+    "compilerOptions": {
10
+        "lib": [
11
+            "ES2020",
12
+            "DOM",
13
+            "DOM.Iterable"
14
+        ],
15
+        "jsx": "react-jsx",
16
+        "jsxImportSource": "react",
17
+        "jsxImportSourceTypes": "@types/react"
18
+    },
19
+    "imports": {
20
+        "@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.0",
21
+        "@types/react": "npm:@types/react@^18.3.12",
22
+        "@types/react-dom": "npm:@types/react-dom@^18.3.1",
23
+        "@vitejs/plugin-react-swc": "npm:@vitejs/plugin-react-swc@^3.7.1",
24
+        "framer-motion": "npm:framer-motion@^11.15.0",
25
+        "react": "npm:react@^18.3.1",
26
+        "react-dom": "npm:react-dom@^18.3.1",
27
+        "vite": "npm:vite@^6.0.1"
28
+    }
29
+}

+ 31
- 0
vite-wrapped/deno.lock View File

@@ -2,9 +2,11 @@
2 2
   "version": "4",
3 3
   "specifiers": {
4 4
     "npm:@deno/vite-plugin@1": "1.0.2_vite@6.0.6",
5
+    "npm:@types/node@*": "22.5.4",
5 6
     "npm:@types/react-dom@^18.3.1": "18.3.5_@types+react@18.3.18",
6 7
     "npm:@types/react@^18.3.12": "18.3.18",
7 8
     "npm:@vitejs/plugin-react-swc@^3.7.1": "3.7.2_vite@6.0.6",
9
+    "npm:framer-motion@^11.15.0": "11.15.0_react@18.3.1_react-dom@18.3.1__react@18.3.1",
8 10
     "npm:react-dom@^18.3.1": "18.3.1_react@18.3.1",
9 11
     "npm:react@^18.3.1": "18.3.1",
10 12
     "npm:vite@*": "6.0.6",
@@ -208,6 +210,12 @@
208 210
     "@types/estree@1.0.6": {
209 211
       "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
210 212
     },
213
+    "@types/node@22.5.4": {
214
+      "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
215
+      "dependencies": [
216
+        "undici-types"
217
+      ]
218
+    },
211 219
     "@types/prop-types@15.7.14": {
212 220
       "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
213 221
     },
@@ -264,6 +272,16 @@
264 272
         "@esbuild/win32-x64"
265 273
       ]
266 274
     },
275
+    "framer-motion@11.15.0_react@18.3.1_react-dom@18.3.1__react@18.3.1": {
276
+      "integrity": "sha512-MLk8IvZntxOMg7lDBLw2qgTHHv664bYoYmnFTmE0Gm/FW67aOJk0WM3ctMcG+Xhcv+vh5uyyXwxvxhSeJzSe+w==",
277
+      "dependencies": [
278
+        "motion-dom",
279
+        "motion-utils",
280
+        "react",
281
+        "react-dom",
282
+        "tslib"
283
+      ]
284
+    },
267 285
     "fsevents@2.3.3": {
268 286
       "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
269 287
     },
@@ -276,6 +294,12 @@
276 294
         "js-tokens"
277 295
       ]
278 296
     },
297
+    "motion-dom@11.14.3": {
298
+      "integrity": "sha512-lW+D2wBy5vxLJi6aCP0xyxTxlTfiu+b+zcpVbGVFUxotwThqhdpPRSmX8xztAgtZMPMeU0WGVn/k1w4I+TbPqA=="
299
+    },
300
+    "motion-utils@11.14.3": {
301
+      "integrity": "sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ=="
302
+    },
279 303
     "nanoid@3.3.8": {
280 304
       "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="
281 305
     },
@@ -339,6 +363,12 @@
339 363
     "source-map-js@1.2.1": {
340 364
       "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
341 365
     },
366
+    "tslib@2.8.1": {
367
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
368
+    },
369
+    "undici-types@6.19.8": {
370
+      "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
371
+    },
342 372
     "vite@6.0.6": {
343 373
       "integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==",
344 374
       "dependencies": [
@@ -355,6 +385,7 @@
355 385
       "npm:@types/react-dom@^18.3.1",
356 386
       "npm:@types/react@^18.3.12",
357 387
       "npm:@vitejs/plugin-react-swc@^3.7.1",
388
+      "npm:framer-motion@^11.15.0",
358 389
       "npm:react-dom@^18.3.1",
359 390
       "npm:react@^18.3.1",
360 391
       "npm:vite@^6.0.1"

+ 15
- 0
vite-wrapped/public/star.svg View File

@@ -0,0 +1,15 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<svg
3
+    id="Calque_1"
4
+    data-name="Calque 1"
5
+    xmlns="http://www.w3.org/2000/svg"
6
+    viewBox="0 0 456.79 436.42"
7
+>
8
+    <defs>
9
+    <style></style>
10
+    </defs>
11
+    <polygon stroke="white" fill="#00000000"
12
+    stroke-width="1em"
13
+    points="372.66 431.54 231.13 359.66 91.67 435.5 116.3 278.68 1.08 169.49 157.83 144.45 226.07 1.13 298.32 142.47 455.72 163.08 343.62 275.48 372.66 431.54"
14
+    />
15
+</svg>

+ 7
- 38
vite-wrapped/src/App.css View File

@@ -1,41 +1,10 @@
1
+html,
2
+body,
1 3
 #root {
2
-  max-width: 1280px;
3
-  margin: 0 auto;
4
-  padding: 2rem;
4
+  height: 100%;
5
+  width: 100%;
6
+  margin: 0;
7
+  padding: 0;
8
+  font-family: sans-serif;
5 9
   text-align: center;
6 10
 }
7
-
8
-.logo {
9
-  height: 6em;
10
-  padding: 1.5em;
11
-  will-change: filter;
12
-}
13
-.logo:hover {
14
-  filter: drop-shadow(0 0 2em #646cffaa);
15
-}
16
-.logo.react:hover {
17
-  filter: drop-shadow(0 0 2em #61dafbaa);
18
-}
19
-
20
-@keyframes logo-spin {
21
-  from {
22
-    transform: rotate(0deg);
23
-  }
24
-  to {
25
-    transform: rotate(360deg);
26
-  }
27
-}
28
-
29
-@media (prefers-reduced-motion: no-preference) {
30
-  a:nth-of-type(2) .logo {
31
-    animation: logo-spin infinite 20s linear;
32
-  }
33
-}
34
-
35
-.card {
36
-  padding: 2em;
37
-}
38
-
39
-.read-the-docs {
40
-  color: #888;
41
-}

+ 55
- 27
vite-wrapped/src/App.tsx View File

@@ -1,37 +1,65 @@
1
-import './App.css'
1
+import "./App.css";
2 2
 // @deno-types="@types/react"
3
-import { useState } from 'react'
4
-// @ts-expect-error Unable to infer type at the moment
5
-import reactLogo from './assets/react.svg'
3
+import React, { useEffect, useState } from "react";
4
+
5
+import Canvas from "./layout/canvas.tsx";
6
+import Masked from "./components/mask/Masked.tsx";
7
+import { motion, useMotionValue, useMotionValueEvent } from "framer-motion";
8
+import { useAnimationFrame } from "framer-motion";
9
+import Welcome from "./steps/welcome.tsx";
10
+
11
+const steps = [
12
+  {
13
+    title: "Step 1",
14
+    content: "Content of step 1",
15
+  },
16
+  {
17
+    title: "Step 2",
18
+    content: "Content of step 2",
19
+  },
20
+  {
21
+    title: "Step 3",
22
+    content: "Content of step 3",
23
+  },
24
+];
6 25
 
7 26
 function App() {
8
-  const [count, setCount] = useState(0)
27
+  const [currentStep, setCurrentStep] = useState(0);
28
+  const animationDuration = 5000;
29
+  const animationProgress = useMotionValue(0);
30
+  const [animationFrame, setAnimationFrame] = useState(0);
31
+  const [initialAnimationFrame, setInitialAnimationFrame] = useState(0);
32
+
33
+  useAnimationFrame((time, delta) => {
34
+    if (time - initialAnimationFrame >= animationDuration) {
35
+      setInitialAnimationFrame(time);
36
+      setCurrentStep((prev) => prev + 1);
37
+      animationProgress.set(0);
38
+    } else {
39
+      animationProgress.set(animationProgress.get() + delta);
40
+    }
41
+    setAnimationFrame(time);
42
+  });
9 43
 
10 44
   return (
11 45
     <>
12
-      <img src="/vite-deno.svg" alt="Vite with Deno" />
13
-      <div>
14
-        <a href="https://vite.dev" target="_blank">
15
-          <img src="/vite.svg" className="logo" alt="Vite logo" />
16
-        </a>
17
-        <a href="https://reactjs.org" target="_blank">
18
-          <img src={reactLogo} className="logo react" alt="React logo" />
19
-        </a>
20
-      </div>
21
-      <h1>Elo</h1>
22
-      <div className="card">
23
-        <button onClick={() => setCount((count) => count + 1)}>
24
-          count is {count} (looking great!)
25
-        </button>
26
-        <p>
27
-          Edit <code>src/App.tsx</code> and save to test HMR
28
-        </p>
46
+      <div style={page as React.CSSProperties}>
47
+        <Welcome progress={animationProgress} />
29 48
       </div>
30
-      <p className="read-the-docs">
31
-        Click on the Vite and React logos to learn more
32
-      </p>
33 49
     </>
34
-  )
50
+  );
35 51
 }
36 52
 
37
-export default App
53
+const page = {
54
+  display: "flex",
55
+  position: "relative",
56
+  justifyContent: "center",
57
+  alignItems: "center",
58
+  margin: 0,
59
+  padding: 0,
60
+  width: "100%",
61
+  height: "100%",
62
+  flexDirection: "column",
63
+  backgroundColor: "#050505",
64
+};
65
+export default App;

+ 44
- 0
vite-wrapped/src/components/Progress.tsx View File

@@ -0,0 +1,44 @@
1
+
2
+type ProgressProps = {
3
+    step: number,
4
+    steps: number,
5
+    progress: number,
6
+    length: number,
7
+}
8
+
9
+const Progress = ({ step, steps, progress, length }: ProgressProps) => {
10
+    const progressWidth = (progress / length) * 100;
11
+    return (
12
+        <div style={progressContainer as React.CSSProperties}>
13
+            <div style={progressBar}>
14
+                <div style={{ ...progressBarInner, width: `${progressWidth}%` }} />
15
+            </div>
16
+            <div style={progressText as React.CSSProperties}>
17
+                {step + 1} / {steps}
18
+            </div>
19
+        </div>
20
+    );
21
+};
22
+
23
+const progressContainer = {
24
+    display: "flex",
25
+    flexDirection: "column",
26
+    alignItems: "center",
27
+    width: "100%",
28
+    marginBottom: "1rem",
29
+};
30
+
31
+const progressBar = {
32
+    width: "100%",
33
+    height: "1rem",
34
+    backgroundColor: "rgba(0, 0, 0, 0.2)",
35
+    borderRadius: "0.5rem",
36
+};
37
+
38
+const progressBarInner = {
39
+    height: "100%",
40
+    backgroundColor: "rgba(0, 0, 0, 0.5)",
41
+    borderRadius: "0.5rem",
42
+};
43
+
44
+export default Progress;

+ 42
- 0
vite-wrapped/src/components/mask/Masked.tsx View File

@@ -0,0 +1,42 @@
1
+import React from "react";
2
+
3
+type Mask = "Star";
4
+
5
+function Masked({
6
+  children,
7
+  mask,
8
+  style,
9
+}: {
10
+  children?: React.ReactNode;
11
+  mask: Mask;
12
+  style: React.CSSProperties;
13
+}) {
14
+  const [maskUrl, setMaskUrl] = React.useState<String>({});
15
+
16
+  React.useEffect(() => {
17
+    if (mask === "Star") {
18
+      setMaskUrl("/star.svg");
19
+    }
20
+  }, [mask]);
21
+
22
+  return (
23
+    <div
24
+      style={{
25
+        ...{
26
+          maskImage: `url(${maskUrl})`,
27
+          maskSize: "cover",
28
+          maskRepeat: "no-repeat",
29
+          maskPosition: "center",
30
+          maskClip: "content-box",
31
+          width: "100%",
32
+          height: "100%",
33
+        },
34
+        ...style,
35
+      }}
36
+    >
37
+        {children}
38
+    </div>
39
+  );
40
+}
41
+
42
+export default Masked;

+ 20
- 0
vite-wrapped/src/layout/canvas.tsx View File

@@ -0,0 +1,20 @@
1
+
2
+function Canvas({children}: {children: React.ReactNode}) {
3
+  return (
4
+    <div style={canvas}>
5
+        {children}
6
+    </div>
7
+  );
8
+}
9
+
10
+const canvas = {
11
+  display: "flex",
12
+  justifyContent: "center",
13
+  alignItems: "center",
14
+  width: "100%",
15
+  height: "100%",
16
+  backgroundColor: "rgba(0, 0, 0, 0.2)",
17
+};
18
+
19
+
20
+export default Canvas;

+ 5
- 0
vite-wrapped/src/steps/stepProps.ts View File

@@ -0,0 +1,5 @@
1
+import { MotionValue } from "framer-motion";
2
+
3
+export type StepProps = {
4
+    progress: MotionValue<number>;
5
+}

+ 71
- 0
vite-wrapped/src/steps/welcome.tsx View File

@@ -0,0 +1,71 @@
1
+import { motion, MotionConfig, Variants } from "framer-motion";
2
+import { StepProps } from "./stepProps.ts";
3
+import Masked from "../components/mask/Masked.tsx";
4
+import React, { useState, useEffect } from "react";
5
+
6
+const starVariants: Variants = {
7
+  enter: {
8
+    top: "80%",
9
+    left: "80%",
10
+    rotate: 45,
11
+    transition: {
12
+      duration: 2,
13
+      repeat: 0,
14
+      delay: 1,
15
+    },
16
+  },
17
+  idle: {
18
+    top: "80.1%",
19
+    left: "80.1%",
20
+    scale: 1.01,
21
+    rotate: 45,
22
+    transition: {
23
+      duration: 0.5,
24
+      repeat: Infinity,
25
+      repeatType: "reverse",
26
+    },
27
+  },
28
+};
29
+
30
+function Welcome({ progress }: StepProps) {
31
+  const [starVariant, setStarVariant] = useState("enter");
32
+
33
+  useEffect(() => {
34
+    if (progress.get() > 3000) {
35
+      setStarVariant("idle");
36
+    } else {
37
+      setStarVariant("enter");
38
+    }
39
+  }, [progress.get()]);
40
+
41
+  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" }}>
49
+            <Masked mask="Star" style={starStyle} />
50
+        </div>
51
+      </motion.div>
52
+    </div>
53
+  );
54
+}
55
+
56
+const baseStyle = {
57
+  position: "absolute",
58
+  top: "50%",
59
+  left: "50%",
60
+};
61
+
62
+const starStyle = {
63
+  position: "relative",
64
+  width: "100%",
65
+  height: "100%",
66
+  backgroundImage:
67
+    "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
+};
70
+
71
+export default Welcome;

+ 5
- 0
vite-wrapped/tsconfig.json View File

@@ -0,0 +1,5 @@
1
+{
2
+  "compilerOptions": {
3
+    "lib": ["esnext", "dom"]
4
+  }
5
+}

Loading…
Cancel
Save