- Sıra: önce sayfa geçişi biter, kısa nefes (80 ms), sonra logo güneşe/aya dönüşür; üst üste binmez. Geçiş iptal olsa da logo doğru biçime dönüşür. - View Transitions olmayan tarayıcıda da logo dönüşür. - DESIGN.md §4.22: logo biçim dönüşümü tasarım diline eklendi (biçim tablosu, koreografi, kurallar); §6 hareket ve §7 Yap/Yapma satırları. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
456 lines
18 KiB
TypeScript
456 lines
18 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useRef } from "react";
|
||
import { TEMA_GECIS_OLAYI } from "@/lib/tema-gecisi";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
// public/kolay-tercih-mark.svg'nin inline, animasyonlu karşılığı.
|
||
// Üç varyant:
|
||
//
|
||
// - "rota" (varsayılan): her kare sıradakinin pozisyonuna süzülür (mavi →
|
||
// şeftalinin yeri, şeftali → turuncunun, turuncu → mavinin), üç adımda
|
||
// herkes başlangıç formuna döner. Uçuş sırasında kare hafifçe küçülüp
|
||
// solar, varışta toparlanır. Site header'daki logo hover'da bunu oynatır.
|
||
// - "zincir": aynı rota, ama kareler aynı anda değil sırayla yürür — bir kare
|
||
// hedefindeki karenin üzerine konarken alttaki yola çıkar, üçüncü kare boş
|
||
// kalan yere oturur ve zincir baştan başlar. Danışman avatarı yalnız
|
||
// danışman veriye bakarken (araç çalışırken) bunu oynatır.
|
||
// - "nabiz": kareler yerlerinden ayrılmaz; 120° faz farkıyla dalga gibi
|
||
// küçülüp büyür (danışman panel başlığının sakin, sürekli hâli; danışman
|
||
// avatarı da cevap beklenirken böyle atar).
|
||
//
|
||
// Performans notu: kareler SVG <rect> değil HTML <span>, animasyon da Web
|
||
// Animations API ile yalnızca `translate` / `scale` / `opacity` üzerinden
|
||
// yürür. Bu üçü compositor'da (GPU) oynar: ana iş parçacığı meşgulken
|
||
// (hidrasyon, sayfa geçişi, kaydırma) bile kare düşmez ve hover React'i
|
||
// yeniden render etmez. SVG iç elemanlarının transform'u her karede yeniden
|
||
// boyanır; o yüzden buraya geri dönme.
|
||
|
||
const KUTU = 180; // kaynak SVG'nin viewBox kenarı
|
||
|
||
const POZISYONLAR = [
|
||
[27, 56],
|
||
[91, 29],
|
||
[91, 95],
|
||
] as const;
|
||
|
||
const KARELER = [
|
||
{ boyut: 57, renk: "#3b82f6", baslangic: 0 },
|
||
{ boyut: 56, renk: "#fdc7a7", baslangic: 1 },
|
||
{ boyut: 56, renk: "#ff5a00", baslangic: 2 },
|
||
] as const;
|
||
|
||
// Rota: tam tur 3.6s — 3 etap × (%25 yol = 0.9s + %8.3 varışta soluklanma).
|
||
// 0.9s'lik yolculuk, nabız varyantının küçül-büyü temposuyla aynı hızda.
|
||
const ROTA_SURE = 3600;
|
||
// Kalkışlar kademeli: kareler eş zamanlı değil, danışman başlığındaki gibi
|
||
// sırayla (mavi → şeftali → turuncu) yola çıkıp küçülür. Gecikme yalnızca ilk
|
||
// turdan önce uygulanır, sonraki turlarda faz farkı kendiliğinden korunur.
|
||
const ROTA_GECIKME_ADIMI = 200;
|
||
const KONUM_ZAMANLARI = [0, 0.25, 1 / 3, 0.583, 2 / 3, 0.917, 1];
|
||
// Nabız (rota içi): her yolculuğun ortasında dibe vurur, varışta tam form.
|
||
const ROTA_NABIZ_ZAMANLARI = [
|
||
0, 0.125, 0.25, 1 / 3, 0.458, 0.583, 2 / 3, 0.792, 0.917, 1,
|
||
];
|
||
const ROTA_NABIZ_SCALE = [1, 0.84, 1, 1, 0.84, 1, 1, 0.84, 1, 1];
|
||
const ROTA_NABIZ_OPACITY = [1, 0.6, 1, 1, 0.6, 1, 1, 0.6, 1, 1];
|
||
|
||
// Zincir: iki kalkış arası 450 ms, tek yolculuk 600 ms. Sıradaki kare, gelen
|
||
// kare yolun ~%94'ündeyken (üzerine konmuşken) kalkar; son 150 ms'de biri
|
||
// inerken diğeri hızlanır, akış hiç durmaz. Her kare turda 3 yolculuk yapar.
|
||
const ZINCIR_ADIM = 450;
|
||
const ZINCIR_YOL = 600;
|
||
const ZINCIR_SURE = ZINCIR_ADIM * 9;
|
||
const ZINCIR_YOL_PAYI = ZINCIR_YOL / ZINCIR_SURE;
|
||
const ZINCIR_KONUM_ZAMANLARI = [
|
||
...[0, 1, 2].flatMap((etap) => [etap / 3, etap / 3 + ZINCIR_YOL_PAYI]),
|
||
1,
|
||
];
|
||
const ZINCIR_NABIZ_ZAMANLARI = [
|
||
...[0, 1, 2].flatMap((etap) => [
|
||
etap / 3,
|
||
etap / 3 + ZINCIR_YOL_PAYI / 2,
|
||
etap / 3 + ZINCIR_YOL_PAYI,
|
||
]),
|
||
1,
|
||
];
|
||
|
||
// Yerinde nabız: kesintisiz tam-sinüs (dinlenme yok), delay'ler döngüyü üçe
|
||
// böler — biri büyürken sıradaki küçülür, hiçbir kare beklemez.
|
||
const NABIZ_SURE = 1800;
|
||
const NABIZ_GECIKMELER = [0, 600, 1200] as const;
|
||
|
||
const SINE = "cubic-bezier(0.37, 0, 0.63, 1)";
|
||
// Yolculuk: yumuşak kalkış, uzun ve yumuşak iniş (easeInOutCubic).
|
||
const YOL = "cubic-bezier(0.65, 0, 0.35, 1)";
|
||
// Hover bitince yerine oturma (easeOutQuint): hızlı toparlanır, sarsmadan durur.
|
||
const DONUS = "cubic-bezier(0.22, 1, 0.36, 1)";
|
||
const DONUS_SURE = 500;
|
||
// `oynat` kapanınca yerine oturma: hover'daki kadar aceleci değil — duran kare
|
||
// yumuşak kalkar, uzun ve yumuşak iner.
|
||
const YERLES = "cubic-bezier(0.4, 0, 0.2, 1)";
|
||
const YERLES_SURE = 600;
|
||
|
||
/** Karenin `translate` değeri: yüzdeler karenin kendi boyutuna göredir, bu
|
||
* yüzden işaret hangi boyutta çizilirse çizilsin aynı oran korunur. */
|
||
function yer(pozisyon: number, boyut: number): string {
|
||
const [x, y] = POZISYONLAR[pozisyon % 3];
|
||
return `${(x / boyut) * 100}% ${(y / boyut) * 100}%`;
|
||
}
|
||
|
||
/** Karenin merkezini 180'lik kutuda (cx, cy) noktasına taşıyan `translate`. */
|
||
function merkezde(cx: number, cy: number, boyut: number): string {
|
||
return `${((cx - boyut / 2) / boyut) * 100}% ${((cy - boyut / 2) / boyut) * 100}%`;
|
||
}
|
||
|
||
// Tema geçişi biçimi (DESIGN.md §2.3): sayfa geçişi bitince kareler
|
||
// neye geçildiğini gösterir, kısa bir an durur ve yeniden logoya toplanır.
|
||
// - Koyuya: şeftali kare hilale, mavi kare hilalin kucağındaki yıldıza;
|
||
// turuncu söner.
|
||
// - Açığa: turuncu kare güneşe, şeftali kare arkasındaki haleye; mavi söner.
|
||
// Toplam süre ve duraklar: %30'da biçim tamam, %70'e kadar durur, sonra döner.
|
||
const SEKIL_SURE = 1700;
|
||
const SEKIL_DURAKLAR = [0, 0.3, 0.7, 1];
|
||
type SekilHedefi = { translate: string; scale: string; opacity: number };
|
||
|
||
// Ay kompozisyonu kutunun merkezine (90) göre küçültülür ve logonun alanına
|
||
// (x 27–151) oturacak kadar kaydırılır.
|
||
const AY_OLCEK = 0.82;
|
||
const AY_KAYMA_X = 8;
|
||
function ay(nokta: number, kayma = AY_KAYMA_X): number {
|
||
return 90 + (nokta - 90) * AY_OLCEK + kayma;
|
||
}
|
||
|
||
/** Kare `i` için hedef biçim. Kareler sırayla mavi, şeftali, turuncu. */
|
||
function sekilHedefi(i: number, koyu: boolean): SekilHedefi {
|
||
const b = KARELER[i].boyut;
|
||
if (koyu) {
|
||
return [
|
||
// Biçim tema-ikonu.tsx'teki ay ikonuyla aynı: gövde r74 @ (76,92),
|
||
// tek yıldız r13 @ (108,79) — hilalin iki sivri ucunun tam ortası (iki
|
||
// dairenin kesişim kirişinin orta noktası). Turuncu kare yıldızın
|
||
// yerinde söner: güneşte mavi, ayda turuncu sahneden çekilir. `ay`
|
||
// kompozisyonu logonun kendi alanına sığdırır.
|
||
{ translate: merkezde(ay(108), ay(79, 0), b), scale: String((26 * AY_OLCEK) / b), opacity: 1 }, // yıldız
|
||
{ translate: merkezde(ay(76), ay(92, 0), b), scale: String((148 * AY_OLCEK) / b), opacity: 1 }, // hilal
|
||
{ translate: merkezde(ay(108), ay(79, 0), b), scale: "0.2", opacity: 0 }, // söner
|
||
][i];
|
||
}
|
||
return [
|
||
{ translate: merkezde(90, 90, b), scale: "0.3", opacity: 0 }, // söner
|
||
{ translate: merkezde(90, 90, b), scale: "2.15", opacity: 0.45 }, // hale
|
||
{ translate: merkezde(90, 90, b), scale: "1.1", opacity: 1 }, // güneş
|
||
][i];
|
||
}
|
||
|
||
/** Kare için durak koordinatları: kendi yeri → sıradaki → ondan sonraki →
|
||
* kendi yeri. Duraklarda bekleme için her hedef iki kez tekrarlanır
|
||
* (KONUM_ZAMANLARI ile hizalı). */
|
||
function rota(baslangic: number, boyut: number): string[] {
|
||
const [p0, p1, p2] = [0, 1, 2].map((adim) => yer(baslangic + adim, boyut));
|
||
return [p0, p1, p1, p2, p2, p0, p0];
|
||
}
|
||
|
||
type Varyant = "rota" | "zincir" | "nabiz";
|
||
|
||
function donguyuBaslat(
|
||
el: HTMLElement,
|
||
i: number,
|
||
varyant: Varyant,
|
||
): Animation[] {
|
||
const k = KARELER[i];
|
||
if (varyant === "nabiz") {
|
||
return [
|
||
el.animate(
|
||
{ scale: ["1", "0.82", "1"], opacity: [1, 0.55, 1], easing: SINE },
|
||
{
|
||
duration: NABIZ_SURE,
|
||
delay: NABIZ_GECIKMELER[i],
|
||
iterations: Infinity,
|
||
},
|
||
),
|
||
];
|
||
}
|
||
const zincir = varyant === "zincir";
|
||
const secenek = {
|
||
duration: zincir ? ZINCIR_SURE : ROTA_SURE,
|
||
delay: i * (zincir ? ZINCIR_ADIM : ROTA_GECIKME_ADIMI),
|
||
iterations: Infinity,
|
||
};
|
||
// Konum ve nabız ayrı animasyonlar: konum etabı tek parça eğriyle akar,
|
||
// ortasındaki küçülme onu ikiye bölüp duraksatmaz.
|
||
return [
|
||
el.animate(
|
||
{
|
||
translate: rota(k.baslangic, k.boyut),
|
||
offset: zincir ? ZINCIR_KONUM_ZAMANLARI : KONUM_ZAMANLARI,
|
||
easing: YOL,
|
||
},
|
||
secenek,
|
||
),
|
||
el.animate(
|
||
{
|
||
scale: ROTA_NABIZ_SCALE.map(String),
|
||
// Zincirde kare solmaz: başka bir karenin üzerine konar, yarı saydam
|
||
// olursa alttaki renk içinden görünür.
|
||
...(zincir ? {} : { opacity: ROTA_NABIZ_OPACITY }),
|
||
offset: zincir ? ZINCIR_NABIZ_ZAMANLARI : ROTA_NABIZ_ZAMANLARI,
|
||
easing: SINE,
|
||
},
|
||
secenek,
|
||
),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Animasyonlu KolayTercih işareti. Varsayılanda koreografi sürekli döner;
|
||
* `sadeceHover` ile yalnızca işaretin üzerine gelindiğinde oynar (ör. site
|
||
* header'daki logo linki) ve hover bitince kareler yumuşakça yerine oturur.
|
||
* `oynat` ile döngü dışarıdan açılıp kapatılır, `varyant` da çalışırken
|
||
* değişebilir (ör. danışman avatarı: cevap beklenirken nabız, veriye bakarken
|
||
* zincir); her geçişte kareler önce yumuşakça yerine oturur, yeni döngü
|
||
* yerinden başlar.
|
||
*/
|
||
export function AnimasyonluMark({
|
||
className,
|
||
sadeceHover = false,
|
||
oynat = true,
|
||
varyant = "rota",
|
||
renk = "marka",
|
||
}: {
|
||
className?: string;
|
||
sadeceHover?: boolean;
|
||
/** `sadeceHover` yokken döngü açık mı. `false` iken işaret durağandır. */
|
||
oynat?: boolean;
|
||
varyant?: Varyant;
|
||
/** "marka": logodaki üç renk; "turuncu": tüm kareler sitenin CTA turuncusu
|
||
* (orange-500) — ör. danışman panel başlığı. */
|
||
renk?: "marka" | "turuncu";
|
||
}) {
|
||
const kokRef = useRef<HTMLSpanElement>(null);
|
||
const kontrolRef = useRef<{
|
||
ayarla: (hedef: Varyant | null, cevik?: boolean) => void;
|
||
temaSekli: (koyu: boolean) => void;
|
||
} | null>(null);
|
||
|
||
// Animasyonların sahibi: bileşen yaşadıkça bir kez kurulur. Prop değişimi
|
||
// bunu söküp kurmaz (kareler sıçrardı); aşağıdaki efekt `ayarla` ile hedefi
|
||
// bildirir.
|
||
useEffect(() => {
|
||
const kok = kokRef.current;
|
||
if (!kok || typeof kok.animate !== "function") return;
|
||
const azHareket = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||
const kareler = Array.from(kok.children) as HTMLElement[];
|
||
|
||
let dongu: Animation[] = [];
|
||
let donus: Animation[] = [];
|
||
/** Tema geçişi biçimi (güneş/ay) animasyonları; bkz. `temaSekli`. */
|
||
let sekil: Animation[] = [];
|
||
/** Şu an dönen döngü ve istenen döngü (`null`: durağan). */
|
||
let aktif: Varyant | null = null;
|
||
let hedef: Varyant | null = null;
|
||
// Döngü bir mikro görev sonra başlar; o arada efekt sökülmüşse (React
|
||
// geliştirme modunda her efekti kur-sök-kur yapar) başlamamalı — yoksa
|
||
// kimsenin durduramadığı sahipsiz bir döngü kalır.
|
||
let sokuldu = false;
|
||
|
||
/** Döngüyü keser; kareler o an neredeyse oradan yerlerine süzülür. */
|
||
const yerles = (cevik: boolean) => {
|
||
const anlik = kareler.map((el) => {
|
||
const cs = getComputedStyle(el);
|
||
return { translate: cs.translate, scale: cs.scale, opacity: cs.opacity };
|
||
});
|
||
// Nabızda kareler zaten yerindedir, yalnız boyut toparlanır: kısa sürer.
|
||
const sure = aktif === "nabiz" ? YERLES_SURE / 2 : YERLES_SURE;
|
||
dongu.forEach((a) => a.cancel());
|
||
dongu = [];
|
||
aktif = null;
|
||
donus = kareler.map((el, i) =>
|
||
el.animate(
|
||
[
|
||
anlik[i],
|
||
{
|
||
translate: yer(KARELER[i].baslangic, KARELER[i].boyut),
|
||
scale: "1",
|
||
opacity: 1,
|
||
},
|
||
],
|
||
cevik
|
||
? { duration: DONUS_SURE, easing: DONUS }
|
||
: { duration: sure, easing: YERLES },
|
||
),
|
||
);
|
||
};
|
||
|
||
/** `yeni` döngüsünü, süren dönüş ve tema biçimi bitince yerinden başlatır. */
|
||
const donguyuSiraya = (yeni: Varyant) => {
|
||
// Kareler hâlâ yerine dönüyorsa (ya da güneş/ay oynuyorsa) bitmesi
|
||
// beklenir; döngü hep yerinden başlar, sıçrama olmaz.
|
||
Promise.all([...donus, ...sekil].map((a) => a.finished))
|
||
.then(() => {
|
||
if (sokuldu || hedef !== yeni || aktif !== null) return;
|
||
dongu = kareler.flatMap((el, i) => donguyuBaslat(el, i, yeni));
|
||
aktif = yeni;
|
||
})
|
||
.catch(() => {});
|
||
};
|
||
|
||
const ayarla = (yeni: Varyant | null, cevik = false) => {
|
||
if (yeni === hedef) return;
|
||
hedef = yeni;
|
||
if (aktif !== null) yerles(cevik);
|
||
if (yeni === null || azHareket.matches) return;
|
||
donguyuSiraya(yeni);
|
||
};
|
||
|
||
/**
|
||
* Tema geçişi biçimi: kareler güneşe ya da aya dönüşür, durur, logoya
|
||
* döner. Hover döngüsünden önceliklidir: döngü oynuyorsa kesilir, kareler
|
||
* o an neredeyse oradan biçime geçer; biçim bitince imleç hâlâ
|
||
* üzerindeyse döngü yerinden yeniden başlar. Hareket azaltılmışsa çalışmaz.
|
||
* Hilal, kareyi kesen ikinci bir şekille değil karenin kendi iç gölgesiyle
|
||
* çizilir (zemin şeffaf, renk iç gölgede): arkadaki zemin ne olursa olsun
|
||
* doğru görünür.
|
||
*/
|
||
const temaSekli = (koyu: boolean) => {
|
||
if (azHareket.matches) return;
|
||
// Karelerin o anki hâli: döngü ya da önceki biçim yarıdaysa oradan çıkılır.
|
||
const anlik = kareler.map((el) => {
|
||
const cs = getComputedStyle(el);
|
||
return {
|
||
translate: cs.translate,
|
||
scale: cs.scale,
|
||
opacity: cs.opacity,
|
||
borderRadius: cs.borderRadius,
|
||
};
|
||
});
|
||
[...dongu, ...donus, ...sekil].forEach((a) => a.cancel());
|
||
dongu = [];
|
||
donus = [];
|
||
aktif = null;
|
||
sekil = kareler.flatMap((el, i) => {
|
||
const k = KARELER[i];
|
||
const ev = {
|
||
translate: yer(k.baslangic, k.boyut),
|
||
scale: "1",
|
||
opacity: 1,
|
||
borderRadius: `${(13 / k.boyut) * 100}%`,
|
||
};
|
||
const bicim = { ...sekilHedefi(i, koyu), borderRadius: "50%" };
|
||
const secenek = { duration: SEKIL_SURE };
|
||
const animasyonlar = [
|
||
el.animate(
|
||
[
|
||
{ ...anlik[i], easing: DONUS },
|
||
{ ...bicim, easing: "linear" },
|
||
{ ...bicim, easing: YERLES },
|
||
ev,
|
||
].map((kare, d) => ({ ...kare, offset: SEKIL_DURAKLAR[d] })),
|
||
secenek,
|
||
),
|
||
];
|
||
// Hilal: yalnız şeftali kare, yalnız koyuya geçerken.
|
||
if (koyu && i === 1) {
|
||
const renk = getComputedStyle(el).backgroundColor;
|
||
const w = el.offsetWidth;
|
||
const dolu = `inset 0 0 0 ${w}px ${renk}`;
|
||
// tema-ikonu.tsx'teki hilalle aynı kesim: gövde r74'ten, (+40,-16)
|
||
// kaydırılmış r66 daire çıkar — sırtı sol altta.
|
||
// İç gölgede: kaydırma = kesim dairesinin kayması, yayılma = iki
|
||
// yarıçapın farkı (hepsi gövde çapı 148'e oranla).
|
||
const hilal = `inset ${(40 / 148) * w}px ${(-16 / 148) * w}px 0 ${(8 / 148) * w}px ${renk}`;
|
||
animasyonlar.push(
|
||
el.animate(
|
||
[
|
||
{ boxShadow: dolu, backgroundColor: "transparent", easing: DONUS },
|
||
{ boxShadow: hilal, backgroundColor: "transparent", easing: "linear" },
|
||
{ boxShadow: hilal, backgroundColor: "transparent", easing: YERLES },
|
||
{ boxShadow: dolu, backgroundColor: "transparent" },
|
||
].map((kare, d) => ({ ...kare, offset: SEKIL_DURAKLAR[d] })),
|
||
secenek,
|
||
),
|
||
);
|
||
}
|
||
return animasyonlar;
|
||
});
|
||
// İmleç hâlâ üzerindeyse hover döngüsü biçimden sonra sürer.
|
||
if (hedef !== null) donguyuSiraya(hedef);
|
||
};
|
||
|
||
const kontrol = { ayarla, temaSekli };
|
||
kontrolRef.current = kontrol;
|
||
return () => {
|
||
sokuldu = true;
|
||
if (kontrolRef.current === kontrol) kontrolRef.current = null;
|
||
[...dongu, ...donus, ...sekil].forEach((a) => a.cancel());
|
||
};
|
||
}, []);
|
||
|
||
// Üstteki efektten sonra koşar (aynı bileşende efektler sırayla çalışır),
|
||
// o yüzden kontrolRef burada doludur.
|
||
useEffect(() => {
|
||
const kok = kokRef.current;
|
||
if (!kok) return;
|
||
if (!sadeceHover) {
|
||
kontrolRef.current?.ayarla(oynat ? varyant : null);
|
||
return;
|
||
}
|
||
kontrolRef.current?.ayarla(null, true);
|
||
// Hover hedefi işaretin kendisi değil, en yakın `group` ebeveyni (site
|
||
// header'da logo + "KolayTercih" yazısını saran Link): yazının üzerine
|
||
// gelindiğinde de animasyon başlar. `group`'suz kullanımda köke düşer.
|
||
const hoverHedefi = kok.closest(".group") ?? kok;
|
||
const gir = () => kontrolRef.current?.ayarla(varyant, true);
|
||
const cik = () => kontrolRef.current?.ayarla(null, true);
|
||
hoverHedefi.addEventListener("mouseenter", gir);
|
||
hoverHedefi.addEventListener("mouseleave", cik);
|
||
// Tema geçişi bitince (lib/tema-gecisi.ts) kareler yeni temanın
|
||
// biçimine dönüşür: koyuya geçişte ay ve yıldızlar, açığa geçişte güneş.
|
||
// Olay anında <html> yeni temanın sınıfını taşır.
|
||
const temaGecti = () =>
|
||
kontrolRef.current?.temaSekli(
|
||
document.documentElement.classList.contains("dark"),
|
||
);
|
||
window.addEventListener(TEMA_GECIS_OLAYI, temaGecti);
|
||
return () => {
|
||
hoverHedefi.removeEventListener("mouseenter", gir);
|
||
hoverHedefi.removeEventListener("mouseleave", cik);
|
||
window.removeEventListener(TEMA_GECIS_OLAYI, temaGecti);
|
||
};
|
||
}, [oynat, sadeceHover, varyant]);
|
||
|
||
return (
|
||
<span
|
||
ref={kokRef}
|
||
aria-hidden="true"
|
||
className={cn(
|
||
"relative block shrink-0",
|
||
varyant !== "rota" && "isolate",
|
||
className,
|
||
)}
|
||
>
|
||
{KARELER.map((k, i) => (
|
||
<span
|
||
key={k.renk}
|
||
className={cn(
|
||
"absolute top-0 left-0 block",
|
||
renk === "turuncu" && "bg-orange-500",
|
||
)}
|
||
style={{
|
||
width: `${(k.boyut / KUTU) * 100}%`,
|
||
height: `${(k.boyut / KUTU) * 100}%`,
|
||
borderRadius: `${(13 / k.boyut) * 100}%`,
|
||
backgroundColor: renk === "marka" ? k.renk : undefined,
|
||
translate: yer(k.baslangic, k.boyut),
|
||
// Zincirde yürüyen kare hep hedefindekinin üstüne konar: sırası
|
||
// önce gelen üstte durur (mavi > şeftali > turuncu). Avatar nabız
|
||
// ile zincir arasında gidip geldiği için sıra nabızda da aynıdır
|
||
// (orada kareler üst üste gelmez); header'daki rota'ya dokunulmaz.
|
||
zIndex: varyant !== "rota" ? KARELER.length - i : undefined,
|
||
}}
|
||
/>
|
||
))}
|
||
</span>
|
||
);
|
||
}
|