Files
kolaytercih-post/src/sahneler-1.tsx
bilalgursen dc5c87b6aa feat: KolayTercih lansman videosu (Remotion, v3)
64 saniyelik, kesintisiz döngü lansman filmi; 9:16 ve 16:9.
- remocn.dev bileşenleri (sheen-slide-in, tracking-in, gradient-scale-cut-text,
  outline-fill-track-text, perspective-marquee, path-ride, zoom-blur, lens-zoom)
  KolayTercih piksel diline uyarlandı (src/kinetik.tsx)
- Sahne içi imleç + imleci takip eden kamera, piksel iz ve tık patlaması
- Sessizlik → BAM kurgusu (lensZoom), punch zoom ve hareket bulanıklığı
- Müzik ve efektler numpy ile sentezlendi (ses/uret.py), zaman.json tek kaynak
- Örnek taban sıralamaları gerçek YÖK Atlas 2026 kayıtları

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:22:38 +03:00

253 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { measureText } from "@remotion/layout-utils";
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from "remotion";
import { IzAc, PikselDevKesme, PikselMarquee, PikselSheen } from "./kinetik";
import { LogoMark } from "./logo";
import { Sahne, useDuzen } from "./ortak";
import { Eyebrow, KelimeKelime, Wordmark } from "./piksel";
import { Kamera, MaskeSatir, OdakDisiPikseller } from "./sinema";
import { FONT, R, ara, hash, trSayi, zipla } from "./tema";
import { VURUSLAR } from "./vurus";
export const vurguStil = {
fontFamily: FONT.vurgu,
fontStyle: "italic" as const,
fontWeight: 800,
letterSpacing: "-0.04em",
color: R.primary,
};
/* ------------------------------------------------------------------ */
/* Açılış — boş ekran → "45.320." (Track Text) → tebrik */
/* ------------------------------------------------------------------ */
export function Acilis() {
const { dikey } = useDuzen();
return (
<Sahne>
<Kamera anahtarlar={[{ f: 0, s: 1.08 }, { f: 110, s: 1 }]} vuruslar={VURUSLAR.acilis}>
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center", gap: 44, textAlign: "center" }}>
<MaskeSatir gecikme={8} sure={18}>
<Eyebrow olcek={dikey ? 2.8 : 2.4}>YKS sonuçları açıklandı</Eyebrow>
</MaskeSatir>
<IzAc gecikme={14} fontSize={dikey ? 250 : 240}>
45.320<span style={{ color: R.primary }}>.</span>
</IzAc>
<div style={{ fontFamily: FONT.baslik, fontWeight: 700, fontSize: dikey ? 88 : 84, lineHeight: 1.1, maxWidth: dikey ? 960 : 1600 }}>
<MaskeSatir gecikme={42}>Sıralaman geldi. Tebrikler!</MaskeSatir>
<MaskeSatir gecikme={56} style={{ color: R.slate500 }}>
Sırada küçük bir <span style={vurguStil}>bulmaca</span> var.
</MaskeSatir>
</div>
</AbsoluteFill>
</Kamera>
</Sahne>
);
}
/* ------------------------------------------------------------------ */
/* Seçenekler — Perspective Marquee: akıp giden bölümler, 21.000+ / 24 */
/* ------------------------------------------------------------------ */
const UST_SIRA = ["Bilgisayar Mühendisliği", "Hukuk", "Tıp", "Psikoloji", "Mimarlık", "İşletme", "Hemşirelik"];
const ALT_SIRA = ["Diş Hekimliği", "Endüstri Mühendisliği", "Öğretmenlik", "Uluslararası İlişkiler", "Yazılım Mühendisliği", "Eczacılık"];
export function Secenek() {
const frame = useCurrentFrame();
const { dikey, h } = useDuzen();
const sayi = ara(frame, [4, 40], [0, 21000], Easing.bezier(0.16, 1, 0.3, 1));
const arti = zipla(frame, 38);
return (
<Sahne>
<Kamera anahtarlar={[{ f: 0, s: 1.02 }, { f: 125, s: 1.12 }]} vuruslar={VURUSLAR.secenek}>
<div style={{ position: "absolute", left: 0, right: 0, top: h * (dikey ? 0.2 : 0.14) - 60 }}>
<PikselMarquee ogeler={UST_SIRA} fontSize={dikey ? 100 : 96} hiz={6} renk={R.slate400} donY={-22} donX={10} />
</div>
<div style={{ position: "absolute", left: 0, right: 0, top: h * (dikey ? 0.8 : 0.86) - 60 }}>
<PikselMarquee ogeler={ALT_SIRA} fontSize={dikey ? 100 : 96} hiz={5} renk={R.slate400} donY={22} donX={-10} ters />
</div>
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center", textAlign: "center" }}>
<div style={{ fontFamily: FONT.baslik, fontWeight: 700, fontSize: dikey ? 230 : 240, lineHeight: 1, color: R.slate900, letterSpacing: "-0.01em" }}>
{trSayi(sayi)}
<span style={{ display: "inline-block", color: R.primary, transform: `scale(${Math.max(0, arti)})` }}>+</span>
</div>
<div style={{ marginTop: 16, fontSize: dikey ? 46 : 42, fontWeight: 600, color: R.slate500 }}>program var.</div>
<div style={{ marginTop: dikey ? 70 : 50 }}>
<PikselSheen gecikme={50} fontSize={dikey ? 86 : 88}>
Senin <span style={{ color: R.primary }}>24</span> hakkın var.
</PikselSheen>
</div>
</AbsoluteFill>
</Kamera>
</Sahne>
);
}
/* ------------------------------------------------------------------ */
/* Kaos — sekmeler gibi açılan sorular, sonra ani sessizlik */
/* ------------------------------------------------------------------ */
const SORULAR = [
"Geçen yılın tabanı yeter mi?",
"Kontenjan düştü mü?",
"Hangi şehir?",
"Devlet mi, vakıf mı?",
"Danışman 3.000 TL mi?!",
"Teyzem “hukuk yaz” dedi",
"Ya boşta kalırsam?",
"Hangisi kaçıncı sıraya?",
];
const YER_YATAY = [
[-0.62, -0.62], [0.55, -0.7], [-0.78, -0.08], [0.74, -0.18],
[-0.6, 0.55], [0.58, 0.52], [-0.12, 0.8], [0.1, -0.86],
];
const YER_DIKEY = [
[-0.38, -0.74], [0.36, -0.6], [-0.42, -0.42], [0.4, -0.3],
[-0.36, 0.34], [0.38, 0.48], [-0.3, 0.66], [0.32, 0.8],
];
export function Kaos() {
const frame = useCurrentFrame();
const { dikey, w, h } = useDuzen();
const yerler = dikey ? YER_DIKEY : YER_YATAY;
// Sonda kartlar hızlanır, ekran doluyor hissi; sonra sessizliğe sert kesme
const telas = ara(frame, [60, 104], [0, 1], Easing.in(Easing.quad));
return (
<Sahne>
<Kamera anahtarlar={[{ f: 0, s: 0.96, r: -1.2 }, { f: 104, s: 1.14, r: 0.8 }]} vuruslar={VURUSLAR.kaos}>
{SORULAR.map((s, i) => {
const [nx, ny] = yerler[i];
const g = zipla(frame, 6 + i * 5);
const hizli = 1 + telas * 3;
const yuzX = Math.sin(((frame + i * 20) / 18) * hizli) * (16 + telas * 20);
const yuzY = Math.cos(((frame + i * 13) / 15) * hizli) * (14 + telas * 18);
// Sırayla "sekme değiştirme" vurgusu
const aktif = Math.floor(frame / 9) % SORULAR.length === i && frame > 20;
return (
<div
key={s}
style={{
position: "absolute",
left: w / 2 + nx * (w / 2) + yuzX,
top: h / 2 + ny * (h / 2) + yuzY,
transform: `translate(-50%, -50%) scale(${Math.max(0, g) * (aktif ? 1.07 : 1)}) rotate(${(hash(i, 1, 2) - 0.5) * 10}deg)`,
background: aktif ? "#fff7ed" : R.beyaz,
border: `3px solid ${aktif ? R.turuncu : R.slate200}`,
borderRadius: 30,
padding: dikey ? "24px 32px" : "22px 30px",
fontSize: dikey ? 38 : 34,
fontWeight: 600,
color: aktif ? R.turuncu : R.slate700,
whiteSpace: "nowrap",
boxShadow: "0 12px 30px rgba(15,23,42,0.08)",
}}
>
{s}
</div>
);
})}
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
<h2
style={{
margin: 0,
textAlign: "center",
fontFamily: FONT.baslik,
fontWeight: 700,
fontSize: 104,
lineHeight: 1.08,
maxWidth: dikey ? 800 : 1200,
background: `radial-gradient(closest-side, ${R.slate50} 60%, rgba(248,250,252,0))`,
padding: "40px 60px",
}}
>
<KelimeKelime metin="Kafanda yüz sekme açık, değil mi?" gecikme={4} aralik={3} vurgu={["yüz", "sekme"]} />
</h2>
</AbsoluteFill>
</Kamera>
</Sahne>
);
}
/* ------------------------------------------------------------------ */
/* Nefes — sessizlik. Tek bir turuncu piksel nefes alır. */
/* ------------------------------------------------------------------ */
export function Nefes() {
const frame = useCurrentFrame();
const { dikey } = useDuzen();
const nefes = 1 + 0.12 * Math.sin((frame / 30) * Math.PI * 1.1);
const kare = dikey ? 64 : 56;
return (
<Sahne zemin={R.beyaz}>
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center", gap: 56 }}>
<div
style={{
width: kare,
height: kare,
borderRadius: kare * 0.23,
background: R.logoTuruncu,
opacity: interpolate(frame, [2, 12], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }),
transform: `scale(${nefes})`,
}}
/>
<div style={{ fontFamily: FONT.baslik, fontWeight: 700, fontSize: dikey ? 64 : 60, color: R.slate400 }}>
<MaskeSatir gecikme={10} sure={24}>
Derin bir nefes al.
</MaskeSatir>
</div>
</AbsoluteFill>
</Sahne>
);
}
/* ------------------------------------------------------------------ */
/* Logo — BAM: dev piksel kelime süpürür, logoya kesilir, slogan sheen */
/* ------------------------------------------------------------------ */
export function LogoSahnesi() {
const frame = useCurrentFrame();
const { dikey } = useDuzen();
const markBoy = dikey ? 250 : 230;
const yaziBoy = dikey ? 130 : 150;
// Kilit: işaret + yazı (yatayda yan yana, dikeyde alt alta)
const yaziGen =
measureText({ text: "Kolay", fontFamily: "Bricolage", fontSize: yaziBoy, fontWeight: "300", letterSpacing: `${-0.05 * yaziBoy}px` }).width +
measureText({ text: "Tercih", fontFamily: "Bricolage", fontSize: yaziBoy, fontWeight: "600", letterSpacing: `${-0.05 * yaziBoy}px` }).width;
const kilit = (
<div style={{ display: "flex", flexDirection: dikey ? "column" : "row", alignItems: "center", gap: dikey ? 10 : 24, transform: `translateY(${dikey ? -170 : -120}px)` }}>
<LogoMark boy={markBoy} girisYok rotaBas={32} />
<div style={{ width: dikey ? undefined : yaziGen }}>
<Wordmark boy={yaziBoy} />
</div>
</div>
);
return (
<Sahne zemin={R.beyaz}>
<OdakDisiPikseller seed={3} adet={8} guc={ara(frame, [30, 60], [0, 1])} />
<Kamera anahtarlar={[{ f: 0, s: 1.1 }, { f: 130, s: 1 }]} vuruslar={VURUSLAR.logo}>
<PikselDevKesme metin="KolayTercih" devBoy={dikey ? 420 : 520} gecikme={8} kesme={18}>
{kilit}
</PikselDevKesme>
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center", textAlign: "center" }}>
<div style={{ marginTop: dikey ? 560 : 400, display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }}>
{dikey ? (
<>
<PikselSheen gecikme={46} fontSize={76}>
Tercih listeni
</PikselSheen>
<PikselSheen gecikme={52} fontSize={76}>
gerçek veriyle kur,
</PikselSheen>
</>
) : (
<PikselSheen gecikme={46} fontSize={80}>
Tercih listeni gerçek veriyle kur,
</PikselSheen>
)}
<div style={{ fontSize: dikey ? 76 : 80, lineHeight: 1.1 }}>
<MaskeSatir gecikme={62}>
<span style={vurguStil}>panikle</span>
<span style={{ fontFamily: FONT.baslik, fontWeight: 700, color: R.slate900 }}> değil.</span>
</MaskeSatir>
</div>
</div>
</AbsoluteFill>
</Kamera>
</Sahne>
);
}