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>
This commit is contained in:
137
video/src/gecisler/piksel-dalga.tsx
Normal file
137
video/src/gecisler/piksel-dalga.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user