7ed2ccc414
The reference-round workflow, run end to end for a real template: Taste system (how we learn the user's taste, persisted): - references/TASTE_PROFILE.md (living design contract) + references/README.md (the daily loop) + a "reference round" stage in docs/TEMPLATE_BRIEF.md (provide refs or I suggest+mock directions). Design-quality before/after: - HeroDemo — the fix recipe vs the faint default: layered-depth background, a proper big video type scale, and a bold composed focal object. (Backgrounds were naked, text too small, scenes had no objects.) - YaldaSofreh3D + IGPromoDirections + IGProfileMock — reference-match proofs (low-poly 3D, 3 IG-promo style directions, the realistic IG-light page). Instagram channel-promo template (the deliverable — a flexible 5-scene FlexStory): - igkit + 5 blocks: IGIntro, IGProfile (realistic IG-light profile, scales to all aspects), IGFeed (post grid), IGStats (animated count-up), IGFollowCTA (Follow taps to "Following"). - FlexStory gains a `finish` toggle so the IG-light scenes render clean (no brand grade). INSTAGRAM_PROMO preset + 3 aspect comps in Root. Verified: a still of every scene at 9:16 renders clean; full preview MP4 rendering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
2.8 KiB
TypeScript
48 lines
2.8 KiB
TypeScript
import React from "react";
|
|
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
|
import { FONT } from "../../lib/fonts";
|
|
import { hexToRgba } from "../../lib/anim";
|
|
import { IgGlows, IgWordmark } from "./igkit";
|
|
import type { BlockProps, SceneBlock } from "../types";
|
|
|
|
const IGIntro: React.FC<BlockProps> = ({ data, colors, L, durationInFrames }) => {
|
|
const frame = useCurrentFrame();
|
|
const { fps } = useVideoConfig();
|
|
const out = interpolate(frame, [durationInFrames - 10, durationInFrames], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
|
|
const logo = spring({ frame, fps, config: { damping: 13, stiffness: 110 } });
|
|
const headSp = spring({ frame: frame - 8, fps, config: { damping: 16, stiffness: 110 } });
|
|
const subOp = interpolate(frame, [20, 36], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
|
|
|
|
return (
|
|
<AbsoluteFill style={{ fontFamily: FONT, background: `linear-gradient(165deg, ${colors.backgroundColor}, ${hexToRgba(colors.accentColor, 0.06)})`, opacity: Math.min(1, out) }}>
|
|
<IgGlows />
|
|
<AbsoluteFill style={{ direction: "rtl", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", textAlign: "center", padding: L.vmin(60) }}>
|
|
<div style={{ transform: `scale(${interpolate(logo, [0, 1], [0.6, 1])})`, opacity: logo }}><IgWordmark L={L} /></div>
|
|
<div style={{ marginTop: L.vmin(34), display: "inline-flex", alignItems: "center", gap: L.vmin(10), background: colors.accentColor, color: "#fff", fontWeight: 800, fontSize: L.vmin(28), padding: `${L.vmin(10)}px ${L.vmin(26)}px`, borderRadius: 999, opacity: logo }}>
|
|
{data.badge}
|
|
</div>
|
|
<div style={{ marginTop: L.vmin(28), fontWeight: 900, fontSize: L.pick(L.vmin(120), L.vmin(112), L.vmin(104)), lineHeight: 1.05, letterSpacing: -2, color: colors.textColor, transform: `translateY(${interpolate(headSp, [0, 1], [L.vmin(50), 0])}px)`, maxWidth: L.vmin(1100) }}>
|
|
{data.headline}
|
|
</div>
|
|
<div style={{ marginTop: L.vmin(24), fontWeight: 500, fontSize: L.pick(L.vmin(46), L.vmin(44), L.vmin(42)), color: hexToRgba(colors.textColor, 0.62), opacity: subOp, maxWidth: L.vmin(950) }}>
|
|
{data.subtitle}
|
|
</div>
|
|
</AbsoluteFill>
|
|
</AbsoluteFill>
|
|
);
|
|
};
|
|
|
|
export const IGIntroBlock: SceneBlock = {
|
|
id: "IGIntro",
|
|
label: "شروع (لوگوی اینستاگرام)",
|
|
component: IGIntro,
|
|
fields: [
|
|
{ key: "badge", label: "نشان", type: "text", default: "اینستاگرام" },
|
|
{ key: "headline", label: "تیتر", type: "text", default: "صفحهٔ ما را دنبال کنید" },
|
|
{ key: "subtitle", label: "زیرعنوان", type: "text", default: "هر روز یک طرح تازه", multiline: true },
|
|
],
|
|
defaultDurationSec: 3,
|
|
minDurationSec: 2,
|
|
maxDurationSec: 5,
|
|
};
|