diff --git a/public/template-media/LogoMotion3D-16x9.mp4 b/public/template-media/LogoMotion3D-16x9.mp4
new file mode 100644
index 0000000..db52167
Binary files /dev/null and b/public/template-media/LogoMotion3D-16x9.mp4 differ
diff --git a/public/template-media/LogoMotion3D-16x9.png b/public/template-media/LogoMotion3D-16x9.png
new file mode 100644
index 0000000..55062b3
Binary files /dev/null and b/public/template-media/LogoMotion3D-16x9.png differ
diff --git a/public/template-media/LogoMotion3D-1x1.mp4 b/public/template-media/LogoMotion3D-1x1.mp4
new file mode 100644
index 0000000..d57ae26
Binary files /dev/null and b/public/template-media/LogoMotion3D-1x1.mp4 differ
diff --git a/public/template-media/LogoMotion3D-1x1.png b/public/template-media/LogoMotion3D-1x1.png
new file mode 100644
index 0000000..f97aa68
Binary files /dev/null and b/public/template-media/LogoMotion3D-1x1.png differ
diff --git a/public/template-media/LogoMotion3D-9x16.mp4 b/public/template-media/LogoMotion3D-9x16.mp4
new file mode 100644
index 0000000..e82b967
Binary files /dev/null and b/public/template-media/LogoMotion3D-9x16.mp4 differ
diff --git a/public/template-media/LogoMotion3D-9x16.png b/public/template-media/LogoMotion3D-9x16.png
new file mode 100644
index 0000000..ebe8349
Binary files /dev/null and b/public/template-media/LogoMotion3D-9x16.png differ
diff --git a/public/template-media/LogoMotion3D.mp4 b/public/template-media/LogoMotion3D.mp4
new file mode 100644
index 0000000..db52167
Binary files /dev/null and b/public/template-media/LogoMotion3D.mp4 differ
diff --git a/scripts/seed_remotion_templates.py b/scripts/seed_remotion_templates.py
index d527db6..3de22e7 100644
--- a/scripts/seed_remotion_templates.py
+++ b/scripts/seed_remotion_templates.py
@@ -78,6 +78,8 @@ T = [
[("appName","نام اپلیکیشن","اپلیکیشن شما"),("tagline","شعار","تجربهای روان، سریع و زیبا"),("cta","دکمه","همین حالا دانلود کنید")],("#3b82f6","#8b5cf6","#f4f5f7")),
("CharacterStory","fr-character-story","داستان شخصیتی (۱۳ صحنه)","روایت داستان شما در سیزده صحنهٔ متحرک با شخصیت؛ تصویرسازی مدرن و مینیمال، صحنهها کاملاً قابل ویرایش و انعطافپذیر",39,
CS_TEXTS,("#cf8a76","#6f9d96","#ece4d6")),
+ ("LogoMotion3D","fr-logo-motion-3d","موشن لوگو سهبعدی","نمایش سینمایی و سهبعدی لوگو با کارت فلزی، پرتوهای نور، درخشش و افکت لنز؛ لوگو و متن قابل ویرایش",5,
+ [("brandText","نام برند","برند شما"),("tagline","شعار","شعار شما اینجا")],("#38bdf8","#818cf8","#0a0e1a")),
]
# Optional Media (image) content elements per template — these surface in the
@@ -85,17 +87,19 @@ T = [
MEDIA = {
"GlitterReveal": [("logoUrl", "لوگو (تصویر دلخواه)")],
"AppShowcase3D": [("screenUrl", "تصویر اپلیکیشن (اسکرینشات)")],
+ "LogoMotion3D": [("logoUrl", "لوگو (تصویر)")],
}
# Per-template text colour (default white for dark backgrounds; dark for light studios).
TEXTCOLORS = {
"AppShowcase3D": "#0f172a",
"CharacterStory": "#2b3a55",
+ "LogoMotion3D": "#f8fafc",
}
# Templates that ship a distinct preview video PER aspect (so the detail page shows
# the matching render, not the 16:9 cropped). Others reuse the single 16:9 preview.
-PERASPECT_VIDEO = {"AppShowcase3D", "CharacterStory"}
+PERASPECT_VIDEO = {"AppShowcase3D", "CharacterStory", "LogoMotion3D"}
# Templates whose content is split across MANY scenes (key c1..cN), one editable
# scene card per beat. value = scene count; texts are assigned 2-per-scene in order.
diff --git a/services/remotion/src/Root.tsx b/services/remotion/src/Root.tsx
index d1203c9..66eabac 100644
--- a/services/remotion/src/Root.tsx
+++ b/services/remotion/src/Root.tsx
@@ -5,6 +5,7 @@ import { Three3DTest } from "./compositions/Three3DTest";
import { AssetSheet } from "./compositions/AssetSheet";
import { StoryScenes, STORY_SCENES_DURATION } from "./compositions/StoryScenes";
import { FlexStory, flexStorySchema, flexStoryDefaults, calcFlexStoryMetadata } from "./compositions/FlexStory";
+import { LogoMotion3D, logoMotion3DSchema, logoMotion3DDefaults } from "./compositions/LogoMotion3D";
import { CHARACTER_JOURNEY } from "./scenes/presets";
import {
IlluminatedCircles,
@@ -107,6 +108,21 @@ export const RemotionRoot: React.FC = () => {
}}
/>
+ {/* Tech/3D logo motion — quality-preview composition */}
+ {ASPECTS.map((a) => (
+
+ ))}
+
{/* 3D feasibility test */}
(renders any SVG/PNG/data-URI). Static camera keeps the
+// flat logo aligned to the card.
+export const logoMotion3DSchema = z.object({
+ logoUrl: z.string(), // uploaded logo (SVG/PNG/data-URI); "" → branded placeholder mark
+ brandText: z.string(),
+ tagline: z.string(),
+ ...colorSchema,
+});
+type Props = z.infer;
+
+export const logoMotion3DDefaults: Props = {
+ logoUrl: "",
+ brandText: "فلترندر",
+ tagline: "موشن، ساده و حرفهای",
+ accentColor: "#38bdf8",
+ secondaryColor: "#818cf8",
+ backgroundColor: "#0a0e1a",
+ textColor: "#f8fafc",
+};
+
+const resolveSrc = (u: string) => (/^https?:\/\//.test(u) || u.startsWith("data:") ? u : staticFile(u));
+
+const Rays: React.FC<{ frame: number; color: string; op: number }> = ({ frame, color, op }) => {
+ const n = 16;
+ const pulse = 1 + Math.sin(frame / 11) * 0.08;
+ return (
+
+ {Array.from({ length: n }).map((_, i) => (
+
+
+
+
+ ))}
+
+ );
+};
+
+const Flare: React.FC<{ core: number; streak: number; accent: string }> = ({ core, streak, accent }) => (
+
+
+
+
+
+
+);
+
+const Card: React.FC<{ scale: number; accent: string }> = ({ scale, accent }) => (
+
+
+
+
+
+
+);
+
+export const LogoMotion3D: React.FC = (props) => {
+ const frame = useCurrentFrame();
+ const { fps, width, height } = useVideoConfig();
+ const L = useLayout();
+ const accent = props.accentColor;
+
+ const popIn = spring({ frame: frame - 6, fps, config: { damping: 11, stiffness: 130 } });
+ const scale = interpolate(popIn, [0, 1], [0.2, 1]);
+ const raysOp = interpolate(frame, [4, 24], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
+ const core = interpolate(frame, [8, 20, 40], [0, 1, 0.32], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
+ const streak = interpolate(frame, [10, 22, 44], [0, 0.9, 0.18], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
+ const brandOp = interpolate(frame, [30, 48], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
+ const brandY = interpolate(popIn, [0, 1], [L.vmin(28), 0]);
+ const tagOp = interpolate(frame, [46, 64], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
+
+ // The 3D card (1.7 units) projects to ≈43% of the frame height — match the HTML
+ // logo to it so it sits exactly on the card.
+ const cardPx = height * 0.43;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: 14 }).map((_, i) => {
+ const x = (rand(i) - 0.5) * 9;
+ const y = (rand(i + 3) - 0.5) * 5 + Math.sin((frame + i * 20) / 36) * 0.2;
+ const z = -1 - rand(i + 6) * 1.6;
+ return ;
+ })}
+
+
+
+
+
+
+ {/* Logo on the card (centered, scales in with the card). Any format renders. */}
+
+
+ {props.logoUrl ? (
+
})
+ ) : (
+
+ )}
+
+
+
+ {/* Brand + tagline below the card */}
+
+
+ {props.brandText}
+
+
+ {props.tagline}
+
+
+
+
+
+ );
+};