💥 Update interactive Globe with latest features
It was long and painful but it's finally looking neat! - place markers on globe following camera world matrix - show location on marker hover instead of showing label near dot - make marker easier to select - make rotation constant no matter the monitor refresh rate by using a delta timed animation - [wip] lighting the globe with a dark area / todo: how to get current sun lighting from a location? Merci Julien :)
@@ -22,6 +22,7 @@
|
|||||||
"motion": "^10.14.2",
|
"motion": "^10.14.2",
|
||||||
"ogl": "^0.0.99",
|
"ogl": "^0.0.99",
|
||||||
"sanitize.css": "^13.0.0",
|
"sanitize.css": "^13.0.0",
|
||||||
|
"suncalc": "^1.9.0",
|
||||||
"swell-js": "^3.17.6",
|
"swell-js": "^3.17.6",
|
||||||
"tweakpane": "^3.1.0"
|
"tweakpane": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -29,13 +30,13 @@
|
|||||||
"@sveltejs/adapter-auto": "^1.0.0-next.80",
|
"@sveltejs/adapter-auto": "^1.0.0-next.80",
|
||||||
"@sveltejs/adapter-node": "^1.0.0-next.95",
|
"@sveltejs/adapter-node": "^1.0.0-next.95",
|
||||||
"@sveltejs/adapter-vercel": "^1.0.0-next.77",
|
"@sveltejs/adapter-vercel": "^1.0.0-next.77",
|
||||||
"@sveltejs/kit": "^1.0.0-next.499",
|
"@sveltejs/kit": "^1.0.0-next.503",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||||
"@typescript-eslint/parser": "^5.38.0",
|
"@typescript-eslint/parser": "^5.38.0",
|
||||||
"base-64": "^1.0.0",
|
"base-64": "^1.0.0",
|
||||||
"browserslist": "^4.21.4",
|
"browserslist": "^4.21.4",
|
||||||
"cssnano": "^5.1.13",
|
"cssnano": "^5.1.13",
|
||||||
"eslint": "^8.23.1",
|
"eslint": "^8.24.0",
|
||||||
"eslint-plugin-svelte3": "^4.0.0",
|
"eslint-plugin-svelte3": "^4.0.0",
|
||||||
"postcss": "^8.4.16",
|
"postcss": "^8.4.16",
|
||||||
"postcss-focus-visible": "^7.1.0",
|
"postcss-focus-visible": "^7.1.0",
|
||||||
|
|||||||
88
pnpm-lock.yaml
generated
@@ -5,7 +5,7 @@ specifiers:
|
|||||||
'@sveltejs/adapter-auto': ^1.0.0-next.80
|
'@sveltejs/adapter-auto': ^1.0.0-next.80
|
||||||
'@sveltejs/adapter-node': ^1.0.0-next.95
|
'@sveltejs/adapter-node': ^1.0.0-next.95
|
||||||
'@sveltejs/adapter-vercel': ^1.0.0-next.77
|
'@sveltejs/adapter-vercel': ^1.0.0-next.77
|
||||||
'@sveltejs/kit': ^1.0.0-next.499
|
'@sveltejs/kit': ^1.0.0-next.503
|
||||||
'@typescript-eslint/eslint-plugin': ^5.38.0
|
'@typescript-eslint/eslint-plugin': ^5.38.0
|
||||||
'@typescript-eslint/parser': ^5.38.0
|
'@typescript-eslint/parser': ^5.38.0
|
||||||
base-64: ^1.0.0
|
base-64: ^1.0.0
|
||||||
@@ -13,7 +13,7 @@ specifiers:
|
|||||||
cssnano: ^5.1.13
|
cssnano: ^5.1.13
|
||||||
dayjs: ^1.11.5
|
dayjs: ^1.11.5
|
||||||
embla-carousel: ^7.0.3
|
embla-carousel: ^7.0.3
|
||||||
eslint: ^8.23.1
|
eslint: ^8.24.0
|
||||||
eslint-plugin-svelte3: ^4.0.0
|
eslint-plugin-svelte3: ^4.0.0
|
||||||
focus-visible: ^5.2.0
|
focus-visible: ^5.2.0
|
||||||
motion: ^10.14.2
|
motion: ^10.14.2
|
||||||
@@ -25,6 +25,7 @@ specifiers:
|
|||||||
postcss-sort-media-queries: ^4.3.0
|
postcss-sort-media-queries: ^4.3.0
|
||||||
sanitize.css: ^13.0.0
|
sanitize.css: ^13.0.0
|
||||||
sass: ^1.55.0
|
sass: ^1.55.0
|
||||||
|
suncalc: ^1.9.0
|
||||||
svelte: ^3.50.1
|
svelte: ^3.50.1
|
||||||
svelte-check: ^2.9.0
|
svelte-check: ^2.9.0
|
||||||
svelte-preprocess: ^4.10.7
|
svelte-preprocess: ^4.10.7
|
||||||
@@ -42,6 +43,7 @@ dependencies:
|
|||||||
motion: 10.14.2
|
motion: 10.14.2
|
||||||
ogl: 0.0.99
|
ogl: 0.0.99
|
||||||
sanitize.css: 13.0.0
|
sanitize.css: 13.0.0
|
||||||
|
suncalc: 1.9.0
|
||||||
swell-js: 3.17.6
|
swell-js: 3.17.6
|
||||||
tweakpane: 3.1.0
|
tweakpane: 3.1.0
|
||||||
|
|
||||||
@@ -49,14 +51,14 @@ devDependencies:
|
|||||||
'@sveltejs/adapter-auto': 1.0.0-next.80
|
'@sveltejs/adapter-auto': 1.0.0-next.80
|
||||||
'@sveltejs/adapter-node': 1.0.0-next.95
|
'@sveltejs/adapter-node': 1.0.0-next.95
|
||||||
'@sveltejs/adapter-vercel': 1.0.0-next.77
|
'@sveltejs/adapter-vercel': 1.0.0-next.77
|
||||||
'@sveltejs/kit': 1.0.0-next.499_svelte@3.50.1+vite@3.1.3
|
'@sveltejs/kit': 1.0.0-next.503_svelte@3.50.1+vite@3.1.3
|
||||||
'@typescript-eslint/eslint-plugin': 5.38.0_wsb62dxj2oqwgas4kadjymcmry
|
'@typescript-eslint/eslint-plugin': 5.38.0_4gkcvl6qsi23tqqawfqgcwtp54
|
||||||
'@typescript-eslint/parser': 5.38.0_irgkl5vooow2ydyo6aokmferha
|
'@typescript-eslint/parser': 5.38.0_7ilbxdl5iguzcjriqqcg2m5cku
|
||||||
base-64: 1.0.0
|
base-64: 1.0.0
|
||||||
browserslist: 4.21.4
|
browserslist: 4.21.4
|
||||||
cssnano: 5.1.13_postcss@8.4.16
|
cssnano: 5.1.13_postcss@8.4.16
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
eslint-plugin-svelte3: 4.0.0_rhh4tpcyg7zwtq6pdnkzw5gxma
|
eslint-plugin-svelte3: 4.0.0_snydkosur25h6rjxszquwacaua
|
||||||
postcss: 8.4.16
|
postcss: 8.4.16
|
||||||
postcss-focus-visible: 7.1.0_postcss@8.4.16
|
postcss-focus-visible: 7.1.0_postcss@8.4.16
|
||||||
postcss-normalize: 10.0.1_yroec54rl3ndwvbunmnefp5nvy
|
postcss-normalize: 10.0.1_yroec54rl3ndwvbunmnefp5nvy
|
||||||
@@ -531,8 +533,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@sveltejs/kit/1.0.0-next.499_svelte@3.50.1+vite@3.1.3:
|
/@sveltejs/kit/1.0.0-next.503_svelte@3.50.1+vite@3.1.3:
|
||||||
resolution: {integrity: sha512-HoNsQ9CjdPLlDZLwcCsJTx5tW8YI3xhXEcJGuFHPLf600QcfeJWmQtE6mQF/SiykFkYR+2fD1GdRzKynJbZDMA==}
|
resolution: {integrity: sha512-QSEHe40qMOYjXirxS57dIa9NU4FntlYh+KYslBzasjMCfSiUkHGaWMJRz8uU+R4BWnThD9SdCo7F/NwDxu5LRQ==}
|
||||||
engines: {node: '>=16.14'}
|
engines: {node: '>=16.14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
@@ -604,8 +606,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/node/18.7.18:
|
/@types/node/18.7.20:
|
||||||
resolution: {integrity: sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==}
|
resolution: {integrity: sha512-adzY4vLLr5Uivmx8+zfSJ5fbdgKxX8UMtjtl+17n0B1q1Nz8JEmE151vefMdpD+1gyh+77weN4qEhej/O7budQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/pug/2.0.6:
|
/@types/pug/2.0.6:
|
||||||
@@ -615,16 +617,16 @@ packages:
|
|||||||
/@types/resolve/1.17.1:
|
/@types/resolve/1.17.1:
|
||||||
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
|
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.7.18
|
'@types/node': 18.7.20
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/sass/1.43.1:
|
/@types/sass/1.43.1:
|
||||||
resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==}
|
resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.7.18
|
'@types/node': 18.7.20
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin/5.38.0_wsb62dxj2oqwgas4kadjymcmry:
|
/@typescript-eslint/eslint-plugin/5.38.0_4gkcvl6qsi23tqqawfqgcwtp54:
|
||||||
resolution: {integrity: sha512-GgHi/GNuUbTOeoJiEANi0oI6fF3gBQc3bGFYj40nnAPCbhrtEDf2rjBmefFadweBmO1Du1YovHeDP2h5JLhtTQ==}
|
resolution: {integrity: sha512-GgHi/GNuUbTOeoJiEANi0oI6fF3gBQc3bGFYj40nnAPCbhrtEDf2rjBmefFadweBmO1Du1YovHeDP2h5JLhtTQ==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -635,12 +637,12 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.38.0_irgkl5vooow2ydyo6aokmferha
|
'@typescript-eslint/parser': 5.38.0_7ilbxdl5iguzcjriqqcg2m5cku
|
||||||
'@typescript-eslint/scope-manager': 5.38.0
|
'@typescript-eslint/scope-manager': 5.38.0
|
||||||
'@typescript-eslint/type-utils': 5.38.0_irgkl5vooow2ydyo6aokmferha
|
'@typescript-eslint/type-utils': 5.38.0_7ilbxdl5iguzcjriqqcg2m5cku
|
||||||
'@typescript-eslint/utils': 5.38.0_irgkl5vooow2ydyo6aokmferha
|
'@typescript-eslint/utils': 5.38.0_7ilbxdl5iguzcjriqqcg2m5cku
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
ignore: 5.2.0
|
ignore: 5.2.0
|
||||||
regexpp: 3.2.0
|
regexpp: 3.2.0
|
||||||
semver: 7.3.7
|
semver: 7.3.7
|
||||||
@@ -650,7 +652,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/parser/5.38.0_irgkl5vooow2ydyo6aokmferha:
|
/@typescript-eslint/parser/5.38.0_7ilbxdl5iguzcjriqqcg2m5cku:
|
||||||
resolution: {integrity: sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==}
|
resolution: {integrity: sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -664,7 +666,7 @@ packages:
|
|||||||
'@typescript-eslint/types': 5.38.0
|
'@typescript-eslint/types': 5.38.0
|
||||||
'@typescript-eslint/typescript-estree': 5.38.0_typescript@4.8.3
|
'@typescript-eslint/typescript-estree': 5.38.0_typescript@4.8.3
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
typescript: 4.8.3
|
typescript: 4.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -678,7 +680,7 @@ packages:
|
|||||||
'@typescript-eslint/visitor-keys': 5.38.0
|
'@typescript-eslint/visitor-keys': 5.38.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/type-utils/5.38.0_irgkl5vooow2ydyo6aokmferha:
|
/@typescript-eslint/type-utils/5.38.0_7ilbxdl5iguzcjriqqcg2m5cku:
|
||||||
resolution: {integrity: sha512-iZq5USgybUcj/lfnbuelJ0j3K9dbs1I3RICAJY9NZZpDgBYXmuUlYQGzftpQA9wC8cKgtS6DASTvF3HrXwwozA==}
|
resolution: {integrity: sha512-iZq5USgybUcj/lfnbuelJ0j3K9dbs1I3RICAJY9NZZpDgBYXmuUlYQGzftpQA9wC8cKgtS6DASTvF3HrXwwozA==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -689,9 +691,9 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 5.38.0_typescript@4.8.3
|
'@typescript-eslint/typescript-estree': 5.38.0_typescript@4.8.3
|
||||||
'@typescript-eslint/utils': 5.38.0_irgkl5vooow2ydyo6aokmferha
|
'@typescript-eslint/utils': 5.38.0_7ilbxdl5iguzcjriqqcg2m5cku
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
tsutils: 3.21.0_typescript@4.8.3
|
tsutils: 3.21.0_typescript@4.8.3
|
||||||
typescript: 4.8.3
|
typescript: 4.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -724,7 +726,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/utils/5.38.0_irgkl5vooow2ydyo6aokmferha:
|
/@typescript-eslint/utils/5.38.0_7ilbxdl5iguzcjriqqcg2m5cku:
|
||||||
resolution: {integrity: sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==}
|
resolution: {integrity: sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -734,9 +736,9 @@ packages:
|
|||||||
'@typescript-eslint/scope-manager': 5.38.0
|
'@typescript-eslint/scope-manager': 5.38.0
|
||||||
'@typescript-eslint/types': 5.38.0
|
'@typescript-eslint/types': 5.38.0
|
||||||
'@typescript-eslint/typescript-estree': 5.38.0_typescript@4.8.3
|
'@typescript-eslint/typescript-estree': 5.38.0_typescript@4.8.3
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
eslint-scope: 5.1.1
|
eslint-scope: 5.1.1
|
||||||
eslint-utils: 3.0.0_eslint@8.23.1
|
eslint-utils: 3.0.0_eslint@8.24.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
@@ -859,7 +861,7 @@ packages:
|
|||||||
postcss: ^8.1.0
|
postcss: ^8.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.21.4
|
browserslist: 4.21.4
|
||||||
caniuse-lite: 1.0.30001410
|
caniuse-lite: 1.0.30001411
|
||||||
fraction.js: 4.2.0
|
fraction.js: 4.2.0
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
@@ -909,8 +911,8 @@ packages:
|
|||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001410
|
caniuse-lite: 1.0.30001411
|
||||||
electron-to-chromium: 1.4.258
|
electron-to-chromium: 1.4.261
|
||||||
node-releases: 2.0.6
|
node-releases: 2.0.6
|
||||||
update-browserslist-db: 1.0.9_browserslist@4.21.4
|
update-browserslist-db: 1.0.9_browserslist@4.21.4
|
||||||
dev: true
|
dev: true
|
||||||
@@ -940,13 +942,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
|
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.21.4
|
browserslist: 4.21.4
|
||||||
caniuse-lite: 1.0.30001410
|
caniuse-lite: 1.0.30001411
|
||||||
lodash.memoize: 4.1.2
|
lodash.memoize: 4.1.2
|
||||||
lodash.uniq: 4.5.0
|
lodash.uniq: 4.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caniuse-lite/1.0.30001410:
|
/caniuse-lite/1.0.30001411:
|
||||||
resolution: {integrity: sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==}
|
resolution: {integrity: sha512-HPnJKESKuhKpHvMY1/ux7J3nG7xG8jRuL4lbyCjDRm0doTNV91tcRk60xrP7Ym9DtJH/yuqntDWBJCqpXB4b7g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/chalk/4.1.2:
|
/chalk/4.1.2:
|
||||||
@@ -1256,8 +1258,8 @@ packages:
|
|||||||
domhandler: 4.3.1
|
domhandler: 4.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/electron-to-chromium/1.4.258:
|
/electron-to-chromium/1.4.261:
|
||||||
resolution: {integrity: sha512-vutF4q0dTUXoAFI7Vbtdwen/BJVwPgj8GRg/SElOodfH7VTX+svUe62A5BG41QRQGk5HsZPB0M++KH1lAlOt0A==}
|
resolution: {integrity: sha512-fVXliNUGJ7XUVJSAasPseBbVgJIeyw5M1xIkgXdTSRjlmCqBbiSTsEdLOCJS31Fc8B7CaloQ/BFAg8By3ODLdg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/embla-carousel/7.0.3:
|
/embla-carousel/7.0.3:
|
||||||
@@ -1496,13 +1498,13 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-svelte3/4.0.0_rhh4tpcyg7zwtq6pdnkzw5gxma:
|
/eslint-plugin-svelte3/4.0.0_snydkosur25h6rjxszquwacaua:
|
||||||
resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==}
|
resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=8.0.0'
|
eslint: '>=8.0.0'
|
||||||
svelte: ^3.2.0
|
svelte: ^3.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
svelte: 3.50.1
|
svelte: 3.50.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -1522,13 +1524,13 @@ packages:
|
|||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-utils/3.0.0_eslint@8.23.1:
|
/eslint-utils/3.0.0_eslint@8.24.0:
|
||||||
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
|
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
|
||||||
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
|
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=5'
|
eslint: '>=5'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.23.1
|
eslint: 8.24.0
|
||||||
eslint-visitor-keys: 2.1.0
|
eslint-visitor-keys: 2.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -1542,8 +1544,8 @@ packages:
|
|||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint/8.23.1:
|
/eslint/8.24.0:
|
||||||
resolution: {integrity: sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==}
|
resolution: {integrity: sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1558,7 +1560,7 @@ packages:
|
|||||||
doctrine: 3.0.0
|
doctrine: 3.0.0
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 7.1.1
|
eslint-scope: 7.1.1
|
||||||
eslint-utils: 3.0.0_eslint@8.23.1
|
eslint-utils: 3.0.0_eslint@8.24.0
|
||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
espree: 9.4.0
|
espree: 9.4.0
|
||||||
esquery: 1.4.0
|
esquery: 1.4.0
|
||||||
@@ -3284,6 +3286,10 @@ packages:
|
|||||||
postcss-selector-parser: 6.0.10
|
postcss-selector-parser: 6.0.10
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/suncalc/1.9.0:
|
||||||
|
resolution: {integrity: sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/supports-color/7.2.0:
|
/supports-color/7.2.0:
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|||||||
@@ -4,16 +4,17 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, onMount } from 'svelte'
|
import { getContext, onMount } from 'svelte'
|
||||||
import { fly } from 'svelte/transition'
|
import { fade, fly as flySvelte } from 'svelte/transition'
|
||||||
import { quartOut } from 'svelte/easing'
|
import { quartOut } from 'svelte/easing'
|
||||||
import { Globe, type Marker } from '$modules/globe2'
|
import { Globe, type Marker } from '$modules/globe2'
|
||||||
import { getRandomItem, debounce } from '$utils/functions'
|
import { getRandomItem, debounce } from '$utils/functions'
|
||||||
|
import reveal from '$animations/reveal'
|
||||||
// Components
|
// Components
|
||||||
import Image from '$components/atoms/Image.svelte'
|
import SplitText from '$components/SplitText.svelte'
|
||||||
|
|
||||||
export let type: string = undefined
|
export let type: string = undefined
|
||||||
export let enableMarkers: boolean = true
|
export let enableMarkers: boolean = true
|
||||||
export let speed: number = 0.003
|
export let speed: number = 0.1
|
||||||
export let pane: boolean = import.meta.env.DEV
|
export let pane: boolean = import.meta.env.DEV
|
||||||
export let width: number = undefined
|
export let width: number = undefined
|
||||||
|
|
||||||
@@ -22,10 +23,7 @@
|
|||||||
let globe: any
|
let globe: any
|
||||||
let observer: IntersectionObserver
|
let observer: IntersectionObserver
|
||||||
let animation: number
|
let animation: number
|
||||||
let popinOpen: boolean = false
|
let hoveredMarker: { name: string, country: string } = null
|
||||||
let clusterLocations: Marker[] = []
|
|
||||||
|
|
||||||
$: globeResolution = innerWidth > 1440 && window.devicePixelRatio > 1 ? '4k' : '2k'
|
|
||||||
|
|
||||||
const { continents, locations }: any = getContext('global')
|
const { continents, locations }: any = getContext('global')
|
||||||
const randomContinent: any = getRandomItem(continents)
|
const randomContinent: any = getRandomItem(continents)
|
||||||
@@ -39,35 +37,34 @@
|
|||||||
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
const globeResolution = innerWidth > 1440 && window.devicePixelRatio > 1 ? 4 : 2
|
||||||
|
|
||||||
globe = new Globe({
|
globe = new Globe({
|
||||||
el: globeEl,
|
el: globeEl,
|
||||||
parent: globeParentEl,
|
parent: globeParentEl,
|
||||||
mapFile: `/images/globe-map-${globeResolution}.png`,
|
mapFile: `/images/globe-map-${globeResolution}k.png`,
|
||||||
|
mapFileDark: `/images/globe-map-dark-${globeResolution}k.png`,
|
||||||
dpr: Math.min(Math.round(window.devicePixelRatio), 2),
|
dpr: Math.min(Math.round(window.devicePixelRatio), 2),
|
||||||
autoRotate: true,
|
autoRotate: true,
|
||||||
speed,
|
speed,
|
||||||
|
sunAngle: 2,
|
||||||
rotationStart: randomContinent.rotation,
|
rotationStart: randomContinent.rotation,
|
||||||
enableMarkers,
|
enableMarkers,
|
||||||
markers,
|
markers,
|
||||||
pane,
|
pane,
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Define cluster locations and position it
|
|
||||||
clusterLocations = locations.filter((loc: any) => loc.country.slug === 'france')
|
|
||||||
|
|
||||||
resize()
|
resize()
|
||||||
|
|
||||||
// Render only if in viewport
|
// Render only if in viewport
|
||||||
observer = new IntersectionObserver(entries => {
|
observer = new IntersectionObserver(([{ isIntersecting }]) => {
|
||||||
entries.forEach(({ isIntersecting }: IntersectionObserverEntry) => {
|
if (isIntersecting) {
|
||||||
if (isIntersecting) {
|
update()
|
||||||
update()
|
console.log('render globe2')
|
||||||
console.log('render globe2')
|
} else {
|
||||||
} else {
|
stop()
|
||||||
stop()
|
console.log('stop globe2')
|
||||||
console.log('stop globe2')
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
}, { threshold: 0 })
|
}, { threshold: 0 })
|
||||||
observer.observe(globeEl)
|
observer.observe(globeEl)
|
||||||
|
|
||||||
@@ -114,55 +111,41 @@
|
|||||||
class:is-cropped={type === 'cropped'}
|
class:is-cropped={type === 'cropped'}
|
||||||
style:--width={width ? `${width}px` : null}
|
style:--width={width ? `${width}px` : null}
|
||||||
>
|
>
|
||||||
<div class="globe__inner">
|
<div class="globe__canvas" bind:this={globeEl}
|
||||||
<div class="globe__canvas" bind:this={globeEl} />
|
class:is-faded={hoveredMarker}
|
||||||
</div>
|
>
|
||||||
|
<ul class="globe__markers">
|
||||||
{#if enableMarkers}
|
|
||||||
<ul class="globe__markers" data-sveltekit-noscroll>
|
|
||||||
{#each markers as { name, slug, country, lat, lng }}
|
{#each markers as { name, slug, country, lat, lng }}
|
||||||
<li class="globe__marker" data-location={slug} data-lat={lat} data-lng={lng}>
|
<li class="globe__marker" data-location={slug} data-lat={lat} data-lng={lng}>
|
||||||
<a href="/{country.slug}/{slug}">
|
<a href="/{country.slug}/{slug}" data-sveltekit-noscroll
|
||||||
<dl>
|
on:mouseenter={() => hoveredMarker = { name, country: country.name }}
|
||||||
<dt class="title-small">{name}</dt>
|
on:mouseleave={() => hoveredMarker = null}
|
||||||
<dd class="text-label text-label--small">{country.name}</dd>
|
>
|
||||||
</dl>
|
<i />
|
||||||
|
<span>{name}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<li class="globe__cluster">
|
|
||||||
<button on:click={() => popinOpen = !popinOpen} aria-label="{popinOpen ? 'Close' : 'Open'} cluster" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if popinOpen}
|
{#if hoveredMarker}
|
||||||
<div class="globe__popin" transition:fly={{ y: 16, duration: 500, easing: quartOut }}>
|
<div class="globe__location"
|
||||||
<ul data-sveltekit-noscroll>
|
transition:fade={{ duration: 300, easing: quartOut }}
|
||||||
{#each clusterLocations as { name, slug, country }}
|
use:reveal={{
|
||||||
<li>
|
children: '.char',
|
||||||
<a href="/{country.slug}/{slug}" tabindex="0">
|
animation: { y: ['110%', 0] },
|
||||||
<Image
|
options: {
|
||||||
class="flag"
|
stagger: 0.04,
|
||||||
id={country.flag.id}
|
duration: 1,
|
||||||
sizeKey="square-small"
|
threshold: 0,
|
||||||
width={32} height={32}
|
},
|
||||||
alt="Flag of {country.name}"
|
}}
|
||||||
/>
|
>
|
||||||
<dl>
|
<SplitText text={hoveredMarker.name} mode="chars" class="name" />
|
||||||
<dt class="title-small">{name}</dt>
|
<p class="country" in:flySvelte={{ y: 16, duration: 800, easing: quartOut, delay: 900 }}>
|
||||||
<dd class="text-label text-label--small">{country.name}</dd>
|
{hoveredMarker.country}
|
||||||
</dl>
|
</p>
|
||||||
</a>
|
</div>
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
<button class="close" aria-label="Close" on:click={() => popinOpen = false}>
|
|
||||||
<svg width="9" height="9">
|
|
||||||
<use xlink:href="#cross" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,37 +1,20 @@
|
|||||||
precision highp float;
|
precision highp float;
|
||||||
varying vec3 v_normal;
|
|
||||||
varying vec3 v_surfaceToLight;
|
varying vec3 vNormal;
|
||||||
varying vec3 v_surfaceToView;
|
|
||||||
varying vec2 v_uv;
|
|
||||||
uniform float u_dt;
|
|
||||||
uniform float u_shininess;
|
|
||||||
uniform sampler2D map;
|
uniform sampler2D map;
|
||||||
|
uniform sampler2D mapDark;
|
||||||
|
varying vec2 vUv;
|
||||||
|
varying vec3 vSunDir;
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Re-normalize interpolated varyings
|
float cosineAngleSunToNormal = dot(normalize(vNormal), normalize(vSunDir));
|
||||||
vec3 normal = normalize(v_normal);
|
cosineAngleSunToNormal = clamp(cosineAngleSunToNormal * 1.0, -1.0, 1.0);
|
||||||
vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
|
|
||||||
vec3 surfaceToViewDirection = normalize(v_surfaceToView);
|
|
||||||
// Calculate Half-Vector, Vector that bisects the angle of reflection.
|
|
||||||
// This vector indecates the "brightest point" A "refrence vector" if you will.
|
|
||||||
vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);
|
|
||||||
// Then we can get the brightness at any point by seeing "how similar" the surface normal is to the refrence vector.
|
|
||||||
float light = dot(normal, surfaceToLightDirection);
|
|
||||||
|
|
||||||
// By raising the specular vector to a power we can control the intensity of the light
|
float mixAmount = cosineAngleSunToNormal * 0.666 + 0.333;
|
||||||
float specular = 0.0;
|
vec3 dayColor = texture2D(map, vUv).rgb;
|
||||||
|
vec3 nightColor = texture2D(mapDark, vUv).rgb;
|
||||||
|
vec3 color = mix(nightColor, dayColor, mixAmount);
|
||||||
|
|
||||||
if (light > 0.0) {
|
gl_FragColor = vec4(color, 1.0);
|
||||||
specular = pow(dot(normal, halfVector), u_shininess * 100.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping textures
|
|
||||||
vec4 map = texture2D(map, v_uv).rgba;
|
|
||||||
// vec3 spec = texture2D(specMap, v_uv).rgb;
|
|
||||||
|
|
||||||
gl_FragColor.rgba = map;
|
|
||||||
// Add Point Lighting
|
|
||||||
gl_FragColor.rgba *= light;
|
|
||||||
// Add Specular Highlights
|
|
||||||
// gl_FragColor.rgb += specular * spec;
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { Renderer, Camera, Vec3, Orbit, Sphere, Transform, Program, Mesh, Texture } from 'ogl'
|
import { Renderer, Camera, Vec3, Orbit, Sphere, Transform, Program, Mesh, Texture } from 'ogl'
|
||||||
|
import SunCalc from 'suncalc'
|
||||||
|
import { map } from '$utils/functions/index'
|
||||||
// Shaders
|
// Shaders
|
||||||
import VERTEX_SHADER from '../../modules/globe2/vertex.glsl?raw'
|
import VERTEX_SHADER from '$modules/globe2/vertex.glsl?raw'
|
||||||
import FRAGMENT_SHADER from '../../modules/globe2/frag.glsl?raw'
|
import FRAGMENT_SHADER from '$modules/globe2/frag.glsl?raw'
|
||||||
|
|
||||||
|
|
||||||
export class Globe {
|
export class Globe {
|
||||||
@@ -14,17 +16,47 @@ export class Globe {
|
|||||||
this.width = this.el.offsetWidth
|
this.width = this.el.offsetWidth
|
||||||
this.height = this.el.offsetHeight
|
this.height = this.el.offsetHeight
|
||||||
this.markers = options.markers || []
|
this.markers = options.markers || []
|
||||||
|
this.globeRotation = {
|
||||||
|
lat: degToRad(-this.options.rotationStart.lat) || 0,
|
||||||
|
lng: degToRad(-this.options.rotationStart.lng) || 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the current sun position from a given location
|
||||||
|
const locations = [
|
||||||
|
{
|
||||||
|
lat: -37.840935,
|
||||||
|
lng: 144.946457,
|
||||||
|
tz: 'Australia/Melbourne',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.856614,
|
||||||
|
lng: 2.3522219,
|
||||||
|
tz: 'Europe/Paris',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const location = locations[1]
|
||||||
|
const localDate = new Date(now.toLocaleString('en-US', { timeZone: location.tz }))
|
||||||
|
|
||||||
|
this.sunPosition = SunCalc.getPosition(localDate, location.lat, location.lng)
|
||||||
|
|
||||||
|
var times = SunCalc.getTimes(new Date(), location.lat, location.lng);
|
||||||
|
var sunrisePos = SunCalc.getPosition(times.sunrise, location.lat, location.lng);
|
||||||
|
this.sunriseAzimuth = sunrisePos.azimuth * 180 / Math.PI;
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
this.params = {
|
this.params = {
|
||||||
autoRotate: options.autoRotate,
|
autoRotate: options.autoRotate,
|
||||||
speed: options.speed,
|
speed: options.speed,
|
||||||
enableMarkers: options.enableMarkers,
|
enableMarkers: options.enableMarkers,
|
||||||
zoom: 1.305,
|
zoom: 1.3075,
|
||||||
|
sunAngle: options.sunAngle || 0,
|
||||||
|
sunAngleDelta: 1.8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
this.hoveringMarker = false
|
this.hoveringMarker = false
|
||||||
|
this.hoveringMarkerTimeout = 0
|
||||||
|
this.lastFrame = now()
|
||||||
this.dragging = false
|
this.dragging = false
|
||||||
this.webgl = WebGLSupport() !== null
|
this.webgl = WebGLSupport() !== null
|
||||||
this.pane = undefined
|
this.pane = undefined
|
||||||
@@ -33,7 +65,6 @@ export class Globe {
|
|||||||
if (this.webgl) {
|
if (this.webgl) {
|
||||||
this.build()
|
this.build()
|
||||||
this.resize()
|
this.resize()
|
||||||
this.render()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add GUI panel if activated
|
// Add GUI panel if activated
|
||||||
@@ -60,19 +91,16 @@ export class Globe {
|
|||||||
|
|
||||||
// Create camera
|
// Create camera
|
||||||
this.camera = new Camera(this.gl)
|
this.camera = new Camera(this.gl)
|
||||||
// TODO: Why 1.315? Is there a way to calculate this number?
|
|
||||||
this.camera.position.set(0, 0, this.params.zoom)
|
this.camera.position.set(0, 0, this.params.zoom)
|
||||||
|
|
||||||
// Create controls
|
// Create controls
|
||||||
this.controls = new Orbit(this.camera, {
|
this.controls = new Orbit(this.camera, {
|
||||||
element: this.el,
|
element: this.el,
|
||||||
target: new Vec3(0,0,0),
|
|
||||||
enableZoom: false,
|
enableZoom: false,
|
||||||
enablePan: false,
|
enablePan: false,
|
||||||
autoRotate: false,
|
|
||||||
ease: 0.2,
|
ease: 0.2,
|
||||||
minPolarAngle: Math.PI / 4,
|
minPolarAngle: Math.PI / 4,
|
||||||
maxPolarAngle: Math.PI / 1.5,
|
maxPolarAngle: Math.PI / 1.85,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Append canvas to scene
|
// Append canvas to scene
|
||||||
@@ -81,51 +109,54 @@ export class Globe {
|
|||||||
// Create scene and geometry
|
// Create scene and geometry
|
||||||
this.scene = new Transform()
|
this.scene = new Transform()
|
||||||
this.geometry = new Sphere(this.gl, {
|
this.geometry = new Sphere(this.gl, {
|
||||||
widthSegments: 64,
|
widthSegments: 75,
|
||||||
heightSegments: 64,
|
heightSegments: 75,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create light
|
|
||||||
// TODO: How to create a nicer light that doesn't fade to 0? Just creating a "dark" area where you still can read markers and see countries/continents
|
|
||||||
// this.light = new Vec3(0, 50, 150)
|
|
||||||
this.light = new Vec3(0, 0, 15)
|
|
||||||
|
|
||||||
// Add map texture
|
// Add map texture
|
||||||
const map = new Texture(this.gl)
|
const mapWorld = new Texture(this.gl)
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.onload = () => (map.image = img)
|
img.onload = () => (mapWorld.image = img)
|
||||||
img.src = this.options.mapFile
|
img.src = this.options.mapFile
|
||||||
|
|
||||||
|
// Dark map texture
|
||||||
|
const mapDark = new Texture(this.gl)
|
||||||
|
const imgDark = new Image()
|
||||||
|
imgDark.onload = () => (mapDark.image = imgDark)
|
||||||
|
imgDark.src = this.options.mapFileDark
|
||||||
|
|
||||||
|
const azimuthValue = map(this.sunriseAzimuth, -180, 180, -Math.PI, Math.PI);
|
||||||
|
|
||||||
// Create program
|
// Create program
|
||||||
this.program = new Program(this.gl, {
|
const program = new Program(this.gl, {
|
||||||
vertex: VERTEX_SHADER,
|
vertex: VERTEX_SHADER,
|
||||||
fragment: FRAGMENT_SHADER,
|
fragment: FRAGMENT_SHADER,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
u_dt: { value: 0 },
|
u_dt: { value: 0 },
|
||||||
u_lightWorldPosition: { value: this.light }, // Position of the Light
|
map: { value: mapWorld }, // Map Texture
|
||||||
u_shininess: { value: 1.0 },
|
mapDark: { value: mapDark }, // Map Dark Texture
|
||||||
map: { value: map }, // Color Map
|
altitude: { value: 0 },
|
||||||
|
azimuth: { value: 0 },
|
||||||
},
|
},
|
||||||
transparent: true,
|
cullFace: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create mesh
|
// Create light
|
||||||
this.mesh = new Mesh(this.gl, {
|
program.uniforms.altitude.value = this.sunPosition.altitude
|
||||||
|
program.uniforms.azimuth.value = this.sunPosition.azimuth
|
||||||
|
|
||||||
|
// Create globe mesh
|
||||||
|
this.globe = new Mesh(this.gl, {
|
||||||
geometry: this.geometry,
|
geometry: this.geometry,
|
||||||
program: this.program,
|
program,
|
||||||
})
|
})
|
||||||
this.mesh.setParent(this.scene)
|
this.globe.setParent(this.scene)
|
||||||
|
|
||||||
// Start globe angle with a random continent's position
|
|
||||||
if (this.options.rotationStart) {
|
|
||||||
this.mesh.rotation.y = degToRad(this.options.rotationStart * -1) || 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add events
|
// Add events
|
||||||
this.addEvents()
|
this.addEvents()
|
||||||
|
|
||||||
// Setup markers
|
// Setup markers
|
||||||
if (this.enableMarkers && this.markers) {
|
if (this.markers) {
|
||||||
this.setupMarkers()
|
this.setupMarkers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,45 +196,57 @@ export class Globe {
|
|||||||
this.markers.forEach((marker: Marker) => {
|
this.markers.forEach((marker: Marker) => {
|
||||||
const markerEl = this.getMarker(marker.slug)
|
const markerEl = this.getMarker(marker.slug)
|
||||||
|
|
||||||
// Define position
|
// Update marker position
|
||||||
const position = lonlatVec3(marker.lng, marker.lat)
|
this.updateMarkerPosition(marker, markerEl)
|
||||||
|
|
||||||
// Scale marker position to fit globe size
|
|
||||||
marker.position = [position[0] *= 0.5, position[1] *= 0.5, position[2] *= 0.5]
|
|
||||||
|
|
||||||
// Position marker
|
|
||||||
const posX = (marker.position[0] + 1) * (this.width / this.params.zoom)
|
|
||||||
const posY = (1 - marker.position[1]) * (this.height / this.params.zoom)
|
|
||||||
markerEl.style.transform = `translate3d(${posX}px, ${posY}px, 0)`
|
|
||||||
|
|
||||||
// Entering marker
|
// Entering marker
|
||||||
markerEl.addEventListener('mouseenter', () => {
|
markerEl.addEventListener('mouseenter', () => {
|
||||||
this.hoveringMarker = true
|
this.hoveringMarker = true
|
||||||
}, false)
|
clearTimeout(this.hoveringMarkerTimeout)
|
||||||
// Leaving marker
|
|
||||||
markerEl.addEventListener('mouseleave', () => {
|
|
||||||
this.hoveringMarker = false
|
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
// console.log(marker)
|
// Leaving marker
|
||||||
|
markerEl.addEventListener('mouseleave', () => {
|
||||||
|
this.hoveringMarkerTimeout = setTimeout(() => {
|
||||||
|
this.hoveringMarker = false
|
||||||
|
}, 300)
|
||||||
|
}, false)
|
||||||
|
|
||||||
return marker
|
return marker
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update marker position
|
||||||
|
updateMarkerPosition (marker: Marker, markerEl: HTMLElement) {
|
||||||
|
const position = latLonToVec3(marker.lat, marker.lng)
|
||||||
|
const screenVector = new Vec3(position.x, position.y, position.z)
|
||||||
|
screenVector.applyMatrix4(this.globe.worldMatrix)
|
||||||
|
this.camera.project(screenVector)
|
||||||
|
|
||||||
|
// Position marker
|
||||||
|
const posX = ((screenVector[0] + 1) / 2) * this.width
|
||||||
|
const posY = (1. - (screenVector[1] + 1) / 2) * this.height
|
||||||
|
markerEl.style.transform = `translate3d(${posX}px, ${posY}px, 0)`
|
||||||
|
|
||||||
|
// Hide marker if behind globe
|
||||||
|
markerEl.classList.toggle('is-hidden', screenVector[2] > 0.82)
|
||||||
|
}
|
||||||
|
|
||||||
// Update markers
|
// Update markers
|
||||||
updateMarkers () {
|
updateMarkers () {
|
||||||
this.markers.forEach((marker: Marker) => {
|
this.markers.forEach((marker: Marker) => {
|
||||||
// const markerEl = this.getMarker(marker.slug)
|
const markerEl = this.getMarker(marker.slug)
|
||||||
// const screenVector = new Vec3(0,0,0)
|
|
||||||
// screenVector.copy(marker.position)
|
|
||||||
// this.camera.project(screenVector)
|
|
||||||
|
|
||||||
|
// Update marker position
|
||||||
|
this.updateMarkerPosition(marker, markerEl)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// let posX = (screenVector.x + 1) * (this.options.width / this.params.zoom)
|
// Disable markers
|
||||||
// // // posX /= this.mesh.rotation.y
|
hideMarkers () {
|
||||||
// let posY = (1 - screenVector.y) * (this.options.height / this.params.zoom)
|
this.markers.forEach((marker: Marker) => {
|
||||||
// markerEl.style.transform = `translate3d(${posX}px, ${posY}px, 0)`
|
const markerEl = this.getMarker(marker.slug)
|
||||||
|
markerEl.classList.add('is-hidden')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +255,6 @@ export class Globe {
|
|||||||
* Resize method
|
* Resize method
|
||||||
*/
|
*/
|
||||||
resize () {
|
resize () {
|
||||||
// this.renderer.setSize(window.innerWidth, window.innerHeight)
|
|
||||||
this.width = this.el.offsetWidth
|
this.width = this.el.offsetWidth
|
||||||
this.height = this.el.offsetHeight
|
this.height = this.el.offsetHeight
|
||||||
this.renderer.setSize(this.width, this.height)
|
this.renderer.setSize(this.width, this.height)
|
||||||
@@ -226,27 +268,28 @@ export class Globe {
|
|||||||
* Update method
|
* Update method
|
||||||
*/
|
*/
|
||||||
render () {
|
render () {
|
||||||
// Stop render if not dragging but hovering marker
|
const delta = (now() - this.lastFrame) / 1000
|
||||||
if (!this.dragging && this.hoveringMarker) return
|
this.lastFrame = now()
|
||||||
|
|
||||||
// Update globe rotation
|
// Rotate globe if not dragging neither hovering marker
|
||||||
if (this.params.autoRotate) {
|
if (this.params.autoRotate && !this.hoveringMarker) {
|
||||||
this.mesh.rotation.y += this.params.speed
|
this.globeRotation.lat += this.params.speed * delta
|
||||||
|
this.globe.rotation.y = this.globeRotation.lat
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update controls and renderer
|
// Update controls and renderer
|
||||||
this.controls.update(this.params)
|
this.controls.update()
|
||||||
this.renderer.render({
|
this.renderer.render({
|
||||||
scene: this.scene,
|
scene: this.scene,
|
||||||
camera: this.camera,
|
camera: this.camera,
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Update light
|
|
||||||
// this.light.set(this.camera.position)
|
|
||||||
// this.program.uniforms.u_lightWorldPosition.value = [this.mesh.rotation.y * 1, 50, 150]
|
|
||||||
|
|
||||||
// Update markers
|
// Update markers
|
||||||
this.updateMarkers()
|
if (this.params.enableMarkers) {
|
||||||
|
this.updateMarkers()
|
||||||
|
} else {
|
||||||
|
this.hideMarkers()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -259,7 +302,7 @@ export class Globe {
|
|||||||
this.gl = null
|
this.gl = null
|
||||||
this.scene = null
|
this.scene = null
|
||||||
this.camera = null
|
this.camera = null
|
||||||
this.mesh = null
|
this.globe = null
|
||||||
this.renderer = null
|
this.renderer = null
|
||||||
this.controls.remove()
|
this.controls.remove()
|
||||||
|
|
||||||
@@ -277,9 +320,11 @@ type Options = {
|
|||||||
el: HTMLElement
|
el: HTMLElement
|
||||||
parent: HTMLElement
|
parent: HTMLElement
|
||||||
mapFile: string
|
mapFile: string
|
||||||
|
mapFileDark: string
|
||||||
dpr: number
|
dpr: number
|
||||||
autoRotate: boolean
|
autoRotate: boolean
|
||||||
speed: number
|
speed: number
|
||||||
|
sunAngle: number
|
||||||
rotationStart?: number
|
rotationStart?: number
|
||||||
enableMarkers?: boolean
|
enableMarkers?: boolean
|
||||||
markers?: any[]
|
markers?: any[]
|
||||||
@@ -297,7 +342,6 @@ export type Marker = {
|
|||||||
}
|
}
|
||||||
lat: number
|
lat: number
|
||||||
lng: number
|
lng: number
|
||||||
position?: number[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -319,17 +363,24 @@ function WebGLSupport () {
|
|||||||
/**
|
/**
|
||||||
* Convert lat/lng to Vec3
|
* Convert lat/lng to Vec3
|
||||||
*/
|
*/
|
||||||
function lonlatVec3 (longitude: number, latitude: number) {
|
const latLonToVec3 = (lat: number, lng: number) => {
|
||||||
const lat = latitude * Math.PI / 180
|
const phi = (90 - lat) * (Math.PI / 180)
|
||||||
const lng = -longitude * Math.PI / 180
|
const theta = (lng + 180) * (Math.PI / 180)
|
||||||
return new Vec3(
|
|
||||||
Math.cos(lat) * Math.cos(lng),
|
const x = -((0.5) * Math.sin(phi) * Math.cos(theta))
|
||||||
Math.sin(lat),
|
const z = ((0.5) * Math.sin(phi) * Math.sin(theta))
|
||||||
Math.cos(lat) * Math.sin(lng)
|
const y = ((0.5) * Math.cos(phi))
|
||||||
)
|
|
||||||
|
return new Vec3(x,y,z)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Degrees to Radians
|
* Convert Degrees to Radians
|
||||||
*/
|
*/
|
||||||
const degToRad = (deg: number) => deg * Math.PI / 180
|
const degToRad = (deg: number) => deg * Math.PI / 180
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current timestamp (performance or Date)
|
||||||
|
*/
|
||||||
|
const now = () => (typeof performance === 'undefined' ? Date : performance).now()
|
||||||
@@ -3,19 +3,50 @@ import { Pane } from 'tweakpane'
|
|||||||
export const createPane = (ctx: any) => {
|
export const createPane = (ctx: any) => {
|
||||||
ctx.pane = new Pane({
|
ctx.pane = new Pane({
|
||||||
container: ctx.parent,
|
container: ctx.parent,
|
||||||
title: 'Settings',
|
title: 'Globe Settings',
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.pane.addInput(ctx.params, 'autoRotate', {
|
|
||||||
|
/**
|
||||||
|
* Rotation
|
||||||
|
*/
|
||||||
|
const rotation = ctx.pane.addFolder({
|
||||||
|
title: 'Rotation',
|
||||||
|
})
|
||||||
|
rotation.addInput(ctx.params, 'autoRotate', {
|
||||||
label: 'Auto-rotate',
|
label: 'Auto-rotate',
|
||||||
})
|
})
|
||||||
ctx.pane.addInput(ctx.params, 'speed', {
|
rotation.addInput(ctx.params, 'speed', {
|
||||||
label: 'Rotation speed',
|
label: 'Rotation speed',
|
||||||
min: 0.0005,
|
min: 0.01,
|
||||||
max: 0.025,
|
max: 2,
|
||||||
step: 0.00025,
|
step: 0.05,
|
||||||
})
|
})
|
||||||
ctx.pane.addInput(ctx.params, 'enableMarkers', {
|
|
||||||
label: 'Enable markers',
|
|
||||||
|
/**
|
||||||
|
* Markers
|
||||||
|
*/
|
||||||
|
if (ctx.markers && ctx.markers.length > 0) {
|
||||||
|
const markers = ctx.pane.addFolder({
|
||||||
|
title: 'Markers',
|
||||||
|
})
|
||||||
|
markers.addInput(ctx.params, 'enableMarkers', {
|
||||||
|
label: 'Enable markers',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Others
|
||||||
|
*/
|
||||||
|
const misc = ctx.pane.addFolder({
|
||||||
|
title: 'Misc',
|
||||||
|
})
|
||||||
|
// Sun position
|
||||||
|
misc.addInput(ctx.params, 'sunAngleDelta', {
|
||||||
|
label: 'Sun angle delta',
|
||||||
|
min: 0,
|
||||||
|
max: 2 * Math.PI,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,29 @@
|
|||||||
|
varying vec3 vNormal;
|
||||||
|
|
||||||
attribute vec2 uv;
|
attribute vec2 uv;
|
||||||
attribute vec3 position;
|
attribute vec3 position;
|
||||||
attribute vec3 normal;
|
attribute vec3 normal;
|
||||||
uniform mat4 modelMatrix;
|
|
||||||
uniform mat4 modelViewMatrix;
|
uniform mat4 modelViewMatrix;
|
||||||
uniform mat4 projectionMatrix;
|
uniform mat4 projectionMatrix;
|
||||||
uniform mat3 normalMatrix;
|
uniform mat3 normalMatrix;
|
||||||
uniform vec3 u_lightWorldPosition;
|
uniform float azimuth;
|
||||||
uniform vec3 cameraPosition;
|
uniform float altitude;
|
||||||
|
varying vec2 vUv;
|
||||||
|
varying vec3 vSunDir;
|
||||||
|
|
||||||
varying vec3 v_normal;
|
|
||||||
varying vec3 v_surfaceToLight;
|
|
||||||
varying vec3 v_surfaceToView;
|
|
||||||
varying vec2 v_uv;
|
|
||||||
|
|
||||||
void main () {
|
void main() {
|
||||||
// Pass UV information to Fragment Shader
|
vUv = uv;
|
||||||
v_uv = uv;
|
// float px = sin(rotation) * 1.0;
|
||||||
|
// float pz = cos(rotation) * 1.0;
|
||||||
|
float px = sin(0.0) * 1.0;
|
||||||
|
float pz = cos(0.0) * 1.0;
|
||||||
|
vec3 uLightPos = vec3(px, 0.0, pz);
|
||||||
|
|
||||||
// Calculate World Space Normal
|
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
||||||
v_normal = normalMatrix * normal;
|
|
||||||
|
|
||||||
// Compute the world position of the surface
|
vNormal = normalMatrix * normal;
|
||||||
vec3 surfaceWorldPosition = mat3(modelMatrix) * position;
|
vSunDir = mat3(normalMatrix) * uLightPos;
|
||||||
|
|
||||||
// Vector from the surface, to the light
|
gl_Position = projectionMatrix * mvPosition;
|
||||||
v_surfaceToLight = u_lightWorldPosition - surfaceWorldPosition;
|
|
||||||
|
|
||||||
// Vector from the surface, to the camera
|
|
||||||
v_surfaceToView = cameraPosition - surfaceWorldPosition;
|
|
||||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
||||||
}
|
}
|
||||||
@@ -4,46 +4,93 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
// Inner
|
// Canvas
|
||||||
&__inner {
|
&__canvas {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: var(--width);
|
z-index: 2;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%) translateZ(0);
|
||||||
|
width: var(--width);
|
||||||
|
|
||||||
|
// Responsive square padding
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
padding-bottom: 100%;
|
padding-bottom: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overlay
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 21;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
border-radius: 100%;
|
||||||
|
background: $color-primary;
|
||||||
|
transition: opacity 1.5s var(--ease-quart);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(canvas) {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Is faded under name
|
||||||
|
&:global(.is-faded:before) {
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Canvas
|
|
||||||
&__canvas {
|
// Location name
|
||||||
|
&__location {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 30;
|
||||||
top: 0;
|
top: 50%;
|
||||||
left: 50%;
|
|
||||||
transform: translate3d(-50%, 0, 0);
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
:global(canvas) {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
overflow: hidden;
|
||||||
cursor: grab;
|
transform: translateY(-50%) translateZ(0);
|
||||||
|
pointer-events: none;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
:global(.char) {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
:global(.name) {
|
||||||
|
font-family: $font-serif;
|
||||||
|
font-weight: 100;
|
||||||
|
letter-spacing: -0.035em;
|
||||||
|
color: $color-secondary;
|
||||||
|
font-size: clamp(#{rem(88px)}, 20vw, #{rem(320px)});
|
||||||
|
}
|
||||||
|
.country {
|
||||||
|
display: block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: rem(14px);
|
||||||
|
color: $color-tertiary;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Markers
|
// Markers
|
||||||
&__markers {
|
&__markers {
|
||||||
position: absolute;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 20;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
@@ -61,178 +108,51 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transform: translate3d(var(--x), var(--y), 0);
|
transform: translate3d(var(--x), var(--y), 0);
|
||||||
|
transition: opacity 0.4s var(--ease-quart);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
top: -10px;
|
||||||
|
left: -10px;
|
||||||
|
display: block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $color-secondary;
|
color: $color-secondary;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
|
||||||
dl > * {
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
}
|
|
||||||
dt {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
color: $color-gray;
|
|
||||||
margin-top: 4px;
|
|
||||||
line-height: 1;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dot
|
// Dot
|
||||||
&:before {
|
i {
|
||||||
content: "";
|
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
width: 10px;
|
||||||
top: 10px;
|
height: 10px;
|
||||||
left: -16px;
|
border-radius: 32px;
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 100%;
|
|
||||||
background: $color-secondary;
|
background: $color-secondary;
|
||||||
|
transition: box-shadow 0.4s var(--ease-quart), transform 0.4s var(--ease-quart);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
}
|
}
|
||||||
}
|
// Name
|
||||||
|
span {
|
||||||
/*
|
display: none;
|
||||||
** States
|
|
||||||
*/
|
|
||||||
// Has name
|
|
||||||
&:global(.is-dot-only) {
|
|
||||||
dt, dd {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Has country
|
|
||||||
&:global(.has-country) {
|
|
||||||
dd {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cluster
|
|
||||||
&__cluster {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
top: 300px;
|
|
||||||
left: 300px;
|
|
||||||
pointer-events: auto;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
border-radius: 100%;
|
|
||||||
background: rgba($color-secondary, 0.2);
|
|
||||||
transition: box-shadow 0.5s var(--ease-quart), background 0.5s var(--ease-quart);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
button {
|
|
||||||
background: rgba($color-secondary, 0.3);
|
|
||||||
box-shadow: 0 0 0 8px rgba($color-secondary, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Popin
|
|
||||||
&__popin {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
top: 12vw;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate3d(-50%, 0, 0);
|
|
||||||
pointer-events: auto;
|
|
||||||
width: 546px;
|
|
||||||
padding: 24px 32px;
|
|
||||||
border-radius: 16px;
|
|
||||||
background: #fff;
|
|
||||||
--shadow-color: #{rgba(45, 4, 88, 0.05)};
|
|
||||||
box-shadow:
|
|
||||||
0 6px 6px var(--shadow-color),
|
|
||||||
0 12px 12px var(--shadow-color),
|
|
||||||
0 24px 24px var(--shadow-color),
|
|
||||||
0 40px 40px var(--shadow-color);
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 8px 16px;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: block;
|
|
||||||
transform: translateZ(0);
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: background 0.3s var(--ease-quart);
|
|
||||||
|
|
||||||
|
// Hover: Grow marker outline
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba($color-secondary, 0.1);
|
i {
|
||||||
|
box-shadow: 0 0 0 10px rgba($color-tertiary, 0.25);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag
|
// State: Is hidden
|
||||||
:global(.flag) {
|
&:global(.is-hidden) {
|
||||||
display: block;
|
opacity: 0;
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 100%;
|
|
||||||
transform: translateZ(0);
|
|
||||||
|
|
||||||
:global(img) {
|
i {
|
||||||
display: block;
|
transform: scale(0) translateZ(0);
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Details
|
|
||||||
dl {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
dt {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
color: $color-gray;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close buttom
|
|
||||||
.close {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
top: 12px;
|
|
||||||
right: 12px;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: $color-primary-darker;
|
|
||||||
background: rgba($color-secondary, 0.15);
|
|
||||||
border-radius: 100%;
|
|
||||||
transition: background 0.3s var(--ease-quart);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba($color-secondary, 0.3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 84 KiB |
BIN
static/images/globe-map-dark-2k.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/globe-map-dark-4k.png
Normal file
|
After Width: | Height: | Size: 84 KiB |