fix(tasarim): üniversite haritası dört kartın arkasına sarkar; yakınlaşma sıçramasız yay, mask rampaları uzun
All checks were successful
Deploy / deploy (push) Successful in 4m51s
All checks were successful
Deploy / deploy (push) Successful in 4m51s
- Özet ızgarası relative isolate; konum haritası absolute katman (-inset-x-4 -inset-y-8) olarak ızgaranın tamamını kaplar, üçüncü sütun yalnız yer tutar. lg'de sağdan sola, telefonda üst satırdan aşağı uzun rampalı mask. - Yakınlaşma ease-out yerine spring bounce 0 (0,6 s gecikme, 1,6 s): sert başlangıç gitti. Şehir yüzeyi mask'ları da uzatıldı. - DESIGN.md §4.17 güncellendi. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -474,14 +474,20 @@ bulunulan şehir `border-primary bg-primary text-white` + `aria-current="page"`.
|
|||||||
çok kötü"; "harita bulunduğu yerin tamamını kaplasın"). Şehir sayfasında `SehirYuzeyi`
|
çok kötü"; "harita bulunduğu yerin tamamını kaplasın"). Şehir sayfasında `SehirYuzeyi`
|
||||||
kartını `doldur` (slice) ile kaplar, metin üstünde; kenarlar `mask-image` ile erir —
|
kartını `doldur` (slice) ile kaplar, metin üstünde; kenarlar `mask-image` ile erir —
|
||||||
telefonda yukarıdan (metnin altında belirir), lg'de soldan (sağ yarıda belirir). Metin
|
telefonda yukarıdan (metnin altında belirir), lg'de soldan (sağ yarıda belirir). Metin
|
||||||
sütunu lg'de %58, kart en az 26rem; telefonda altta 12–14rem harita payı. Üniversite konum
|
sütunu lg'de %58, kart en az 26rem; telefonda altta 12–14rem harita payı. Üniversite
|
||||||
kartında radyal mask. Sert kenarlı, kırpılmış bir SVG asla görünmez.
|
sayfasında harita **dört özet kartın arkasına sarkar** (Bilal, 24 Eyl 2026): ızgara `relative
|
||||||
|
isolate`, üçüncü sütun yalnız yer tutar, harita `absolute -inset-x-4 -inset-y-8 -z-10`
|
||||||
|
katmanda; lg'de sağdan sola, telefonda üst satırdan aşağı doğru uzun rampalı mask. Mask
|
||||||
|
rampaları uzun tutulur (şeffaf → %6 → %30 → siyah); kısa rampa "kanvas" gibi okunur. Sert
|
||||||
|
kenarlı, kırpılmış bir SVG asla görünmez.
|
||||||
- **Yakınlaşma** (Bilal, 24 Eyl 2026: "ilin bulunduğu yere doğru harita büyüsün, sayfa
|
- **Yakınlaşma** (Bilal, 24 Eyl 2026: "ilin bulunduğu yere doğru harita büyüsün, sayfa
|
||||||
yüklendikten sonra delay ile motion ile"): SSR tüm ülke; yüklenince 0,7 s sonra viewBox 1,1 s
|
yüklendikten sonra delay ile motion ile"): SSR tüm ülke; yüklenince 0,6 s sonra viewBox
|
||||||
boyunca güçlü ease-out (`0.23, 1, 0.32, 1`) ile ilin çevresindeki kutuya kayar — en çok 2,4
|
sıçramasız yayla (`spring`, bounce 0, 1,6 s; Apple'ın damping 1'i — ease-out "sert"
|
||||||
kat (ülke haritasında şehir işaretine 1,8 kat). Kutu kabın o anki oranında kurulur ve il
|
duruyordu) ilin çevresindeki kutuya kayar — en çok 2,4 kat (ülke haritasında şehir işaretine
|
||||||
|
1,8 kat). Kutu kabın o anki oranında kurulur ve il
|
||||||
`odakHiza` ile kabın istenen noktasına gelir (şehir sayfası: lg'de x 0,74 / y 0,5, telefonda
|
`odakHiza` ile kabın istenen noktasına gelir (şehir sayfası: lg'de x 0,74 / y 0,5, telefonda
|
||||||
x 0,5 / y 0,78; konum kartı: orta). Kareler büyür, il kadraja gelir; ısı fırçası viewBox'ı
|
x 0,5 / y 0,78; üniversite sayfası: lg'de x 0,82 / y 0,5 — üçüncü sütun, telefonda y 0,17 —
|
||||||
|
üst satır). Kareler büyür, il kadraja gelir; ısı fırçası viewBox'ı
|
||||||
canlı okur. Hareketi azalt açıkken kutuya doğrudan atlanır. Sayfa başına tek yakınlaşma.
|
canlı okur. Hareketi azalt açıkken kutuya doğrudan atlanır. Sayfa başına tek yakınlaşma.
|
||||||
|
|
||||||
## 5. Yerleşim
|
## 5. Yerleşim
|
||||||
|
|||||||
@@ -48,11 +48,13 @@ const VIEW_H = SATIR * CELL;
|
|||||||
// Hero'nun ısı fırçası 640 birimlik viewBox'ta 34; burada aynı oran.
|
// Hero'nun ısı fırçası 640 birimlik viewBox'ta 34; burada aynı oran.
|
||||||
const ISI_YARICAPI = 34 * (VIEW_W / 640);
|
const ISI_YARICAPI = 34 * (VIEW_W / 640);
|
||||||
// Yakınlaşma: gecikme (s), süre (s), en çok büyütme, ilin çevresinde pay (hücre)
|
// Yakınlaşma: gecikme (s), süre (s), en çok büyütme, ilin çevresinde pay (hücre)
|
||||||
const ODAK_GECIKME = 0.7;
|
// Sıçramasız yay (Apple: damping 1): hızlı başlayan ease-out "sert"
|
||||||
const ODAK_SURE = 1.1;
|
// duruyordu (Bilal, 24 Eyl 2026: "geçişler sert"); yay sıfır hızla başlar,
|
||||||
|
// yumuşak ivmelenir ve sessizce oturur.
|
||||||
|
const ODAK_GECIKME = 0.6;
|
||||||
|
const ODAK_SURE = 1.6;
|
||||||
const ODAK_ZOOM = 2.4;
|
const ODAK_ZOOM = 2.4;
|
||||||
const ODAK_PAY = 2;
|
const ODAK_PAY = 2;
|
||||||
const ODAK_EASE = [0.23, 1, 0.32, 1] as const;
|
|
||||||
|
|
||||||
type Kutu = readonly [number, number, number, number];
|
type Kutu = readonly [number, number, number, number];
|
||||||
const TAM_KUTU: Kutu = [0, 0, VIEW_W, VIEW_H];
|
const TAM_KUTU: Kutu = [0, 0, VIEW_W, VIEW_H];
|
||||||
@@ -201,9 +203,10 @@ function PikselSvg({
|
|||||||
}
|
}
|
||||||
const bas = TAM_KUTU;
|
const bas = TAM_KUTU;
|
||||||
const kontrol = animate(0, 1, {
|
const kontrol = animate(0, 1, {
|
||||||
|
type: "spring",
|
||||||
|
bounce: 0,
|
||||||
delay: ODAK_GECIKME,
|
delay: ODAK_GECIKME,
|
||||||
duration: ODAK_SURE,
|
duration: ODAK_SURE,
|
||||||
ease: [...ODAK_EASE],
|
|
||||||
onUpdate: (t) =>
|
onUpdate: (t) =>
|
||||||
yaz([
|
yaz([
|
||||||
bas[0] + (odak[0] - bas[0]) * t,
|
bas[0] + (odak[0] - bas[0]) * t,
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ export function SehirYuzeyi({
|
|||||||
{harita ? (
|
{harita ? (
|
||||||
<div
|
<div
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className="pointer-events-none absolute inset-0 -z-10 [mask-image:linear-gradient(to_bottom,transparent_0%,rgba(0,0,0,0.18)_40%,black_74%,black_90%,transparent_100%)] lg:[mask-image:linear-gradient(to_right,transparent_28%,rgba(0,0,0,0.22)_50%,black_72%,black_92%,transparent_100%)]"
|
className="pointer-events-none absolute inset-0 -z-10 [mask-image:linear-gradient(to_bottom,transparent_4%,rgba(0,0,0,0.06)_28%,rgba(0,0,0,0.3)_50%,black_72%,black_90%,transparent_100%)] lg:[mask-image:linear-gradient(to_right,transparent_22%,rgba(0,0,0,0.06)_40%,rgba(0,0,0,0.3)_56%,black_74%,black_92%,transparent_100%)]"
|
||||||
>
|
>
|
||||||
<div className="h-full w-full [mask-image:linear-gradient(to_right,transparent_0%,black_10%,black_90%,transparent_100%)] lg:[mask-image:linear-gradient(to_bottom,transparent_0%,black_14%,black_86%,transparent_100%)]">
|
<div className="h-full w-full [mask-image:linear-gradient(to_right,transparent_0%,black_14%,black_86%,transparent_100%)] lg:[mask-image:linear-gradient(to_bottom,transparent_0%,black_18%,black_82%,transparent_100%)]">
|
||||||
{harita}
|
{harita}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -625,7 +625,10 @@ export function UniversiteIcerik({
|
|||||||
|
|
||||||
{ustSiraSatiri}
|
{ustSiraSatiri}
|
||||||
|
|
||||||
<div className="mt-8 grid grid-cols-2 gap-4 lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2">
|
{/* relative isolate: konum haritası ızgaranın arkasına serbest
|
||||||
|
katman olarak yayılır, dört kartın altına sarkar (Bilal, 24 Eyl
|
||||||
|
2026); üçüncü sütun yalnız yer tutar. */}
|
||||||
|
<div className="relative isolate mt-8 grid grid-cols-2 gap-4 lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2">
|
||||||
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
|
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
|
||||||
{/* Şablon tam 4 hücre bırakıyor: masaüstünde harita 3. sütunu
|
{/* Şablon tam 4 hücre bırakıyor: masaüstünde harita 3. sütunu
|
||||||
iki satır boyunca kaplıyor, 5. kart ızgarayı bozar. Fakülte
|
iki satır boyunca kaplıyor, 5. kart ızgarayı bozar. Fakülte
|
||||||
|
|||||||
@@ -8,15 +8,26 @@ import {
|
|||||||
|
|
||||||
// Harita il adları modül yükünde bir kez DB biçimine çevrilir.
|
// Harita il adları modül yükünde bir kez DB biçimine çevrilir.
|
||||||
const HARITA_ILLERI = new Set(cities.map((c) => normalizeIlAdi(c.name)));
|
const HARITA_ILLERI = new Set(cities.map((c) => normalizeIlAdi(c.name)));
|
||||||
// Kartı doldurur, kenarları radyal mask ile erir (kesik kanvas yok).
|
/**
|
||||||
const KART_HARITA_SINIFI =
|
* Harita katmanı ızgaranın tamamını kaplar ve biraz dışına sarkar; kartlar
|
||||||
"absolute inset-0 [mask-image:radial-gradient(ellipse_at_center,black_55%,transparent_100%)]";
|
* (beyaz, opak) üstünde kalır, harita aralıklardan ve üçüncü sütunun boş
|
||||||
|
* alanından görünür. Mask'lar (dış: kartların tarafına doğru, iç: dik
|
||||||
|
* yönde iki kenar) hiçbir kenarda kesik kare bırakmaz — geniş ekranda sağdan
|
||||||
|
* sola, telefonda üst satırdan (harita yeri) aşağı doğru erir.
|
||||||
|
*/
|
||||||
|
const KATMAN_SINIFI =
|
||||||
|
"pointer-events-none absolute -inset-x-4 -inset-y-8 -z-10 [mask-image:linear-gradient(to_bottom,transparent_0%,black_14%,black_34%,rgba(0,0,0,0.12)_58%,transparent_82%)] lg:[mask-image:linear-gradient(to_right,transparent_2%,rgba(0,0,0,0.08)_30%,rgba(0,0,0,0.35)_54%,black_74%,black_92%,transparent_100%)]";
|
||||||
|
const IC_MASK_SINIFI =
|
||||||
|
"h-full w-full [mask-image:linear-gradient(to_right,transparent_0%,black_12%,black_88%,transparent_100%)] lg:[mask-image:linear-gradient(to_bottom,transparent_0%,black_16%,black_84%,transparent_100%)]";
|
||||||
|
// İl geniş ekranda üçüncü sütunun ortasına (x≈0,82), telefonda üst satıra gelir.
|
||||||
|
const ODAK_HIZA = { lg: { x: 0.82, y: 0.5 }, dar: { x: 0.5, y: 0.17 } } as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Üniversite sayfasındaki konum kartı: şehir sayfasıyla aynı dot-matrix
|
* Üniversite sayfasındaki konum haritası: şehir sayfasıyla aynı dot-matrix
|
||||||
* harita (DESIGN.md §4.17; Bilal, 24 Eyl 2026: "bu kart kısmına piksel
|
* harita (DESIGN.md §4.17; Bilal, 24 Eyl 2026: "bu kart kısmına piksel
|
||||||
* dokuları ekle"). Türkiye'de il turuncu noktalarla yanar; KKTC/yurt dışında
|
* dokuları ekle", sonra "4 kartın arkasına sarkabilir"). Türkiye'de il
|
||||||
* ülke kontürü + kampüs şehrinde iri turuncu piksel.
|
* turuncu karelerle yanar; KKTC/yurt dışında ülke kontürü + kampüs şehrinde
|
||||||
|
* iri turuncu kare. Ebeveyn ızgara `relative isolate` olmalı.
|
||||||
*/
|
*/
|
||||||
export function UniversiteKonumHaritasi({
|
export function UniversiteKonumHaritasi({
|
||||||
universite,
|
universite,
|
||||||
@@ -26,55 +37,52 @@ export function UniversiteKonumHaritasi({
|
|||||||
il: string | null;
|
il: string | null;
|
||||||
}) {
|
}) {
|
||||||
const yurtdisi = uniUlkeKonum(universite, il);
|
const yurtdisi = uniUlkeKonum(universite, il);
|
||||||
|
let harita: React.ReactNode = null;
|
||||||
|
let altYazi: React.ReactNode = null;
|
||||||
if (yurtdisi) {
|
if (yurtdisi) {
|
||||||
const konum = yurtdisiUniKonum(universite, il);
|
const konum = yurtdisiUniKonum(universite, il);
|
||||||
return (
|
harita = (
|
||||||
<KonumBolumu universite={universite}>
|
<UlkePikselHaritasi
|
||||||
<UlkePikselHaritasi
|
ulke={yurtdisi.ulke}
|
||||||
ulke={yurtdisi.ulke}
|
sehir={konum ? { x: konum.x, y: konum.y } : null}
|
||||||
sehir={konum ? { x: konum.x, y: konum.y } : null}
|
etiket={`${universite} konumunun ${yurtdisi.ulkeAd} haritasındaki görünümü${yurtdisi.sehir ? ` (${yurtdisi.sehir})` : ""}`}
|
||||||
etiket={`${universite} konumunun ${yurtdisi.ulkeAd} haritasındaki görünümü${yurtdisi.sehir ? ` (${yurtdisi.sehir})` : ""}`}
|
doldur
|
||||||
doldur
|
odakHiza={ODAK_HIZA}
|
||||||
className={KART_HARITA_SINIFI}
|
/>
|
||||||
/>
|
|
||||||
<p className="pointer-events-none absolute bottom-0 left-0 text-xs font-medium text-slate-500">
|
|
||||||
{yurtdisi.ulkeAd}
|
|
||||||
{yurtdisi.sehir ? ` · ${yurtdisi.sehir}` : ""}
|
|
||||||
</p>
|
|
||||||
</KonumBolumu>
|
|
||||||
);
|
);
|
||||||
}
|
altYazi = (
|
||||||
|
<p className="pointer-events-none absolute bottom-0 left-0 text-xs font-medium text-slate-500">
|
||||||
if (!il || !HARITA_ILLERI.has(il)) return null;
|
{yurtdisi.ulkeAd}
|
||||||
|
{yurtdisi.sehir ? ` · ${yurtdisi.sehir}` : ""}
|
||||||
return (
|
</p>
|
||||||
<KonumBolumu universite={universite}>
|
);
|
||||||
|
} else if (il && HARITA_ILLERI.has(il)) {
|
||||||
|
harita = (
|
||||||
<TurkiyePikselHaritasi
|
<TurkiyePikselHaritasi
|
||||||
il={il}
|
il={il}
|
||||||
etiket={`${universite} konumunun Türkiye haritasındaki görünümü`}
|
etiket={`${universite} konumunun Türkiye haritasındaki görünümü`}
|
||||||
doldur
|
doldur
|
||||||
className={KART_HARITA_SINIFI}
|
odakHiza={ODAK_HIZA}
|
||||||
/>
|
/>
|
||||||
</KonumBolumu>
|
);
|
||||||
);
|
}
|
||||||
}
|
if (!harita) return null;
|
||||||
|
|
||||||
function KonumBolumu({
|
|
||||||
universite,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
universite: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<section
|
<>
|
||||||
aria-labelledby="universite-konumu"
|
{/* Yer tutucu: telefonda tam genişlikte bir satır, lg'de üçüncü sütun */}
|
||||||
className="relative col-span-2 min-h-44 lg:col-span-1 lg:col-start-3 lg:row-start-1 lg:row-span-2 lg:min-h-0"
|
<section
|
||||||
>
|
aria-labelledby="universite-konumu"
|
||||||
<h2 id="universite-konumu" className="sr-only">
|
className="relative col-span-2 min-h-44 lg:col-span-1 lg:col-start-3 lg:row-start-1 lg:row-span-2 lg:min-h-0"
|
||||||
{universite} kampüs konumu
|
>
|
||||||
</h2>
|
<h2 id="universite-konumu" className="sr-only">
|
||||||
<div className="relative h-full min-h-44 w-full">{children}</div>
|
{universite} kampüs konumu
|
||||||
</section>
|
</h2>
|
||||||
|
{altYazi}
|
||||||
|
</section>
|
||||||
|
<div className={KATMAN_SINIFI}>
|
||||||
|
<div className={IC_MASK_SINIFI}>{harita}</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user