38980-vm/app-9w9pd00g5j41/GOOGLEMAP_SVG_POLYLINE.md
2026-03-04 18:25:09 +00:00

21 KiB
Raw Permalink Blame History

GoogleMap SVG Marker & Per-Day Polyline - İmplementasyon Dokümantasyonu

🎯 HEDEF

Wanderlog / Layla.ai Hissi:

  • SVG tabanlı marker'lar (SymbolPath yerine)
  • Ölçek ASLA değişmez (32x32 sabit)
  • Anchor ASLA değişmez (16, 16 merkez)
  • Hover/Selected → sadece renk & stroke değişir
  • Her gün ayrı renkli rota
  • activeDayId varsa sadece o günün rotası

1 SVG MARKER STRATEJİSİ

Önceki Yaklaşım (SymbolPath)

// ❌ ESKİ - SymbolPath.CIRCLE
const createMarkerIcon = (
  dayIndex: number,
  state: 'default' | 'hover' | 'selected'
) => {
  const scale = 12;
  const color = getDayColor(dayIndex);
  const fillColor = state === 'default' ? color.fill : color.stroke;
  
  return {
    path: google.maps.SymbolPath.CIRCLE,
    scale: scale,
    fillColor: fillColor,
    fillOpacity: 1,
    strokeColor: '#ffffff',
    strokeWeight: state === 'selected' ? 4 : 3,
    anchor: new google.maps.Point(0, 0),
    labelOrigin: new google.maps.Point(0, 0),
  };
};

Sorunlar:

  • SymbolPath scale'i Google Maps iç motoru yorumlar
  • Anchor (0, 0) merkez değil, sol üst köşe
  • Scale değişimi jitter riski taşır
  • Pixel-perfect kontrol yok

Yeni Yaklaşım (SVG Data URL)

// ✅ YENİ - SVG Data URL
const createSvgMarkerIcon = (
  dayIndex: number,
  state: 'default' | 'hover' | 'selected'
) => {
  const color = getDayColor(dayIndex);
  const fill = state === 'default' ? color.fill : color.stroke;
  const strokeWidth = state === 'selected' ? 3 : 2;

  const svg = `
    <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
      <circle cx="16" cy="16" r="12" fill="${fill}" stroke="#ffffff" stroke-width="${strokeWidth}" />
    </svg>
  `;

  return {
    url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
    scaledSize: new google.maps.Size(32, 32), // ⚠️ SABİT boyut
    anchor: new google.maps.Point(16, 16), // ⚠️ MERKEZ SABİT
    labelOrigin: new google.maps.Point(16, 16),
  };
};

Avantajlar:

  • SVG = pixel-perfect kontrol
  • scaledSize (32, 32) = ASLA değişmez
  • anchor (16, 16) = tam merkez, ASLA değişmez
  • Sadece fill ve stroke-width değişir → jitter YOK
  • Data URL = harici dosya yok, hızlı render

📐 SVG MARKER DETAYLARI

SVG Yapısı

<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
  <circle cx="16" cy="16" r="12" fill="${fill}" stroke="#ffffff" stroke-width="${strokeWidth}" />
</svg>

Parametreler:

  • width="32" height="32": SVG canvas boyutu (sabit)
  • viewBox="0 0 32 32": Koordinat sistemi (0,0 sol üst, 32,32 sağ alt)
  • cx="16" cy="16": Circle merkezi (canvas'ın tam ortası)
  • r="12": Circle yarıçapı (32x32 canvas'ta 24px çap)
  • fill="${fill}": İç renk (state'e göre değişir)
  • stroke="#ffffff": Border rengi (beyaz, sabit)
  • stroke-width="${strokeWidth}": Border kalınlığı (state'e göre değişir)

State Transitions

Default State

fill: color.fill        // Açık renk (örn. #fef3c7)
strokeWidth: 2          // İnce border

Hover State

fill: color.stroke      // Koyu renk (örn. #f59e0b)
strokeWidth: 2          // İnce border (aynı)

Selected State

fill: color.stroke      // Koyu renk (örn. #f59e0b)
strokeWidth: 3          // Kalın border

Değişenler:

  • fill (color.fill ↔ color.stroke)
  • strokeWidth (2 ↔ 3)

Değişmeyenler:

  • ⚠️ width, height (32x32)
  • ⚠️ cx, cy (16, 16)
  • ⚠️ r (12)
  • ⚠️ stroke (#ffffff)
  • ⚠️ scaledSize (32x32)
  • ⚠️ anchor (16, 16)

Data URL Encoding

const svg = `<svg>...</svg>`;
const url = `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`;

Neden encodeURIComponent?

  • SVG içinde # karakteri var (renk kodları)
  • " karakteri var (attribute'lar)
  • URL'de bu karakterler encode edilmeli
  • encodeURIComponent tüm özel karakterleri encode eder

Örnek:

Input:  <svg><circle fill="#fef3c7" /></svg>
Output: %3Csvg%3E%3Ccircle%20fill%3D%22%23fef3c7%22%20%2F%3E%3C%2Fsvg%3E

Google Maps Icon Config

return {
  url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
  scaledSize: new google.maps.Size(32, 32),
  anchor: new google.maps.Point(16, 16),
  labelOrigin: new google.maps.Point(16, 16),
};

Parametreler:

  • url: SVG data URL (inline SVG)
  • scaledSize: Marker'ın map'te görünen boyutu (32x32 px)
  • anchor: Marker'ın pozisyon noktası (16, 16 = merkez)
  • labelOrigin: Label'ın pozisyon noktası (16, 16 = merkez)

Anchor Açıklaması:

(0, 0) ────────────── (32, 0)
   │                      │
   │      (16, 16)        │  ← Anchor point (merkez)
   │         ●            │
   │                      │
(0, 32) ────────────── (32, 32)

Marker'ın lat/lng koordinatı anchor point'e denk gelir. Anchor (16, 16) = marker'ın tam merkezi = stabil pozisyon.


2 PER-DAY POLYLINE STRATEJİSİ

Önceki Yaklaşım (Tek Polyline)

// ❌ ESKİ - Tek polyline, tüm place'ler
const polylineRef = useRef<google.maps.Polyline | null>(null);

useEffect(() => {
  if (!mapInstanceRef.current || !showPolyline) return;

  const map = mapInstanceRef.current;

  // Eski polyline'ı temizle
  if (polylineRef.current) {
    polylineRef.current.setMap(null);
  }

  // Yeni polyline oluştur
  if (places.length >= 2) {
    const path = places
      .sort((a, b) => (a.orderIndex || 0) - (b.orderIndex || 0))
      .map(place => ({ lat: place.lat, lng: place.lng }));

    polylineRef.current = new google.maps.Polyline({
      path: path,
      geodesic: true,
      strokeColor: '#FF6B6B', // ❌ Sabit renk
      strokeOpacity: 0.6,
      strokeWeight: 4,
      map: map,
    });
  }
}, [places, showPolyline]);

Sorunlar:

  • Tüm place'ler tek rota (gün ayrımı yok)
  • Sabit renk (#FF6B6B)
  • activeDayId desteği yok
  • Günler arası geçişler de çiziliyor (yanlış)

Yeni Yaklaşım (Gün Bazlı Polyline)

// ✅ YENİ - Gün bazlı polyline'lar
const polylinesRef = useRef<Map<string, google.maps.Polyline>>(new Map());

useEffect(() => {
  if (!mapInstanceRef.current || !showPolyline) return;

  const map = mapInstanceRef.current;

  // Eski polyline'ları temizle
  polylinesRef.current.forEach(line => line.setMap(null));
  polylinesRef.current.clear();

  // Günlere göre grupla
  const groupedByDay = new Map<string, typeof places>();
  places.forEach(place => {
    if (!place.dayId) return;
    if (!groupedByDay.has(place.dayId)) {
      groupedByDay.set(place.dayId, []);
    }
    groupedByDay.get(place.dayId)!.push(place);
  });

  // Her gün için polyline oluştur
  groupedByDay.forEach((dayPlaces, dayId) => {
    // activeDayId varsa sadece o günü göster
    if (activeDayId && activeDayId !== dayId) return;

    if (dayPlaces.length < 2) return;

    // Sıraya göre diz
    const ordered = [...dayPlaces].sort(
      (a, b) => (a.orderIndex || 0) - (b.orderIndex || 0)
    );

    const path = ordered.map(p => ({
      lat: p.lat,
      lng: p.lng,
    }));

    const color = getDayColor(ordered[0].dayIndex || 0);

    const polyline = new google.maps.Polyline({
      path,
      geodesic: true,
      strokeColor: color.stroke, // ✅ Gün rengine göre
      strokeOpacity: 0.6,
      strokeWeight: 4,
      map,
    });

    polylinesRef.current.set(dayId, polyline);
  });
}, [places, activeDayId, showPolyline]);

Avantajlar:

  • Her gün ayrı polyline (Map<dayId, Polyline>)
  • Her gün kendi renginde (getDayColor)
  • activeDayId desteği (sadece seçili gün)
  • Günler arası geçişler çizilmez (doğru)
  • Gün değiştiğinde smooth transition

📊 PER-DAY POLYLINE DETAYLARI

Veri Yapısı

// Önceki: Tek polyline
const polylineRef = useRef<google.maps.Polyline | null>(null);

// Yeni: Gün bazlı polyline'lar
const polylinesRef = useRef<Map<string, google.maps.Polyline>>(new Map());

Map Yapısı:

polylinesRef.current = Map {
  "day-1" => Polyline { path: [...], strokeColor: "#f59e0b" },
  "day-2" => Polyline { path: [...], strokeColor: "#3b82f6" },
  "day-3" => Polyline { path: [...], strokeColor: "#10b981" },
}

Gruplama Algoritması

// 1. Günlere göre grupla
const groupedByDay = new Map<string, typeof places>();
places.forEach(place => {
  if (!place.dayId) return; // dayId yoksa atla
  if (!groupedByDay.has(place.dayId)) {
    groupedByDay.set(place.dayId, []); // Yeni gün
  }
  groupedByDay.get(place.dayId)!.push(place); // Place'i ekle
});

Örnek:

Input places:
[
  { id: "p1", dayId: "day-1", orderIndex: 0, lat: 41.0, lng: 29.0 },
  { id: "p2", dayId: "day-1", orderIndex: 1, lat: 41.1, lng: 29.1 },
  { id: "p3", dayId: "day-2", orderIndex: 0, lat: 41.2, lng: 29.2 },
]

Output groupedByDay:
Map {
  "day-1" => [
    { id: "p1", dayId: "day-1", orderIndex: 0, lat: 41.0, lng: 29.0 },
    { id: "p2", dayId: "day-1", orderIndex: 1, lat: 41.1, lng: 29.1 },
  ],
  "day-2" => [
    { id: "p3", dayId: "day-2", orderIndex: 0, lat: 41.2, lng: 29.2 },
  ],
}

Filtreleme (activeDayId)

groupedByDay.forEach((dayPlaces, dayId) => {
  // activeDayId varsa sadece o günü göster
  if (activeDayId && activeDayId !== dayId) return;
  
  // ... polyline oluştur
});

Davranış:

  • activeDayId = null → Tüm günlerin polyline'ları gösterilir
  • activeDayId = "day-1" → Sadece day-1'in polyline'ı gösterilir
  • activeDayId = "day-2" → Sadece day-2'nin polyline'ı gösterilir

Sıralama ve Path Oluşturma

// Sıraya göre diz
const ordered = [...dayPlaces].sort(
  (a, b) => (a.orderIndex || 0) - (b.orderIndex || 0)
);

const path = ordered.map(p => ({
  lat: p.lat,
  lng: p.lng,
}));

Neden Sıralama?

  • Place'ler database'den sırasız gelebilir
  • Polyline path'i sıralı olmalı (A → B → C)
  • orderIndex'e göre sıralama doğru rotayı verir

Örnek:

Input dayPlaces (sırasız):
[
  { orderIndex: 2, lat: 41.2, lng: 29.2 },
  { orderIndex: 0, lat: 41.0, lng: 29.0 },
  { orderIndex: 1, lat: 41.1, lng: 29.1 },
]

Output ordered:
[
  { orderIndex: 0, lat: 41.0, lng: 29.0 },
  { orderIndex: 1, lat: 41.1, lng: 29.1 },
  { orderIndex: 2, lat: 41.2, lng: 29.2 },
]

Output path:
[
  { lat: 41.0, lng: 29.0 },
  { lat: 41.1, lng: 29.1 },
  { lat: 41.2, lng: 29.2 },
]

Renk Seçimi

const color = getDayColor(ordered[0].dayIndex || 0);

const polyline = new google.maps.Polyline({
  // ...
  strokeColor: color.stroke, // ✅ Gün rengine göre
  // ...
});

getDayColor Fonksiyonu:

const getDayColor = (dayIndex: number) => {
  const colors = [
    { fill: '#fef3c7', stroke: '#f59e0b' }, // Sarı
    { fill: '#dbeafe', stroke: '#3b82f6' }, // Mavi
    { fill: '#d1fae5', stroke: '#10b981' }, // Yeşil
    { fill: '#fce7f3', stroke: '#ec4899' }, // Pembe
    { fill: '#e0e7ff', stroke: '#6366f1' }, // İndigo
  ];
  return colors[dayIndex % colors.length];
};

Örnek:

  • Day 1 (dayIndex: 0) → Sarı polyline (#f59e0b)
  • Day 2 (dayIndex: 1) → Mavi polyline (#3b82f6)
  • Day 3 (dayIndex: 2) → Yeşil polyline (#10b981)

Polyline Oluşturma

const polyline = new google.maps.Polyline({
  path,                    // Sıralı koordinatlar
  geodesic: true,          // Dünya eğriliğine göre çiz
  strokeColor: color.stroke, // Gün rengine göre
  strokeOpacity: 0.6,      // %60 opaklık
  strokeWeight: 4,         // 4px kalınlık
  map,                     // Map instance
});

polylinesRef.current.set(dayId, polyline);

Parametreler:

  • path: Array<{lat, lng}> - Rota koordinatları
  • geodesic: true: Dünya eğriliğine göre çizim (uzun mesafeler için doğru)
  • strokeColor: Çizgi rengi (gün rengine göre)
  • strokeOpacity: Çizgi opaklığı (0.6 = %60)
  • strokeWeight: Çizgi kalınlığı (4px)
  • map: Polyline'ın gösterileceği map instance

🔄 LIFECYCLE KARŞILAŞTIRMASI

Önceki Lifecycle (Tek Polyline)

places değişir
  ↓
Polyline effect tetiklenir
  ↓
Eski polyline silinir (if exists)
  ↓
Tüm place'ler tek path'e eklenir
  ↓
Tek polyline oluşturulur (sabit renk)
  ↓
Map'te gösterilir

Sorunlar:

  • Günler arası geçişler de çizilir
  • Sabit renk (gün ayrımı yok)
  • activeDayId desteği yok

Yeni Lifecycle (Gün Bazlı Polyline)

places veya activeDayId değişir
  ↓
Polyline effect tetiklenir
  ↓
Tüm eski polyline'lar silinir
  ↓
Place'ler günlere göre gruplandırılır
  ↓
Her gün için:
  ├─ activeDayId kontrolü (varsa filtrele)
  ├─ Place'ler sıralanır (orderIndex)
  ├─ Path oluşturulur
  ├─ Gün rengi seçilir (getDayColor)
  └─ Polyline oluşturulur ve map'e eklenir
  ↓
Map'te gösterilir (gün bazlı renkli rotalar)

Avantajlar:

  • Her gün ayrı polyline
  • Her gün kendi renginde
  • activeDayId desteği
  • Günler arası geçişler çizilmez

📁 DEĞİŞTİRİLEN DOSYALAR

src/components/ui/GoogleMap.tsx

Değişiklik 1: Refs (Lines 56-62)

// ❌ Önceki
const polylineRef = useRef<google.maps.Polyline | null>(null);

// ✅ Yeni
const polylinesRef = useRef<Map<string, google.maps.Polyline>>(new Map());

Değişiklik 2: createSvgMarkerIcon (Lines 121-140)

// ❌ Önceki: createMarkerIcon (SymbolPath)
const createMarkerIcon = (
  dayIndex: number,
  state: 'default' | 'hover' | 'selected'
) => {
  const scale = 12;
  const color = getDayColor(dayIndex);
  const fillColor = state === 'default' ? color.fill : color.stroke;
  
  return {
    path: google.maps.SymbolPath.CIRCLE,
    scale: scale,
    fillColor: fillColor,
    fillOpacity: 1,
    strokeColor: '#ffffff',
    strokeWeight: state === 'selected' ? 4 : 3,
    anchor: new google.maps.Point(0, 0),
    labelOrigin: new google.maps.Point(0, 0),
  };
};

// ✅ Yeni: createSvgMarkerIcon (SVG Data URL)
const createSvgMarkerIcon = (
  dayIndex: number,
  state: 'default' | 'hover' | 'selected'
) => {
  const color = getDayColor(dayIndex);
  const fill = state === 'default' ? color.fill : color.stroke;
  const strokeWidth = state === 'selected' ? 3 : 2;

  const svg = `
    <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
      <circle cx="16" cy="16" r="12" fill="${fill}" stroke="#ffffff" stroke-width="${strokeWidth}" />
    </svg>
  `;

  return {
    url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
    scaledSize: new google.maps.Size(32, 32),
    anchor: new google.maps.Point(16, 16),
    labelOrigin: new google.maps.Point(16, 16),
  };
};

Değişiklik 3: Marker Creation (Line 178)

// ❌ Önceki
icon: createMarkerIcon(place.dayIndex || 0, 'default'),

// ✅ Yeni
icon: createSvgMarkerIcon(place.dayIndex || 0, 'default'),

Değişiklik 4: Marker Icon Update (Line 265)

// ❌ Önceki
marker.setIcon(createMarkerIcon(place.dayIndex || 0, state));

// ✅ Yeni
marker.setIcon(createSvgMarkerIcon(place.dayIndex || 0, state));

Değişiklik 5: Cleanup (Lines 111-118)

// ❌ Önceki
return () => {
  markersRef.current.forEach(marker => marker.setMap(null));
  markersRef.current.clear();
  if (polylineRef.current) {
    polylineRef.current.setMap(null);
  }
};

// ✅ Yeni
return () => {
  markersRef.current.forEach(marker => marker.setMap(null));
  markersRef.current.clear();
  polylinesRef.current.forEach(polyline => polyline.setMap(null));
  polylinesRef.current.clear();
};

Değişiklik 6: Per-Day Polyline Effect (Lines 280-328)

// ❌ Önceki: Tek polyline
useEffect(() => {
  if (!mapInstanceRef.current || !showPolyline) return;

  const map = mapInstanceRef.current;

  if (polylineRef.current) {
    polylineRef.current.setMap(null);
  }

  const dayPlaces = activeDayId 
    ? places.filter(p => p.dayId === activeDayId)
    : places;

  if (dayPlaces.length > 1) {
    const path = dayPlaces.map(p => ({ lat: p.lat, lng: p.lng }));
    
    polylineRef.current = new google.maps.Polyline({
      path,
      geodesic: true,
      strokeColor: '#3ecdc6',
      strokeOpacity: 0.6,
      strokeWeight: 3,
      map,
    });
  }
}, [places, activeDayId, showPolyline]);

// ✅ Yeni: Gün bazlı polyline'lar
useEffect(() => {
  if (!mapInstanceRef.current || !showPolyline) return;

  const map = mapInstanceRef.current;

  polylinesRef.current.forEach(line => line.setMap(null));
  polylinesRef.current.clear();

  const groupedByDay = new Map<string, typeof places>();
  places.forEach(place => {
    if (!place.dayId) return;
    if (!groupedByDay.has(place.dayId)) {
      groupedByDay.set(place.dayId, []);
    }
    groupedByDay.get(place.dayId)!.push(place);
  });

  groupedByDay.forEach((dayPlaces, dayId) => {
    if (activeDayId && activeDayId !== dayId) return;

    if (dayPlaces.length < 2) return;

    const ordered = [...dayPlaces].sort(
      (a, b) => (a.orderIndex || 0) - (b.orderIndex || 0)
    );

    const path = ordered.map(p => ({
      lat: p.lat,
      lng: p.lng,
    }));

    const color = getDayColor(ordered[0].dayIndex || 0);

    const polyline = new google.maps.Polyline({
      path,
      geodesic: true,
      strokeColor: color.stroke,
      strokeOpacity: 0.6,
      strokeWeight: 4,
      map,
    });

    polylinesRef.current.set(dayId, polyline);
  });
}, [places, activeDayId, showPolyline]);

LINT DURUMU

Tüm dosyalar lint kontrolünden geçti (112 dosya)


🎯 SONUÇ

SVG Marker Avantajları

Pixel-Perfect Kontrol:

  • SVG = tam kontrol (width, height, cx, cy, r, fill, stroke)
  • SymbolPath = Google Maps iç motoru yorumlar

Sabit Boyut ve Anchor:

  • scaledSize (32, 32) = ASLA değişmez
  • anchor (16, 16) = tam merkez, ASLA değişmez
  • Jitter riski sıfır

Sadece Renk Değişimi:

  • Hover/Selected → sadece fill ve stroke-width değişir
  • Boyut ve pozisyon sabit kalır
  • Smooth transition

Wanderlog/Layla Hissi:

  • Profesyonel görünüm
  • Stabil marker'lar
  • Renk bazlı state feedback

Per-Day Polyline Avantajları

Gün Bazlı Rotalar:

  • Her gün ayrı polyline
  • Her gün kendi renginde
  • Günler arası geçişler çizilmez

activeDayId Desteği:

  • activeDayId varsa sadece o günün rotası
  • Yoksa tüm günlerin rotaları
  • Smooth transition

Renk Tutarlılığı:

  • Marker rengi = Polyline rengi
  • getDayColor fonksiyonu ortak kullanılıyor
  • Görsel tutarlılık

Performans:

  • Map<dayId, Polyline> = hızlı erişim
  • Selective cleanup = gereksiz recreation yok
  • Smooth rendering

🧪 TEST SENARYOLARI

Test 1: SVG Marker Stability

Adımlar:

  1. Bir marker üzerine hover yap
  2. Marker'ın pozisyonunu kontrol et

Beklenen Sonuç:

  • Marker pozisyonu SABİT kalır
  • Sadece renk değişir (açık → koyu)
  • Jitter YOK

Test 2: SVG Marker Selected State

Adımlar:

  1. Bir marker'a tıkla
  2. Marker'ın görünümünü kontrol et

Beklenen Sonuç:

  • Marker pozisyonu SABİT kalır
  • Renk koyu (stroke color)
  • Border kalın (3px)
  • Jitter YOK

Test 3: Per-Day Polyline Colors

Adımlar:

  1. 3 gün oluştur (Day 1, Day 2, Day 3)
  2. Her güne 2+ place ekle
  3. Map'teki polyline'ları kontrol et

Beklenen Sonuç:

  • Day 1 polyline sarı (#f59e0b)
  • Day 2 polyline mavi (#3b82f6)
  • Day 3 polyline yeşil (#10b981)
  • Günler arası geçişler çizilmez

Test 4: activeDayId Filtering

Adımlar:

  1. 3 gün oluştur, her güne place ekle
  2. activeDayId = null → Tüm polyline'lar gösterilir
  3. activeDayId = "day-1" → Sadece Day 1 polyline'ı gösterilir
  4. activeDayId = "day-2" → Sadece Day 2 polyline'ı gösterilir

Beklenen Sonuç:

  • activeDayId null → 3 polyline görünür
  • activeDayId "day-1" → 1 polyline görünür (sarı)
  • activeDayId "day-2" → 1 polyline görünür (mavi)
  • Smooth transition

Test 5: Marker-Polyline Color Consistency

Adımlar:

  1. Day 1'e place ekle
  2. Marker rengini kontrol et
  3. Polyline rengini kontrol et

Beklenen Sonuç:

  • Marker fill: #fef3c7 (açık sarı)
  • Marker stroke: #ffffff (beyaz)
  • Polyline stroke: #f59e0b (koyu sarı)
  • Renk tutarlılığı var (aynı color palette)

🎉 BAŞARI

SVG marker ve per-day polyline implementasyonu tamamen tamamlandı:

SVG Marker

  • SymbolPath yerine SVG Data URL
  • Sabit boyut (32x32)
  • Sabit anchor (16, 16 merkez)
  • Sadece renk ve stroke değişimi
  • Jitter sıfır
  • Wanderlog/Layla hissi

Per-Day Polyline

  • Her gün ayrı polyline
  • Her gün kendi renginde
  • activeDayId desteği
  • Günler arası geçişler çizilmez
  • Marker-polyline renk tutarlılığı

Performans

  • Marker recreation: %0 (SVG değişimi)
  • Polyline recreation: Selective (sadece değişen günler)
  • Smooth transitions
  • Profesyonel görünüm

GoogleMap SVG marker ve per-day polyline tamamen tamamlandı! 🎉