22 MEDIA
33========================= */
44const overlay = document . getElementById ( "overlay" ) ;
5- const audio = document . getElementById ( "background-audio" ) ;
5+ const audio = document . getElementById ( "background-audio" ) ;
66const volumeIcon = document . getElementById ( "volume-icon" ) ;
77const volumeSlider = document . getElementById ( "volume" ) ;
88
9- function startMedia ( ) { overlay . classList . add ( "hidden" ) ; audio ?. play ?. ( ) ; }
10- function setVolume ( v ) {
9+ function startMedia ( ) {
10+ overlay . classList . add ( "hidden" ) ;
11+ audio ?. play ?. ( ) ;
12+ }
13+ function setVolume ( v ) {
1114 v = Math . max ( 0 , Math . min ( 1 , Number ( v ) ) ) ;
1215 if ( audio ) audio . volume = v ;
13- if ( volumeIcon ) volumeIcon . src = v > 0 ? "assets/vol_on.png" : "assets/vol_off.png" ;
16+ if ( volumeIcon )
17+ volumeIcon . src = v > 0 ? "assets/vol_on.png" : "assets/vol_off.png" ;
18+ }
19+ function toggleMute ( ) {
20+ setVolume ( ( audio ?. volume || 0 ) > 0 ? 0 : 1 ) ;
21+ if ( volumeSlider ) volumeSlider . value = audio ?. volume || 0 ;
1422}
15- function toggleMute ( ) { setVolume ( ( ( audio ?. volume || 0 ) > 0 ) ?0 :1 ) ; if ( volumeSlider ) volumeSlider . value = ( audio ?. volume || 0 ) ; }
16- window . addEventListener ( "load" , ( ) => { const v = Number ( localStorage . getItem ( "vol" ) || 0.5 ) ; setVolume ( v ) ; if ( volumeSlider ) volumeSlider . value = v ; } ) ;
17- if ( volumeSlider ) volumeSlider . addEventListener ( "input" , e => { setVolume ( e . target . value ) ; localStorage . setItem ( "vol" , e . target . value ) ; } ) ;
23+ window . addEventListener ( "load" , ( ) => {
24+ const v = Number ( localStorage . getItem ( "vol" ) || 0.5 ) ;
25+ setVolume ( v ) ;
26+ if ( volumeSlider ) volumeSlider . value = v ;
27+ } ) ;
28+ if ( volumeSlider )
29+ volumeSlider . addEventListener ( "input" , ( e ) => {
30+ setVolume ( e . target . value ) ;
31+ localStorage . setItem ( "vol" , e . target . value ) ;
32+ } ) ;
1833
1934/* =========================
2035 MAP / ZOOM
2136========================= */
22- const svg = document . getElementById ( "got-map" ) ;
37+ const svg = document . getElementById ( "got-map" ) ;
2338const panzoom = document . getElementById ( "panzoom" ) ;
2439const backBtn = document . getElementById ( "back-btn" ) ;
2540
2641let activeRegion = null ;
2742
28- function computeZoomToBBox ( bbox , factor = 0.82 ) {
43+ function computeZoomToBBox ( bbox , factor = 0.82 ) {
2944 const viewW = svg . clientWidth ;
3045 const viewH = svg . clientHeight ;
31- const scale = Math . min ( ( viewW * factor ) / bbox . width , ( viewH * factor ) / bbox . height ) ;
32- const cx = bbox . x + bbox . width / 2 ;
33- const cy = bbox . y + bbox . height / 2 ;
34- const tx = ( viewW / 2 ) - ( cx * scale ) ;
35- const ty = ( viewH / 2 ) - ( cy * scale ) ;
46+ const scale = Math . min (
47+ ( viewW * factor ) / bbox . width ,
48+ ( viewH * factor ) / bbox . height
49+ ) ;
50+ const cx = bbox . x + bbox . width / 2 ;
51+ const cy = bbox . y + bbox . height / 2 ;
52+ const tx = viewW / 2 - cx * scale ;
53+ const ty = viewH / 2 - cy * scale ;
3654 return { scale, tx, ty } ;
3755}
3856
39- function zoomToRegion ( regionEl ) {
40- const bbox = regionEl . getBBox ( ) ;
41- const { scale, tx, ty} = computeZoomToBBox ( bbox ) ;
57+ function zoomToRegion ( regionEl ) {
58+ const bbox = regionEl . getBBox ( ) ;
59+ const { scale, tx, ty } = computeZoomToBBox ( bbox ) ;
4260 panzoom . style . transform = `translate(${ tx } px, ${ ty } px) scale(${ scale } )` ;
4361 // Reveal island info AFTER the zoom finishes
44- setTimeout ( ( ) => {
62+ setTimeout ( ( ) => {
4563 regionEl . classList . add ( "active" ) ;
4664 activeRegion = regionEl ;
4765 backBtn . classList . add ( "show" ) ;
4866 } , 650 ) ;
4967}
5068
51- function resetZoom ( ) {
69+ function resetZoom ( ) {
5270 panzoom . style . transform = `translate(0px, 0px) scale(1)` ;
5371 if ( activeRegion ) activeRegion . classList . remove ( "active" ) ;
5472 activeRegion = null ;
@@ -57,31 +75,35 @@ function resetZoom(){
5775
5876/* Enable: clicking island zooms + reveals in-island info (About/Contact);
5977 Projects also zooms, and only then cities become clickable */
60- document . querySelectorAll ( ".region" ) . forEach ( region => {
61- region . addEventListener ( "click" , ( ) => {
78+ document . querySelectorAll ( ".region" ) . forEach ( ( region ) => {
79+ region . addEventListener ( "click" , ( ) => {
6280 // If already active -> do nothing (let Back handle zoom out)
6381 if ( activeRegion === region ) return ;
6482 // Reset current active region first
65- if ( activeRegion ) { activeRegion . classList . remove ( "active" ) ; }
83+ if ( activeRegion ) {
84+ activeRegion . classList . remove ( "active" ) ;
85+ }
6686 zoomToRegion ( region ) ;
6787 } ) ;
6888} ) ;
6989
7090/* Cities: only clickable when Projects island is the active region */
71- const modal = document . getElementById ( "modal" ) ;
72- const modalTitle = document . getElementById ( "modal-title" ) ;
73- const modalBody = document . getElementById ( "modal-body" ) ;
74- const modalClose = document . getElementById ( "modal-close" ) ;
91+ const modal = document . getElementById ( "modal" ) ;
92+ const modalTitle = document . getElementById ( "modal-title" ) ;
93+ const modalBody = document . getElementById ( "modal-body" ) ;
94+ const modalClose = document . getElementById ( "modal-close" ) ;
7595
76- function openModal ( title , content ) {
96+ function openModal ( title , content ) {
7797 modalTitle . textContent = title ;
7898 modalBody . innerHTML = content ;
7999 modal . classList . add ( "open" ) ;
80100}
81- function closeModal ( ) { modal . classList . remove ( "open" ) ; }
101+ function closeModal ( ) {
102+ modal . classList . remove ( "open" ) ;
103+ }
82104
83- document . querySelectorAll ( ".city" ) . forEach ( city => {
84- city . addEventListener ( "click" , ( e ) => {
105+ document . querySelectorAll ( ".city" ) . forEach ( ( city ) => {
106+ city . addEventListener ( "click" , ( e ) => {
85107 e . stopPropagation ( ) ;
86108 if ( ! activeRegion || activeRegion . dataset . key !== "projects" ) return ;
87109 openModal ( city . dataset . title , city . dataset . content ) ;
@@ -91,6 +113,20 @@ document.querySelectorAll(".city").forEach(city=>{
91113modalClose . addEventListener ( "click" , closeModal ) ;
92114
93115/* Global handlers */
94- backBtn . addEventListener ( "click" , ( ) => { closeModal ( ) ; resetZoom ( ) ; } ) ;
95- window . addEventListener ( "keydown" , ( e ) => { if ( e . key === "Escape" ) { closeModal ( ) ; resetZoom ( ) ; } } ) ;
96- window . addEventListener ( "resize" , ( ) => { if ( activeRegion ) { const b = activeRegion . getBBox ( ) ; const { scale, tx, ty} = computeZoomToBBox ( b ) ; panzoom . style . transform = `translate(${ tx } px, ${ ty } px) scale(${ scale } )` ; } } ) ;
116+ backBtn . addEventListener ( "click" , ( ) => {
117+ closeModal ( ) ;
118+ resetZoom ( ) ;
119+ } ) ;
120+ window . addEventListener ( "keydown" , ( e ) => {
121+ if ( e . key === "Escape" ) {
122+ closeModal ( ) ;
123+ resetZoom ( ) ;
124+ }
125+ } ) ;
126+ window . addEventListener ( "resize" , ( ) => {
127+ if ( activeRegion ) {
128+ const b = activeRegion . getBBox ( ) ;
129+ const { scale, tx, ty } = computeZoomToBBox ( b ) ;
130+ panzoom . style . transform = `translate(${ tx } px, ${ ty } px) scale(${ scale } )` ;
131+ }
132+ } ) ;
0 commit comments