@evatril/video-templates 2.0.7 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/package.json +1 -1
  2. package/public/video-images/03022026-bg-2.webp +0 -0
  3. package/public/video-images/03022026-bg-3.webp +0 -0
  4. package/public/video-images/03022026-bg-4.webp +0 -0
  5. package/public/video-images/03022026-bg-5.webp +0 -0
  6. package/public/video-images/03022026-bg.webp +0 -0
  7. package/public/video-images/03022026-krishnaradha-2.webp +0 -0
  8. package/public/video-images/03022026-krishnaradha-4.webp +0 -0
  9. package/public/video-images/03022026-krishnaradha-5.webp +0 -0
  10. package/public/video-images/03022026-krishnaradha.webp +0 -0
  11. package/public/video-images/28012026-BottomLeftFlower.webp +0 -0
  12. package/public/video-images/28012026-BottomRightFlower.webp +0 -0
  13. package/public/video-images/28012026-FlowerBorder.webp +0 -0
  14. package/public/video-images/28012026-Ganesh.webp +0 -0
  15. package/public/video-images/28012026-TopBorder.webp +0 -0
  16. package/public/video-images/28012026-TopLeftFlower.webp +0 -0
  17. package/public/video-images/28012026-TopRightFlower.webp +0 -0
  18. package/public/video-images/28012026-bg.webp +0 -0
  19. package/public/video-images/28012026-border.webp +0 -0
  20. package/public/video-images/28012026-frame.webp +0 -0
  21. package/public/video-images/wedding2.mp3 +0 -0
  22. package/src/Invitations/Elements/BlowingLeaves28012026.jsx +68 -0
  23. package/src/Invitations/Elements/FlowerReveal_TL_BR_28012026.jsx +114 -0
  24. package/src/Invitations/Elements/FlowerReveal_TR_BL_28012026.jsx +116 -0
  25. package/src/Invitations/Elements/GaneshBorder28012026.jsx +95 -0
  26. package/src/Invitations/Elements/GaneshGoldenHalo.jsx +90 -0
  27. package/src/Invitations/Elements/HeartDraw28012026.jsx +51 -0
  28. package/src/Invitations/Elements/HeartFlight28012026.jsx +72 -0
  29. package/src/Invitations/Elements/HoldSlide.jsx +103 -0
  30. package/src/Invitations/Elements/LetterReveal28012026.jsx +47 -0
  31. package/src/Invitations/Elements/PageFlipTransition.jsx +180 -0
  32. package/src/Invitations/Elements/{SmoothRevealFromTop.jsx → SmoothRevealFromTop20012026.jsx} +1 -1
  33. package/src/Invitations/Elements/WordReveal28012026.jsx +92 -0
  34. package/src/Invitations/Frames/F03022026_01.jsx +214 -0
  35. package/src/Invitations/Frames/F03022026_02.jsx +312 -0
  36. package/src/Invitations/Frames/F03022026_03.jsx +332 -0
  37. package/src/Invitations/Frames/F03022026_04.jsx +300 -0
  38. package/src/Invitations/Frames/F03022026_05.jsx +235 -0
  39. package/src/Invitations/Frames/F20012026_01.jsx +0 -2
  40. package/src/Invitations/Frames/F20012026_02.jsx +8 -8
  41. package/src/Invitations/Frames/F20012026_03.jsx +8 -8
  42. package/src/Invitations/Frames/F28012026_01.jsx +51 -0
  43. package/src/Invitations/Frames/F28012026_02.jsx +246 -0
  44. package/src/Invitations/Frames/F28012026_03.jsx +268 -0
  45. package/src/Invitations/Frames/F28012026_04.jsx +275 -0
  46. package/src/Invitations/Frames/F28012026_05.jsx +179 -0
  47. package/src/Invitations/Themes/T03022026_01.jsx +269 -0
  48. package/src/Invitations/Themes/T20012026_01.jsx +21 -3
  49. package/src/Invitations/Themes/T28012026_01.jsx +241 -0
  50. package/src/compositions.jsx +20 -2
  51. package/src/index.js +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evatril/video-templates",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "remotion": {
Binary file
@@ -0,0 +1,68 @@
1
+ import React, { useMemo } from "react";
2
+ import { useCurrentFrame, useVideoConfig, interpolate } from "remotion";
3
+
4
+ export const BlowingLeaves28012026 = ({ startAfter = 2, count = 30 }) => {
5
+ const frame = useCurrentFrame();
6
+ const { fps = 30 } = useVideoConfig();
7
+
8
+ const delayFrames = fps * startAfter;
9
+
10
+ // 🍃 Generate leaves once
11
+ const leaves = useMemo(() => {
12
+ return Array.from({ length: count }).map(() => ({
13
+ x: Math.random() * 100,
14
+ y: Math.random() * 100,
15
+ size: 20 + Math.random() * 25,
16
+ speedX: 0.3 + Math.random() * 0.8,
17
+ speedY: 0.1 + Math.random() * 0.4,
18
+ drift: 40 + Math.random() * 80,
19
+ rotateSpeed: 0.3 + Math.random() * 1,
20
+ phase: Math.random() * 200,
21
+ }));
22
+ }, [count]);
23
+
24
+ return (
25
+ <>
26
+ {leaves.map((l, i) => {
27
+ const local = frame - delayFrames;
28
+
29
+ const appear = interpolate(local, [0, fps], [0, 1], {
30
+ extrapolateLeft: "clamp",
31
+ extrapolateRight: "clamp",
32
+ });
33
+
34
+ // Horizontal flow
35
+ const moveX = ((local * l.speedX + l.phase) % 120) - 10;
36
+
37
+ // Gentle vertical float
38
+ const moveY =
39
+ l.y +
40
+ Math.sin((local + l.phase) / 40) * 20 +
41
+ local * l.speedY;
42
+
43
+ // Rotation
44
+ const rotate = Math.sin((local + l.phase) / 30) * 25;
45
+
46
+ return (
47
+ <div
48
+ key={i}
49
+ style={{
50
+ position: "absolute",
51
+ left: `${moveX}%`,
52
+ top: `${moveY % 110}%`,
53
+ fontSize: l.size,
54
+ opacity: appear,
55
+ transform: `
56
+ translate(-50%, -50%)
57
+ rotate(${rotate}deg)
58
+ `,
59
+ pointerEvents: "none",
60
+ }}
61
+ >
62
+ 🍁
63
+ </div>
64
+ );
65
+ })}
66
+ </>
67
+ );
68
+ };
@@ -0,0 +1,114 @@
1
+ import React from "react";
2
+ import {
3
+ Img,
4
+ staticFile,
5
+ useCurrentFrame,
6
+ useVideoConfig,
7
+ interpolate,
8
+ Easing,
9
+ } from "remotion";
10
+
11
+ export const FlowerReveal_TL_BR_28012026 = ({ startAfter = 0 }) => {
12
+ const frame = useCurrentFrame();
13
+ const { fps } = useVideoConfig();
14
+
15
+ const delayFrames = fps * startAfter;
16
+ const bloomDuration = fps * 2.5; // 🌸 slower cinematic bloom
17
+ const localFrame = frame - delayFrames;
18
+
19
+ const progress = interpolate(localFrame, [0, bloomDuration], [0, 1], {
20
+ extrapolateRight: "clamp",
21
+ easing: Easing.out(Easing.cubic),
22
+ });
23
+
24
+ // 🌿 Opacity
25
+ const opacity = interpolate(localFrame, [0, bloomDuration * 0.5], [0, 1], {
26
+ extrapolateLeft: "clamp",
27
+ extrapolateRight: "clamp",
28
+ });
29
+
30
+ // 🌷 Scale
31
+ const scale = interpolate(localFrame, [0, bloomDuration], [0.6, 1], {
32
+ extrapolateLeft: "clamp",
33
+ extrapolateRight: "clamp",
34
+ easing: Easing.out(Easing.cubic),
35
+ });
36
+
37
+ // 🌺 Gentle settle
38
+ const settle = interpolate(
39
+ localFrame,
40
+ [bloomDuration * 0.7, bloomDuration],
41
+ [1.04, 1],
42
+ {
43
+ extrapolateRight: "clamp",
44
+ }
45
+ );
46
+
47
+ // 🍃 Slide in from corners
48
+ const slideTLX = interpolate(localFrame, [0, bloomDuration], [-180, 0], {
49
+ extrapolateRight: "clamp",
50
+ easing: Easing.out(Easing.cubic),
51
+ });
52
+
53
+ const slideTLY = interpolate(localFrame, [0, bloomDuration], [-180, 0], {
54
+ extrapolateRight: "clamp",
55
+ easing: Easing.out(Easing.cubic),
56
+ });
57
+
58
+ const slideBRX = interpolate(localFrame, [0, bloomDuration], [180, 0], {
59
+ extrapolateRight: "clamp",
60
+ easing: Easing.out(Easing.cubic),
61
+ });
62
+
63
+ const slideBRY = interpolate(localFrame, [0, bloomDuration], [180, 0], {
64
+ extrapolateRight: "clamp",
65
+ easing: Easing.out(Easing.cubic),
66
+ });
67
+
68
+ // 🌿 Organic sway
69
+ const swing = Math.sin(frame / 18) * 0.6;
70
+
71
+ const finalScale = scale * settle;
72
+
73
+ return (
74
+ <>
75
+ {/* 🌸 TOP LEFT */}
76
+ <Img
77
+ src={staticFile("video-images/28012026-TopLeftFlower.webp")}
78
+ style={{
79
+ position: "absolute",
80
+ top: "-170px",
81
+ left: "-50px",
82
+ width: "100%",
83
+ opacity,
84
+ transformOrigin: "top left",
85
+ transform: `
86
+ translate(${slideTLX}px, ${slideTLY}px)
87
+ scale(${finalScale})
88
+ rotate(${-swing}deg)
89
+ `,
90
+ pointerEvents: "none",
91
+ }}
92
+ />
93
+
94
+ {/* 🌸 BOTTOM RIGHT */}
95
+ <Img
96
+ src={staticFile("video-images/28012026-BottomRightFlower.webp")}
97
+ style={{
98
+ position: "absolute",
99
+ bottom: "-300px",
100
+ right: -10,
101
+ width: "100%",
102
+ opacity,
103
+ transformOrigin: "bottom right",
104
+ transform: `
105
+ translate(${slideBRX}px, ${slideBRY}px)
106
+ scale(${finalScale})
107
+ rotate(${swing}deg)
108
+ `,
109
+ pointerEvents: "none",
110
+ }}
111
+ />
112
+ </>
113
+ );
114
+ };
@@ -0,0 +1,116 @@
1
+ import React from "react";
2
+ import {
3
+ Img,
4
+ staticFile,
5
+ useCurrentFrame,
6
+ useVideoConfig,
7
+ interpolate,
8
+ Easing,
9
+ } from "remotion";
10
+
11
+ export const FlowerReveal_TR_BL_28012026 = () => {
12
+ const frame = useCurrentFrame();
13
+ const { fps } = useVideoConfig();
14
+
15
+ const bloomDuration = fps * 2.5;
16
+
17
+ // Main bloom progress
18
+ const progress = interpolate(frame, [0, bloomDuration], [0, 1], {
19
+ extrapolateRight: "clamp",
20
+ easing: Easing.out(Easing.quad),
21
+ });
22
+
23
+ // 🌿 Opacity
24
+ const opacity = interpolate(frame, [0, bloomDuration * 0.4], [0, 1], {
25
+ extrapolateRight: "clamp",
26
+ });
27
+
28
+ // Bloom scale (petal opening feel)
29
+ const scale = interpolate(
30
+ frame,
31
+ [0, bloomDuration * 0.7, bloomDuration],
32
+ [0.4, 1.05, 1],
33
+ {
34
+ extrapolateRight: "clamp",
35
+ easing: Easing.out(Easing.cubic),
36
+ }
37
+ );
38
+
39
+ // 🌺 Soft settle
40
+ const settle = interpolate(
41
+ frame,
42
+ [bloomDuration * 0.7, bloomDuration],
43
+ [1.04, 1],
44
+ {
45
+ extrapolateRight: "clamp",
46
+ }
47
+ );
48
+
49
+ // 🍃 Slide positions
50
+ const slideTRX = interpolate(frame, [0, bloomDuration], [180, 0], {
51
+ extrapolateRight: "clamp",
52
+ easing: Easing.out(Easing.cubic),
53
+ });
54
+
55
+ const slideTRY = interpolate(frame, [0, bloomDuration], [-180, 0], {
56
+ extrapolateRight: "clamp",
57
+ easing: Easing.out(Easing.cubic),
58
+ });
59
+
60
+ const slideBLX = interpolate(frame, [0, bloomDuration], [-180, 0], {
61
+ extrapolateRight: "clamp",
62
+ easing: Easing.out(Easing.cubic),
63
+ });
64
+
65
+ const slideBLY = interpolate(frame, [0, bloomDuration], [180, 0], {
66
+ extrapolateRight: "clamp",
67
+ easing: Easing.out(Easing.cubic),
68
+ });
69
+
70
+ // 🌿 Gentle sway
71
+ const swing = Math.sin(frame / 18) * 0.6;
72
+
73
+ const finalScale = scale * settle;
74
+
75
+ return (
76
+ <>
77
+ {/* 🌸 TOP RIGHT */}
78
+ <Img
79
+ src={staticFile("video-images/28012026-TopRightFlower.webp")}
80
+ style={{
81
+ position: "absolute",
82
+ top: "-130px",
83
+ right: 0,
84
+ width: "55%",
85
+ opacity,
86
+ transformOrigin: "top right",
87
+ transform: `
88
+ translate(${slideTRX}px, ${slideTRY}px)
89
+ scale(${finalScale})
90
+ rotate(${swing}deg)
91
+ `,
92
+ pointerEvents: "none",
93
+ }}
94
+ />
95
+
96
+ {/* 🌸 BOTTOM LEFT */}
97
+ <Img
98
+ src={staticFile("video-images/28012026-BottomLeftFlower.webp")}
99
+ style={{
100
+ position: "absolute",
101
+ bottom: "-250px",
102
+ left: "-80px",
103
+ width: "100%",
104
+ opacity,
105
+ transformOrigin: "bottom left",
106
+ transform: `
107
+ translate(${slideBLX}px, ${slideBLY}px)
108
+ scale(${finalScale})
109
+ rotate(${-swing}deg)
110
+ `,
111
+ pointerEvents: "none",
112
+ }}
113
+ />
114
+ </>
115
+ );
116
+ };
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import {
3
+ Img,
4
+ staticFile,
5
+ useCurrentFrame,
6
+ useVideoConfig,
7
+ interpolate,
8
+ Easing,
9
+ } from "remotion";
10
+
11
+ export const GaneshBorder28012026 = () => {
12
+ const frame = useCurrentFrame();
13
+ const { fps } = useVideoConfig();
14
+
15
+ const borderDelay = fps * 0.5; // 0.5 second delay
16
+
17
+ const borderProgress = interpolate(
18
+ frame,
19
+ [borderDelay, borderDelay + fps * 3],
20
+ [0, 1],
21
+ {
22
+ extrapolateLeft: "clamp",
23
+ extrapolateRight: "clamp",
24
+ easing: Easing.out(Easing.cubic),
25
+ }
26
+ );
27
+
28
+
29
+ const borderScale = interpolate(borderProgress, [0, 1], [0.9, 1]);
30
+ const borderOpacity = borderProgress;
31
+
32
+ const ganeshProgress = interpolate(
33
+ frame,
34
+ [fps * 0.5, fps * 2.5],
35
+ [0, 1],
36
+ {
37
+ extrapolateLeft: "clamp",
38
+ extrapolateRight: "clamp",
39
+ easing: Easing.inOut(Easing.cubic),
40
+ }
41
+ );
42
+
43
+ const ganeshScale = interpolate(ganeshProgress, [0, 1], [0.45, 1]);
44
+ const ganeshOpacity = ganeshProgress;
45
+
46
+ /* 🌿 Gentle border rotation */
47
+ // const rotate = Math.max(0, frame - delayFrames) * 0.20;
48
+ const rotate = frame * 0.15;
49
+ return (
50
+ <div
51
+ style={{
52
+ position: "absolute",
53
+ top: "10%",
54
+ width: "100%",
55
+ display: "flex",
56
+ justifyContent: "center",
57
+ pointerEvents: "none",
58
+ zIndex: 2,
59
+ }}
60
+ >
61
+ <div style={{ width: "100%", position: "relative" }}>
62
+ {/* 🌸 BORDER */}
63
+ <Img
64
+ src={staticFile("video-images/28012026-FlowerBorder.webp")}
65
+ style={{
66
+ width: "100%",
67
+ transformOrigin: "50% 50%",
68
+ opacity: borderOpacity,
69
+ transform: `
70
+ scale(${borderScale})
71
+ rotate(${rotate}deg)
72
+ `,
73
+ }}
74
+ />
75
+
76
+ {/* 🕉 GANESH SMOOTH ENTRY */}
77
+ <Img
78
+ src={staticFile("video-images/28012026-Ganesh.webp")}
79
+ style={{
80
+ position: "absolute",
81
+ left: "53%",
82
+ top: "45%",
83
+ width: "55%",
84
+ opacity: ganeshOpacity,
85
+ transformOrigin: "50% 50%",
86
+ transform: `
87
+ translate(-50%, -50%)
88
+ scale(${ganeshScale})
89
+ `,
90
+ }}
91
+ />
92
+ </div>
93
+ </div>
94
+ );
95
+ };
@@ -0,0 +1,90 @@
1
+ import React from "react";
2
+ import {
3
+ Img,
4
+ staticFile,
5
+ useCurrentFrame,
6
+ useVideoConfig,
7
+ interpolate,
8
+ Easing,
9
+ } from "remotion";
10
+
11
+ export const GaneshGoldenHalo = () => {
12
+ const frame = useCurrentFrame();
13
+ const { fps } = useVideoConfig();
14
+
15
+ /* 🌸 HALO BLOOM FIRST */
16
+ const haloProgress = interpolate(frame, [0, fps * 2], [0, 1], {
17
+ extrapolateLeft: "clamp",
18
+ extrapolateRight: "clamp",
19
+ easing: Easing.inOut(Easing.cubic),
20
+ });
21
+
22
+ const haloScale = interpolate(haloProgress, [0, 1], [0, 1]);
23
+
24
+ /* 🕉 GANESH APPEARS AFTER HALO */
25
+ const ganeshDelay = fps * 1;
26
+
27
+ const ganeshProgress = interpolate(frame - ganeshDelay, [0, fps * 2], [0, 1], {
28
+ extrapolateLeft: "clamp",
29
+ extrapolateRight: "clamp",
30
+ easing: Easing.inOut(Easing.cubic),
31
+ });
32
+
33
+ const ganeshScale = interpolate(ganeshProgress, [0, 1], [0.45, 1]);
34
+ const ganeshOpacity = ganeshProgress;
35
+
36
+ /* 🌿 HALO ROTATION (LOCKED CENTER) */
37
+ const rotate = frame * 0.15;
38
+
39
+ return (
40
+ <div
41
+ style={{
42
+ position: "absolute",
43
+ inset: 0,
44
+ display: "flex",
45
+ justifyContent: "center",
46
+ alignItems: "center",
47
+ pointerEvents: "none",
48
+ zIndex: 2,
49
+ }}
50
+ >
51
+ <div style={{ position: "relative", width: "100%", height: "100%" }}>
52
+ {/* 🌸 HALO (isolated rotation container) */}
53
+ <div
54
+ style={{
55
+ position: "absolute",
56
+ left: "50%",
57
+ top: "45%",
58
+ transform: "translate(-50%, -50%)",
59
+ }}
60
+ >
61
+ <Img
62
+ src={staticFile("video-images/30012026-Ganesh_golden_halo2.png")}
63
+ style={{
64
+ width: "100%",
65
+ transformOrigin: "50% 50%",
66
+ transform: `scale(${haloScale}) rotate(${rotate}deg)`,
67
+ }}
68
+ />
69
+ </div>
70
+
71
+ {/* 🕉 GANESH */}
72
+ <Img
73
+ src={staticFile("video-images/28012026-Ganesh.png")}
74
+ style={{
75
+ position: "absolute",
76
+ left: "50%",
77
+ top: "55%",
78
+ width: "55%",
79
+ opacity: ganeshOpacity,
80
+ transformOrigin: "50% 50%",
81
+ transform: `
82
+ translate(-47%, -50%)
83
+ scale(${ganeshScale})
84
+ `,
85
+ }}
86
+ />
87
+ </div>
88
+ </div>
89
+ );
90
+ };
@@ -0,0 +1,51 @@
1
+ import React, { useRef, useLayoutEffect, useState } from "react";
2
+ import { useCurrentFrame, interpolate, Easing } from "remotion";
3
+
4
+ export const HeartDraw28012026 = ({ startAfter = 30 }) => {
5
+ const frame = useCurrentFrame();
6
+ const pathRef = useRef(null);
7
+ const [length, setLength] = useState(0);
8
+
9
+ // Measure BEFORE paint (no blink)
10
+ useLayoutEffect(() => {
11
+ if (pathRef.current) {
12
+ const total = pathRef.current.getTotalLength();
13
+ setLength(total);
14
+ }
15
+ }, []);
16
+
17
+ const localFrame = frame - startAfter;
18
+
19
+ const progress = interpolate(localFrame, [0, 40], [0, 1], {
20
+ extrapolateLeft: "clamp",
21
+ extrapolateRight: "clamp",
22
+ easing: Easing.out(Easing.ease),
23
+ });
24
+
25
+ return (
26
+ <svg
27
+ width="720"
28
+ height="700"
29
+ viewBox="0 0 512 512"
30
+ style={{
31
+ position: "absolute",
32
+ zIndex: 0,
33
+ }}
34
+ >
35
+ <path
36
+ ref={pathRef}
37
+ d="M256 448s-168-104-168-240c0-53 43-96 96-96 33 0 62 16 72 40 10-24 39-40 72-40 53 0 96 43 96 96 0 136-168 240-168 240z"
38
+ fill="none"
39
+ stroke="#D4A24C"
40
+ strokeWidth={8}
41
+ strokeLinecap="round"
42
+ strokeLinejoin="round"
43
+ strokeDasharray={length || 1}
44
+ strokeDashoffset={(length || 1) * (1 - progress)}
45
+ style={{
46
+ opacity: length ? 1 : 0, // 👈 hide until measured
47
+ }}
48
+ />
49
+ </svg>
50
+ );
51
+ };
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { useCurrentFrame, useVideoConfig, interpolate, Easing } from "remotion";
3
+
4
+ export const HeartFlight28012026 = ({
5
+ emoji = "💖",
6
+ startAfter = 3,
7
+ }) => {
8
+ const frame = useCurrentFrame();
9
+ const { fps, width, height } = useVideoConfig();
10
+
11
+ const GLOBAL_DELAY = fps * startAfter;
12
+
13
+ const hearts = [
14
+ { sx: 0.1, sy: 0.9, ex: 0.8, ey: 0.2, delay: 0 },
15
+ { sx: 0.9, sy: 0.85, ex: 0.2, ey: 0.1, delay: 20 },
16
+ { sx: 0.3, sy: 1.0, ex: 0.7, ey: 0.3, delay: 40 },
17
+ { sx: 0.4, sy: 0.9, ex: 0.8, ey: 0.2, delay: 10 },
18
+ { sx: 0.5, sy: 0.85, ex: 0.2, ey: 0.1, delay: 30 },
19
+ { sx: 0.8, sy: 1.0, ex: 0.7, ey: 0.3, delay: 50 },
20
+ ];
21
+
22
+ return (
23
+ <>
24
+ {hearts.map((h, i) => {
25
+ const localFrame = frame - GLOBAL_DELAY - h.delay;
26
+
27
+ const progress = interpolate(localFrame, [0, fps * 6], [0, 1], {
28
+ extrapolateLeft: "clamp",
29
+ extrapolateRight: "clamp",
30
+ easing: Easing.inOut(Easing.ease),
31
+ });
32
+
33
+ const x = interpolate(progress, [0, 1], [
34
+ h.sx * width,
35
+ h.ex * width,
36
+ ]);
37
+
38
+ const y =
39
+ interpolate(progress, [0, 1], [
40
+ h.sy * height,
41
+ h.ey * height,
42
+ ]) +
43
+ Math.sin(frame / 10 + i * 20) * 25;
44
+
45
+ const scale = interpolate(progress, [0, 0.1, 0.9, 1], [0, 1, 1, 0.6]);
46
+
47
+ const opacity = progress > 0.05 && progress < 0.95 ? 1 : 0;
48
+
49
+ return (
50
+ <div
51
+ key={i}
52
+ style={{
53
+ position: "absolute",
54
+ left: x,
55
+ top: y,
56
+ fontSize: 70,
57
+ opacity,
58
+ transform: `
59
+ translate(-50%, -50%)
60
+ scale(${scale})
61
+ rotate(${Math.sin(localFrame / 15 + i) * 15}deg)
62
+ `,
63
+ pointerEvents: "none",
64
+ }}
65
+ >
66
+ {emoji}
67
+ </div>
68
+ );
69
+ })}
70
+ </>
71
+ );
72
+ };