Skip to content

Instantly share code, notes, and snippets.

@stanwmusic
Created June 15, 2024 18:19
Show Gist options
  • Save stanwmusic/61db7fed72c5b88880d2ec3f5b088902 to your computer and use it in GitHub Desktop.
Save stanwmusic/61db7fed72c5b88880d2ec3f5b088902 to your computer and use it in GitHub Desktop.
Around the 🌎 in 885 seconds
<div class="container">
<canvas id="c"></canvas>
<div class="info" id="info"></div>
</div>
const { sqrt, pow } = Math;
const animation = {
rotation: 0,
display: 0,
isRotate: true
};
const tilt = 0;
let i = 0;
let { innerHeight: height, innerWidth: width } = window;
let currentCountry = 0;
let currentCountryName = "";
let p1, r1, ip, iv, minZoom;
const canvas = document.getElementById("c");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
const data = mapData.features;
const shuffle = (a) => {
const temp = a.slice(0);
for (let i = temp.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[temp[i], temp[j]] = [temp[j], temp[i]];
}
return temp;
};
const map = (value, sMin, sMax, dMin, dMax) => {
return dMin + ((value - sMin) / (sMax - sMin)) * (dMax - dMin);
};
const countriesShuffled = shuffle(countries);
console.log(countriesShuffled.length * 5);
const projection = d3
.geoOrthographic()
.scale(sqrt(width) * 15, sqrt(width) * 15)
.translate([width / 2, height / 2]);
const pathGenerator = d3.geoPath(projection, ctx);
const createMarker = ([long, lat], r) => {
const [x, y] = projection([long, lat]);
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fillStyle = `rgba(97, 177, 90,${r / 10})`;
ctx.fill();
};
const animate = (arc, t, isDisplay) => {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
pathGenerator({ type: "Sphere" });
ctx.strokeStyle = "yellow";
ctx.fillStyle = "#b5d0d0";
ctx.fill();
let matchedCountry = null;
data.forEach((item, index) => {
if (item.properties.name === currentCountryName) {
matchedCountry = item;
}
ctx.beginPath();
pathGenerator(item);
ctx.fillStyle = `#f2efe9`;
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "#d09ccb";
ctx.stroke();
});
if (matchedCountry && isDisplay) {
ctx.beginPath();
pathGenerator(matchedCountry);
ctx.fillStyle = `rgba(190, 219, 187, ${t})`;
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "#da9ff9";
ctx.stroke();
}
if (arc) {
ctx.beginPath();
pathGenerator({ type: "LineString", coordinates: arc });
ctx.lineWidth = 6;
ctx.lineCap = "round";
ctx.strokeStyle = "#61b15a66";
ctx.stroke();
}
};
const getRotation = (p) => [-p[0], tilt - p[1], 0];
let p2 = countriesShuffled[0][1];
let r2 = getRotation(p2);
const getDist = (p1, p2) => {
const [x1, y1] = p1;
const [x2, y2] = p2;
const dist = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
return dist;
};
const updateCountry = () => {
currentCountry = (currentCountry + 1) % countriesShuffled.length;
const country = countriesShuffled[currentCountry];
p1 = p2;
r1 = r2;
p2 = country[1];
currentCountryName = country[0];
document.getElementById("info").innerHTML = currentCountryName;
r2 = getRotation(p2);
ip = d3.geoInterpolate(p1, p2);
iv = Versor.interpolateAngles(r1, r2);
minZoom = map(getDist(p1, p2), 0, 200, sqrt(width) * 30, sqrt(width) * 15);
};
updateCountry();
const tl = gsap.timeline({
repeat: -1,
onRepeat: () => {
updateCountry();
}
});
tl.to(animation, {
duration: 2,
rotation: 1,
isRotate: false,
ease: "sine.inOut"
});
tl.to(animation, { duration: 1, display: 1 });
tl.to(".info", { duration: 1, opacity: 1, y: 0 }, 2.5);
tl.to(".info", { duration: 1, opacity: 0, y: 100 });
const animateGlobe = () => {
if (animation.isRotate) {
const t = animation.rotation;
const s = map(Math.abs(t - 0.5), 0, 0.5, minZoom, sqrt(width) * 30);
projection.rotate(iv(t)).scale(s, s);
animate([p1, ip(t)], t, false);
createMarker(p1, (1 - t) * 10);
} else {
const t = animation.display;
animate([p2, ip(t)], t, true);
createMarker(p2, t * 10);
}
requestAnimationFrame(animateGlobe);
};
animateGlobe();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js"></script>
<script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script>
<script src="https://assets.codepen.io/3685267/around-the-world-map-data-v2.js"></script>
<script src="https://assets.codepen.io/3685267/verser.js"></script>
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@600&display=swap");
body {
height: 100vh;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background: rgb(58, 57, 57);
font-family: "Nunito", sans-serif;
}
.container {
width: 100vw;
height: 100vh;
position: relative;
}
.info {
min-width: 200px;
max-width: 90vw;
text-align: center;
padding: 16px;
background: white;
position: absolute;
border-radius: 10px;
top: 50vh;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0;
font-weight: 600;
font-size: 1.25rem;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment