diff --git a/src/components/WaveBackground.astro b/src/components/WaveBackground.astro index 1fe69c8..1140b31 100644 --- a/src/components/WaveBackground.astro +++ b/src/components/WaveBackground.astro @@ -72,7 +72,8 @@ const pulseCore = theme === 'purple' ? '#fdf4ff' : '#f0fdfa'; - + + @@ -422,9 +423,11 @@ const pulseCore = theme === 'purple' ? '#fdf4ff' : '#f0fdfa'; } function runLogoPulse(durationMs: number) { - if (!blob1 || !boxGroup) return; + if (!blob1) return; const logo = isLogoTargetVisible(); - if (!logo) { + const entryPath = document.getElementById('threadLogoIn') as SVGPathElement | null; + const exitPath = document.getElementById('threadLogoOut') as SVGPathElement | null; + if (!logo || !entryPath || !exitPath) { schedule(); return; } @@ -432,43 +435,37 @@ const pulseCore = theme === 'purple' ? '#fdf4ff' : '#f0fdfa'; updatePulseColors(false); const rect = logo.getBoundingClientRect(); - const vw = window.innerWidth; const logoLeftX = rect.left + 8; const logoRightX = rect.right - 8; const logoY = rect.top + rect.height / 2; - // DYNAMIC PATHS — bygg paths som GARANTERAT passar logo-positionen. - // Pulsen kommer från vänster off-screen, lite under logon, kurvar UPP in i logo-left, - // glider genom logo, kurvar UT mot höger off-screen, lite över logon - const offLeft = -100; - const offRight = vw + 100; - const dipY = logoY + rect.height * 0.7; // entry kommer underifrån vänster - const riseY = logoY - rect.height * 0.7; // exit lämnar uppåt höger + // Använd permanenta wave-trådar (threadLogoIn + threadLogoOut) + // Hitta var de KORSAR logo-x-koordinater i screen-space + const entryTotal = entryPath.getTotalLength(); + const exitTotal = exitPath.getTotalLength(); - const NS_local = 'http://www.w3.org/2000/svg'; - // Entry path (lila): från off-screen vänster (dipY) kurvar upp till logo-left - // TYDLIG SOLID stroke + glow filter så användaren ser tråden ordentligt - const entryPath = document.createElementNS(NS_local, 'path'); - entryPath.setAttribute('d', `M ${offLeft} ${dipY} C ${logoLeftX - 200} ${dipY}, ${logoLeftX - 80} ${logoY}, ${logoLeftX} ${logoY}`); - entryPath.setAttribute('fill', 'none'); - entryPath.setAttribute('stroke', '#e879f9'); - entryPath.setAttribute('stroke-width', '2'); - entryPath.setAttribute('opacity', '0.85'); - entryPath.setAttribute('filter', 'url(#pulseGlowMid)'); - boxGroup.appendChild(entryPath); + // Sök på entryPath efter den punkt där SVG-pt.x motsvarar logoLeftX + // OBS: pathen är i SVG-koord (-1000..2600), klient-koord är getBoundingClientRect + // Vi mappar via path.getBoundingClientRect() vs path.getBBox() + function findLenForScreenX(p: SVGPathElement, targetScreenX: number, total: number): number { + // SVG till screen: använd ctm + const ctm = p.getScreenCTM(); + if (!ctm) return total * 0.5; + let bestLen = total; + for (let l = 0; l <= total; l += 6) { + const pt = p.getPointAtLength(l); + const screenX = ctm.a * pt.x + ctm.c * pt.y + ctm.e; + if (screenX >= targetScreenX) { + bestLen = l; + break; + } + } + return bestLen; + } - // Exit path (cyan): från logo-right kurvar upp till off-screen höger (riseY) - const exitPath = document.createElementNS(NS_local, 'path'); - exitPath.setAttribute('d', `M ${logoRightX} ${logoY} C ${logoRightX + 80} ${logoY}, ${logoRightX + 200} ${riseY}, ${offRight} ${riseY}`); - exitPath.setAttribute('fill', 'none'); - exitPath.setAttribute('stroke', '#4fd1c5'); - exitPath.setAttribute('stroke-width', '2'); - exitPath.setAttribute('opacity', '0.85'); - exitPath.setAttribute('filter', 'url(#pulseGlowMid)'); - boxGroup.appendChild(exitPath); - - const entryLen = entryPath.getTotalLength(); - const exitLen = exitPath.getTotalLength(); + const entryLen = findLenForScreenX(entryPath, logoLeftX, entryTotal); + const exitStartLen = findLenForScreenX(exitPath, logoRightX, exitTotal); + const exitLen = Math.min(exitTotal, exitStartLen + (exitTotal - exitStartLen) * 0.9); const phase1Duration = durationMs * 0.35; const phase2Duration = durationMs * 0.30; @@ -501,12 +498,16 @@ const pulseCore = theme === 'purple' ? '#fdf4ff' : '#f0fdfa'; const fade = fadeFactor(globalT); if (elapsed <= phase1Duration) { - // Glide in på entry-path (lila) + // Glide in på entry-path (lila, permanent tråd) const t = elapsed / phase1Duration; const len = t * entryLen; const pt = entryPath.getPointAtLength(len); - const angle = tangentAngle(entryPath, len, entryLen); - applyTransform(blob1!, pt.x, pt.y, angle); + const angle = tangentAngle(entryPath, len, entryTotal); + // Konvertera SVG-koord till screen-koord + const ctm = entryPath.getScreenCTM(); + const sx = ctm ? (ctm.a * pt.x + ctm.c * pt.y + ctm.e) : pt.x; + const sy = ctm ? (ctm.b * pt.x + ctm.d * pt.y + ctm.f) : pt.y; + applyTransform(blob1!, sx, sy, angle); blob1!.setAttribute('opacity', String(fade)); } else if (elapsed <= phase1Duration + phase2Duration) { // INSIDE logo — pulse vid logo-center, ändra logo-färg @@ -526,12 +527,15 @@ const pulseCore = theme === 'purple' ? '#fdf4ff' : '#f0fdfa'; setPulseTint(greenColor); } } else { - // Glide ut på exit-path (grön) + // Glide ut på exit-path (permanent cyan-tråd) const t = Math.min((elapsed - phase1Duration - phase2Duration) / phase3Duration, 1); - const len = t * exitLen; + const len = exitStartLen + (exitLen - exitStartLen) * t; const pt = exitPath.getPointAtLength(len); - const angle = tangentAngle(exitPath, len, exitLen); - applyTransform(blob1!, pt.x, pt.y, angle); + const angle = tangentAngle(exitPath, len, exitTotal); + const ctm = exitPath.getScreenCTM(); + const sx = ctm ? (ctm.a * pt.x + ctm.c * pt.y + ctm.e) : pt.x; + const sy = ctm ? (ctm.b * pt.x + ctm.d * pt.y + ctm.f) : pt.y; + applyTransform(blob1!, sx, sy, angle); blob1!.setAttribute('opacity', String(fade)); if (logo!.getAttribute('data-logo-tint')) { logo!.removeAttribute('data-logo-tint'); @@ -543,11 +547,7 @@ const pulseCore = theme === 'purple' ? '#fdf4ff' : '#f0fdfa'; } else { blob1!.setAttribute('opacity', '0'); logo!.removeAttribute('data-logo-tint'); - // Cleanup dynamic paths - if (entryPath.parentNode) entryPath.parentNode.removeChild(entryPath); - if (exitPath.parentNode) exitPath.parentNode.removeChild(exitPath); - // ÅTERSTÄLL pulse-gradient till default cyan-tema så nästa - // vanliga wave-pulse inte har stale rosa färg + // Återställ pulse-gradient till default cyan-tema updatePulseColors(false); isPulsing = false; }