Files
kolaytercih/video/src/gecisler/piksel-dalga.tsx
bilalgursen d2359d3f3a Lansman videosu (Remotion) projesi eklendi
video/ ayrı bir Remotion projesi; Docker imajı, ESLint ve tsc kapsamı dışında.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 14:00:33 +03:00

138 lines
4.4 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 type {
TransitionPresentation,
TransitionPresentationComponentProps,
} from "@remotion/transitions";
import { AbsoluteFill, Easing, interpolate } from "remotion";
import { H, LOGO_KARELER, PIKSEL, W } from "../marka";
import { clamp01, hash } from "../yardimci";
/**
* Piksel dalgası: yeni sahne, bir noktadan (çoğunlukla imlecin tıkladığı
* yer) yayılan iri piksellerle açılır. Dalganın önünde logonun üç rengi
* (mavi, şeftali, turuncu) yanıp söner — hero haritasındaki ısınan
* piksellerin sahne ölçeğindeki hâli. Hücreler sitenin nokta oranını
* (%74, rx %20) taşır; dalga geçince birleşip kesintisiz yüzeye döner.
*/
export type PikselDalgaProps = {
origin?: { x: number; y: number };
/** Hücre kenarı (px) */
hucre?: number;
/** Dalga önünün kalınlığı (px) */
bant?: number;
/** Dalga önündeki piksellerin renkleri */
renkler?: string[];
/** Çıkan sahne hafifçe geri çekilsin (derinlik) */
geriCekil?: number;
tohum?: number;
};
const VARSAYILAN_RENKLER = LOGO_KARELER.map((k) => k.renk);
const koseUzakligi = (o: { x: number; y: number }) =>
Math.max(
Math.hypot(o.x, o.y),
Math.hypot(W - o.x, o.y),
Math.hypot(o.x, H - o.y),
Math.hypot(W - o.x, H - o.y),
);
/**
* Dalga önünün yarıçapı. Gelen sahne kendi öğelerini dalgayla eş zamanlı
* belirtmek isterse (ör. haritanın noktaları) aynı fonksiyonu kullanır.
*/
export const dalgaYaricapi = (
p: number,
origin: { x: number; y: number } = { x: W / 2, y: H / 2 },
bant = 420,
) =>
interpolate(clamp01(p), [0, 1], [0, koseUzakligi(origin) + bant * 1.25], {
easing: Easing.bezier(0.45, 0.05, 0.4, 1),
});
const PikselDalgaSunum: React.FC<
TransitionPresentationComponentProps<PikselDalgaProps>
> = ({ children, presentationProgress, presentationDirection, passedProps }) => {
const {
origin = { x: W / 2, y: H / 2 },
hucre = 64,
bant = 420,
renkler = VARSAYILAN_RENKLER,
geriCekil = 0.05,
tohum = 3,
} = passedProps;
const p = presentationProgress;
if (presentationDirection === "exiting") {
return (
<AbsoluteFill
style={{
transformOrigin: `${origin.x}px ${origin.y}px`,
scale: `${1 - geriCekil * Easing.in(Easing.cubic)(p)}`,
}}
>
{children}
</AbsoluteFill>
);
}
const sutun = Math.ceil(W / hucre);
const satir = Math.ceil(H / hucre);
const r = dalgaYaricapi(p, origin, bant);
let maske = "";
let renkli = "";
for (let j = 0; j < satir; j++) {
for (let i = 0; i < sutun; i++) {
const cx = i * hucre + hucre / 2;
const cy = j * hucre + hucre / 2;
const d = Math.hypot(cx - origin.x, cy - origin.y) + (hash(i, j, tohum) - 0.5) * bant * 0.55;
const t = clamp01((r - d) / bant);
if (t <= 0) continue;
// Önde renkli piksel belirip söner…
if (t < 0.72) {
const c = Math.sin(Math.PI * (t / 0.72));
const boy = hucre * PIKSEL.nokta * c;
const renk = renkler[Math.floor(hash(i, j, tohum + 9) * renkler.length)];
renkli += `<rect x="${cx - boy / 2}" y="${cy - boy / 2}" width="${boy}" height="${boy}" rx="${boy * PIKSEL.rx}" fill="${renk}"/>`;
}
// …arkasından yeni sahne piksel piksel açılır
const m = Easing.bezier(0.22, 1, 0.36, 1)(clamp01((t - 0.38) / 0.62));
if (m > 0) {
const boy = hucre * (PIKSEL.nokta + 0.3 * m);
const rx = hucre * PIKSEL.rx * (1 - m);
maske += `<rect x="${cx - boy / 2}" y="${cy - boy / 2}" width="${boy}" height="${boy}" rx="${rx}" fill="white"/>`;
}
}
}
const maskeUrl = `url("data:image/svg+xml;utf8,${encodeURIComponent(
`<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">${maske}</svg>`,
)}")`;
return (
<AbsoluteFill>
<AbsoluteFill
style={{
maskImage: maskeUrl,
WebkitMaskImage: maskeUrl,
maskSize: "100% 100%",
WebkitMaskSize: "100% 100%",
maskRepeat: "no-repeat",
WebkitMaskRepeat: "no-repeat",
}}
>
{children}
</AbsoluteFill>
<AbsoluteFill style={{ pointerEvents: "none" }}>
<svg width={W} height={H} dangerouslySetInnerHTML={{ __html: renkli }} />
</AbsoluteFill>
</AbsoluteFill>
);
};
export function pikselDalga(
props: PikselDalgaProps = {},
): TransitionPresentation<PikselDalgaProps> {
return { component: PikselDalgaSunum, props };
}