index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import React from "react";
  2. import {
  3. useCurrentFrame,
  4. useVideoConfig,
  5. AbsoluteFill,
  6. interpolate,
  7. Easing,
  8. } from "remotion";
  9. import type { RemotionScene } from "../Root";
  10. import { THEMES } from "../base/theme/colors";
  11. import { Background } from "../base/components/background";
  12. import { SubtitleBar } from "../base/components/subtitle-bar";
  13. import { AnimatedText } from "../base/components/animated-text";
  14. import { Watermark } from "../base/components/watermark";
  15. import { ProductShowcase } from "./components/product-showcase";
  16. import { CTAOverlay } from "./components/cta-overlay";
  17. interface MarketingSceneProps {
  18. scene: RemotionScene;
  19. sceneIndex: number;
  20. totalScenes: number;
  21. totalFrames: number;
  22. title: string;
  23. }
  24. const MarketingScene: React.FC<MarketingSceneProps> = ({
  25. scene,
  26. sceneIndex,
  27. totalScenes,
  28. totalFrames,
  29. title,
  30. }) => {
  31. const frame = useCurrentFrame();
  32. const colors = THEMES.marketing;
  33. const fadeOpacity = interpolate(
  34. frame,
  35. [0, 8, scene.endFrame - scene.startFrame - 8, scene.endFrame - scene.startFrame],
  36. [0, 1, 1, 0],
  37. { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
  38. );
  39. const isLastScene = sceneIndex === totalScenes - 1;
  40. const featureKeyframes = scene.keyframes.filter(
  41. (kf) => kf.type === "text" || kf.type === "highlight"
  42. );
  43. return (
  44. <AbsoluteFill style={{ opacity: fadeOpacity }}>
  45. <Background
  46. colors={colors}
  47. sceneIndex={sceneIndex}
  48. totalScenes={totalScenes}
  49. />
  50. {/* Decorative glow */}
  51. <div
  52. style={{
  53. position: "absolute",
  54. top: "30%",
  55. left: "50%",
  56. width: 600,
  57. height: 600,
  58. borderRadius: 300,
  59. background: `radial-gradient(circle, ${colors.primary}22, transparent 70%)`,
  60. transform: "translate(-50%, -50%)",
  61. }}
  62. />
  63. {/* Brand label */}
  64. <div
  65. style={{
  66. position: "absolute",
  67. top: 40,
  68. left: 40,
  69. fontSize: 20,
  70. fontWeight: 700,
  71. color: "rgba(255,255,255,0.3)",
  72. fontFamily: "sans-serif",
  73. letterSpacing: 4,
  74. textTransform: "uppercase",
  75. }}
  76. >
  77. {title}
  78. </div>
  79. {/* Content */}
  80. <div
  81. style={{
  82. position: "absolute",
  83. top: 100,
  84. left: 80,
  85. right: 80,
  86. bottom: 140,
  87. display: "flex",
  88. flexDirection: "column",
  89. justifyContent: "center",
  90. alignItems: isLastScene ? "center" : "flex-start",
  91. gap: 36,
  92. overflow: "hidden",
  93. }}
  94. >
  95. {isLastScene ? (
  96. // CTA scene
  97. <CTAOverlay
  98. text={scene.narration}
  99. subtitle={featureKeyframes[0]?.content}
  100. color={colors.primaryLight}
  101. />
  102. ) : (
  103. <>
  104. <AnimatedText
  105. text={scene.narration}
  106. fontSize={44}
  107. color="white"
  108. fontWeight={700}
  109. delay={5}
  110. />
  111. {featureKeyframes.length > 0 && (
  112. <ProductShowcase
  113. features={featureKeyframes.map((kf) => kf.content)}
  114. accentColor={colors.primaryLight}
  115. />
  116. )}
  117. </>
  118. )}
  119. </div>
  120. {scene.wordTimestamps.length > 0 && (
  121. <SubtitleBar wordTimestamps={scene.wordTimestamps} />
  122. )}
  123. <Watermark />
  124. </AbsoluteFill>
  125. );
  126. };
  127. export default MarketingScene;